1 /* 2 * Support for executing code in new tasks and threads. 3 * 4 * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/re/env.h> 23 24 #include <exec/common.h> 25 #include <fsclient/client.h> 26 #include <ipc/cap_alloc.h> 27 #include <ipc/map.h> 28 #include <resource/resource_server.h> 29 #include <systypes/env.h> 30 #include <systypes/fcntl.h> 31 32 #include <stdio.h> 33 34 #include "process_creating.h" 35 36 37 38 /* Process stack configuration. */ 39 40 static const offset_t initial_stack_size = 16 * L4_PAGESIZE; 41 42 43 44 /* Initialise the process creator with the details of a region mapper. */ 45 46 ProcessCreating::ProcessCreating(const char *rm_filename, file_t *rm_file) 47 : _rm_filename(rm_filename), _rm_file(rm_file) 48 { 49 } 50 51 /* Initialise the memory segments of the region mapper. These are mapped into 52 this task so that we may access them, allowing the external pager in this 53 task to use them. */ 54 55 long ProcessCreating::init_region_mapper() 56 { 57 long err = exec_get_payload_file(_rm_file, &_rm_payload, true); 58 59 if (err) 60 return err; 61 62 _rm_stack = new ExplicitSegment(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); 63 return _rm_stack->allocate(true); 64 } 65 66 /* Initialise the memory segments of the actual program. These are not mapped 67 into this task, instead being accessed by the region mapper in the new 68 task. */ 69 70 long ProcessCreating::init_program(file_t *file) 71 { 72 long err = exec_get_payload_file(file, &_program_payload, false); 73 74 if (err) 75 return err; 76 77 _program_stack = new ExplicitSegment(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW); 78 return _program_stack->allocate(true); 79 } 80 81 /* Initialise an external system-level pager serving the region mapper in a 82 created task. The allocated regions requested by the region mapper are 83 constrained to an area of memory that must not overlap with the area reserved 84 for the program being run. */ 85 86 long ProcessCreating::init_external_pager(l4_cap_idx_t *pager) 87 { 88 _exec_pager = new ExternalPager(0, MEM_INTERNAL_PAGER_LIMIT); 89 _exec_pager->set_monitor(_monitor); 90 91 /* Initialise pager regions for the region mapper. */ 92 93 for (unsigned int i = 0; i < _rm_payload->segments(); i++) 94 { 95 if (_rm_payload->segment(i)->loadable()) 96 _exec_pager->add(_rm_payload->segment(i)->region()); 97 } 98 99 /* Include the region mapper's stack region. */ 100 101 _exec_pager->add(_rm_stack->region()); 102 _exec_pager->set_payload(_rm_payload); 103 _exec_pager->set_stack(_rm_stack); 104 105 /* Start the pager in a separate thread. */ 106 107 long err = ResourceServer(_exec_pager).start_thread(pager); 108 109 if (err) 110 return err; 111 112 return L4_EOK; 113 } 114 115 /* Initialise a resource to receive signals from the process. */ 116 117 long ProcessCreating::init_process_monitor(l4_cap_idx_t *monitor) 118 { 119 _monitor = new ProcessMonitor; 120 121 /* Start the monitor in a separate thread. */ 122 123 return ResourceServer(_monitor).start_thread(monitor); 124 } 125 126 /* Configure the environment for the task. */ 127 128 long ProcessCreating::configure_task() 129 { 130 l4_cap_idx_t task, mapped_task; 131 long err = _process.configure_task(&task, &mapped_task); 132 133 if (err) 134 return err; 135 136 /* Record the task details elsewhere for eventual resource deallocation. */ 137 138 _exec_pager->set_task(task, mapped_task); 139 _monitor->set_task(task, mapped_task); 140 return L4_EOK; 141 } 142 143 /* Create an unbound IPC gate for the internal pager/region mapper and allocate 144 it in the created process. */ 145 146 long ProcessCreating::allocate_internal_pager() 147 { 148 _mapped_internal_pager = _process.allocate_cap(); 149 _internal_pager = ipc_cap_alloc(); 150 151 if (l4_is_invalid_cap(_internal_pager)) 152 return -L4_ENOMEM; 153 154 return l4_error(l4_factory_create_gate(l4re_env()->factory, _internal_pager, L4_INVALID_CAP, 0)); 155 } 156 157 /* Initialise and assign a region in a list to the created process. */ 158 159 void ProcessCreating::init_region(struct exec_region *regions, 160 struct ipc_mapped_cap *mapped_caps, 161 struct exec_region &r, unsigned int &index) 162 { 163 l4_cap_idx_t mapped_cap = _process.allocate_cap(); 164 165 mapped_caps[index] = (struct ipc_mapped_cap) {mapped_cap, r.ds, L4_CAP_FPAGE_RWS, 0}; 166 167 /* Change the region definition to use the allocated capability in the created 168 process. */ 169 170 regions[index] = r; 171 regions[index].ds = mapped_cap; 172 index++; 173 } 174 175 /* Initialise the region mapper with details of the payload program regions 176 and of the associated capabilities, configure the region mapper thread, 177 populate its stack, and start the thread. */ 178 179 long ProcessCreating::start_region_mapper(l4_cap_idx_t pager) 180 { 181 /* Define regions employing dataspaces to provide program segments. */ 182 183 struct exec_region rm_regions[_program_payload->segments() + 2]; 184 185 /* Define capabilities for mapping, including region dataspace capabilities, 186 the stack dataspace capability, plus the pager capability. */ 187 188 struct ipc_mapped_cap rm_mapped_caps[_program_payload->segments() + 3]; 189 190 /* Here, the arrays are sized for the maximum number of regions and 191 capabilities, but in practice only the loadable segments are used, leaving 192 fewer elements utilised. A terminating entry is employed to indicate the 193 limit of utilised elements. */ 194 195 unsigned int rm_index = 0; 196 197 for (unsigned int i = 0; i < _program_payload->segments(); i++) 198 { 199 Segment *s = _program_payload->segment(i); 200 201 if (s->loadable()) 202 init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index); 203 } 204 205 /* Introduce the stack region and capability. */ 206 207 init_region(rm_regions, rm_mapped_caps, _program_stack->exec_region(), rm_index); 208 209 /* Terminate the region array. */ 210 211 rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; 212 213 /* Introduce the internal pager capability and terminate the capability array. */ 214 215 rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_mapped_internal_pager, 216 _internal_pager, L4_CAP_FPAGE_RWS, 217 L4_FPAGE_C_OBJ_RIGHTS}; 218 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}; 219 220 /* Map these additional capabilities. */ 221 222 long err = _process.map_capabilities(rm_mapped_caps, false); 223 224 if (err) 225 return err; 226 227 /* Define the IPC gate as an initial capability to be acquired by the region 228 mapper via the l4re_env API. The capability index is assigned above when 229 mapping the capability and encoded in the entry below. */ 230 231 l4re_env_cap_entry_t rm_init_caps[] = { 232 l4re_env_cap_entry_t(ENV_INTERNAL_PAGER_NAME, _mapped_internal_pager, L4_CAP_FPAGE_RWS), 233 l4re_env_cap_entry_t() 234 }; 235 236 /* NOTE: Environment vector is currently not defined. */ 237 238 const char *envp[] = {NULL}; 239 240 /* Configure the environment for the thread, specifying the pager (and 241 exception handler plus region mapper). */ 242 243 l4_cap_idx_t mapped_pager = L4_INVALID_CAP; 244 err = _process.set_pager(pager, &mapped_pager); 245 246 if (err) 247 return err; 248 249 _exec_pager->set_pager(pager, mapped_pager); 250 251 /* Note the pager as the parent of the new task, recording its capability 252 details in the new task. */ 253 254 err = _process.set_parent(pager, &mapped_pager); 255 256 if (err) 257 return err; 258 259 _exec_pager->set_parent(pager, mapped_pager); 260 261 /* Populate a thread stack with argument and environment details for the 262 region mapper, plus the initial server capability and region details. */ 263 264 const char *argv[] = {_rm_filename}; 265 Stack rm_st(*_rm_stack); 266 267 rm_st.set_init_caps(rm_init_caps); 268 rm_st.set_regions(rm_regions); 269 rm_st.populate(1, argv, envp); 270 271 /* Start the region mapper thread in the appropriate stack. */ 272 273 l4_cap_idx_t thread, mapped_thread; 274 err = _process.thread_start(_rm_payload->entry_point(), rm_st, &thread, &mapped_thread); 275 276 if (err) 277 return err; 278 279 _exec_pager->set_thread(thread, mapped_thread); 280 return L4_EOK; 281 } 282 283 /* Configure a thread for a program, populate its stack, and start the 284 thread. */ 285 286 long ProcessCreating::start_program(l4_cap_idx_t monitor, int argc, 287 const char *argv[], l4_cap_idx_t reader, 288 l4_cap_idx_t writer) 289 { 290 /* NOTE: Environment vector is currently not defined. */ 291 292 const char *envp[] = {NULL}; 293 294 /* Configure the environment for the thread, specifying the pager (and 295 exception handler plus region mapper). */ 296 297 l4_cap_idx_t mapped_pager = _mapped_internal_pager; 298 long err = _process.set_pager(_internal_pager, &mapped_pager); 299 300 if (err) 301 return err; 302 303 _monitor->set_pager(_internal_pager, _mapped_internal_pager); 304 305 /* Note the monitor as the parent of the new task, recording its capability 306 details in the new task. */ 307 308 l4_cap_idx_t mapped_parent = L4_INVALID_CAP; 309 err = _process.set_parent(monitor, &mapped_parent); 310 311 if (err) 312 return err; 313 314 _monitor->set_parent(monitor, mapped_parent); 315 316 /* Obtain the filesystem capability for exporting to the task. */ 317 318 l4_cap_idx_t fsserver_cap = _process.allocate_cap(); 319 l4_cap_idx_t fsserver = l4re_env_get_cap(ENV_FILESYSTEM_SERVER_NAME); 320 321 /* Also reserve capabilities for the reader and writer. If the reader or 322 writer are invalid capabilities, these will not actually be transferred. */ 323 324 l4_cap_idx_t reader_cap = _process.allocate_cap(); 325 l4_cap_idx_t writer_cap = _process.allocate_cap(); 326 327 /* Define the capabilities to be mapped for the filesystem. */ 328 329 struct ipc_mapped_cap program_mapped_caps[] = { 330 {fsserver_cap, fsserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 331 {reader_cap, reader, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 332 {writer_cap, writer, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 333 {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}, 334 }; 335 336 /* Map these additional capabilities. */ 337 338 err = _process.map_capabilities(program_mapped_caps, false); 339 340 if (err) 341 return err; 342 343 /* Define initial capabilities to be acquired by the region mapper via the 344 l4re_env API. Each capability index is assigned above when mapping the 345 capability and encoded in the entry below. */ 346 347 if (l4_is_invalid_cap(reader)) 348 reader_cap = L4_INVALID_CAP; 349 350 if (l4_is_invalid_cap(writer)) 351 writer_cap = L4_INVALID_CAP; 352 353 l4re_env_cap_entry_t program_init_caps[] = { 354 l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS), 355 l4re_env_cap_entry_t(ENV_INPUT_STREAM_NAME, reader_cap, L4_CAP_FPAGE_R), 356 l4re_env_cap_entry_t(ENV_OUTPUT_STREAM_NAME, writer_cap, L4_CAP_FPAGE_W), 357 l4re_env_cap_entry_t() 358 }; 359 360 /* Populate a thread stack with argument and environment details for the 361 actual program. The server capability should be assigned to the region 362 mapper capability slot already. */ 363 364 Stack program_st(*_program_stack); 365 366 program_st.set_init_caps(program_init_caps); 367 program_st.populate(argc, argv, envp); 368 369 /* Start the program thread in the appropriate stack. */ 370 371 l4_cap_idx_t thread, mapped_thread; 372 err = _process.thread_start(_program_payload->entry_point(), program_st, &thread, &mapped_thread); 373 374 if (err) 375 return err; 376 377 _monitor->set_thread(thread, mapped_thread); 378 return L4_EOK; 379 } 380 381 /* Start a new process for the payload indicated by the first of the given 382 program arguments, employing the given reader and writer pipes, and returning 383 a reference to the process monitor as an object for interacting with the 384 process. */ 385 386 long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t reader, 387 l4_cap_idx_t writer, l4_cap_idx_t process) 388 { 389 /* Open the program file, handling any error conditions. If successfully 390 opened, it will be closed when the process terminates. */ 391 392 file_t *file = client_open(argv[0], O_RDONLY); 393 long err; 394 395 if (file == NULL) 396 return -L4_EIO; 397 398 if (!client_opened(file)) 399 { 400 err = file->error; 401 client_close(file); 402 return err; 403 } 404 405 /* Initialise the different elements of the process. */ 406 407 l4_cap_idx_t pager; 408 409 err = init_region_mapper(); 410 if (err) 411 return err; 412 413 err = init_program(file); 414 if (err) 415 return err; 416 417 err = init_external_pager(&pager); 418 if (err) 419 return err; 420 421 err = configure_task(); 422 if (err) 423 return err; 424 425 err = allocate_internal_pager(); 426 if (err) 427 return err; 428 429 err = start_region_mapper(pager); 430 if (err) 431 return err; 432 433 err = start_program(process, argc, argv, reader, writer); 434 if (err) 435 return err; 436 437 /* Discard instances created to initialise the process. The region mapper 438 relies on resources associated with its payload and stack and so these 439 cannot be deleted immediately. Instead, they are released when the pager is 440 deallocated. 441 442 NOTE: The region mapper payload could be retained instead of being 443 reconstructed each time. */ 444 445 delete _program_payload; 446 delete _program_stack; 447 448 /* Close the file given that it was used to construct the payload and 449 separate file references are now used by the created process. */ 450 451 client_close(file); 452 453 return L4_EOK; 454 } 455 456 /* Start the given program, notifying the process monitor upon any error. */ 457 458 long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t reader, 459 l4_cap_idx_t writer, l4_cap_idx_t process) 460 { 461 std::lock_guard<std::mutex> guard(_lock); 462 463 long err = _start(argc, argv, reader, writer, process); 464 465 /* Discard the reader and writer since they will not be used in this task. */ 466 467 ipc_cap_free_um(reader); 468 ipc_cap_free_um(writer); 469 470 /* Communicate the error using the signal value. */ 471 472 if (err) 473 _monitor->notify_all(NOTIFY_TASK_ERROR, (notify_values_t) {0, err}); 474 475 return err; 476 } 477 478 /* vim: tabstop=2 expandtab shiftwidth=2 479 */