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 /* Introduce a page as a barrier between the UTCB and the stack. */ 63 64 _rm_stack = new ExplicitSegment(Utcb_area_start - L4_PAGESIZE - 65 initial_stack_size, initial_stack_size, L4_FPAGE_RW); 66 67 return _rm_stack->allocate(true); 68 } 69 70 /* Initialise the memory segments of the actual program. These are not mapped 71 into this task, instead being accessed by the region mapper in the new 72 task. */ 73 74 long ProcessCreating::init_program(file_t *file) 75 { 76 long err = exec_get_payload_file(file, &_program_payload, false); 77 78 if (err) 79 return err; 80 81 /* Introduce a page as a barrier between the program stack and the region 82 mapper stack. */ 83 84 _program_stack = new ExplicitSegment(Utcb_area_start - L4_PAGESIZE * 2 - 85 initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW); 86 87 return _program_stack->allocate(true); 88 } 89 90 /* Initialise an external system-level pager serving the region mapper in a 91 created task. The allocated regions requested by the region mapper are 92 constrained to an area of memory that must not overlap with the area reserved 93 for the program being run. */ 94 95 long ProcessCreating::init_external_pager(l4_cap_idx_t *pager) 96 { 97 _exec_pager = new ExternalPager(0, MEM_INTERNAL_PAGER_LIMIT); 98 _exec_pager->set_monitor(_monitor); 99 100 /* Initialise pager regions for the region mapper. */ 101 102 for (unsigned int i = 0; i < _rm_payload->segments(); i++) 103 { 104 if (_rm_payload->segment(i)->loadable()) 105 _exec_pager->add(_rm_payload->segment(i)->region()); 106 } 107 108 /* Include the region mapper's stack region. */ 109 110 _exec_pager->add(_rm_stack->region()); 111 _exec_pager->set_payload(_rm_payload); 112 _exec_pager->set_stack(_rm_stack); 113 114 /* Start the pager in a separate thread. */ 115 116 long err = ResourceServer(_exec_pager).start_thread(pager); 117 118 if (err) 119 return err; 120 121 return L4_EOK; 122 } 123 124 /* Initialise a resource to receive signals from the process. */ 125 126 long ProcessCreating::init_process_monitor(l4_cap_idx_t *monitor) 127 { 128 _monitor = new ProcessMonitor; 129 130 /* Start the monitor in a separate thread. */ 131 132 return ResourceServer(_monitor).start_thread(monitor); 133 } 134 135 /* Configure the environment for the task. */ 136 137 long ProcessCreating::configure_task() 138 { 139 l4_cap_idx_t task, mapped_task; 140 long err = _process.configure_task(&task, &mapped_task); 141 142 if (err) 143 return err; 144 145 /* Record the task details elsewhere for eventual resource deallocation. */ 146 147 _exec_pager->set_task(task, mapped_task); 148 _monitor->set_task(task, mapped_task); 149 return L4_EOK; 150 } 151 152 /* Create an unbound IPC gate for the internal pager/region mapper and allocate 153 it in the created process. */ 154 155 long ProcessCreating::allocate_internal_pager() 156 { 157 _mapped_internal_pager = _process.allocate_cap(); 158 _internal_pager = ipc_cap_alloc(); 159 160 if (l4_is_invalid_cap(_internal_pager)) 161 return -L4_ENOMEM; 162 163 return l4_error(l4_factory_create_gate(l4re_env()->factory, _internal_pager, L4_INVALID_CAP, 0)); 164 } 165 166 /* Initialise and assign a region in a list to the created process. */ 167 168 void ProcessCreating::init_region(struct exec_region *regions, 169 struct ipc_mapped_cap *mapped_caps, 170 struct exec_region &r, unsigned int &index) 171 { 172 l4_cap_idx_t mapped_cap = _process.allocate_cap(); 173 174 mapped_caps[index] = (struct ipc_mapped_cap) {mapped_cap, r.ds, L4_CAP_FPAGE_RWS, 0}; 175 176 /* Change the region definition to use the allocated capability in the created 177 process. */ 178 179 regions[index] = r; 180 regions[index].ds = mapped_cap; 181 index++; 182 } 183 184 /* Initialise the region mapper with details of the payload program regions 185 and of the associated capabilities, configure the region mapper thread, 186 populate its stack, and start the thread. */ 187 188 long ProcessCreating::start_region_mapper(l4_cap_idx_t pager) 189 { 190 /* Define regions employing dataspaces to provide program segments. */ 191 192 struct exec_region rm_regions[_program_payload->segments() + 2]; 193 194 /* Define capabilities for mapping, including region dataspace capabilities, 195 the stack dataspace capability, plus the pager capability. */ 196 197 struct ipc_mapped_cap rm_mapped_caps[_program_payload->segments() + 3]; 198 199 /* Here, the arrays are sized for the maximum number of regions and 200 capabilities, but in practice only the loadable segments are used, leaving 201 fewer elements utilised. A terminating entry is employed to indicate the 202 limit of utilised elements. */ 203 204 unsigned int rm_index = 0; 205 206 for (unsigned int i = 0; i < _program_payload->segments(); i++) 207 { 208 Segment *s = _program_payload->segment(i); 209 210 if (s->loadable()) 211 init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index); 212 } 213 214 /* Introduce the stack region and capability. */ 215 216 init_region(rm_regions, rm_mapped_caps, _program_stack->exec_region(), rm_index); 217 218 /* Terminate the region array. */ 219 220 rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; 221 222 /* Introduce the internal pager capability and terminate the capability array. */ 223 224 rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_mapped_internal_pager, 225 _internal_pager, L4_CAP_FPAGE_RWS, 226 L4_FPAGE_C_OBJ_RIGHTS}; 227 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}; 228 229 /* Map these additional capabilities. */ 230 231 long err = _process.map_capabilities(rm_mapped_caps, false); 232 233 if (err) 234 return err; 235 236 /* Define the IPC gate as an initial capability to be acquired by the region 237 mapper via the l4re_env API. The capability index is assigned above when 238 mapping the capability and encoded in the entry below. */ 239 240 l4re_env_cap_entry_t rm_init_caps[] = { 241 l4re_env_cap_entry_t(ENV_INTERNAL_PAGER_NAME, _mapped_internal_pager, L4_CAP_FPAGE_RWS), 242 l4re_env_cap_entry_t() 243 }; 244 245 /* NOTE: Environment vector is currently not defined. */ 246 247 const char *envp[] = {NULL}; 248 249 /* Configure the environment for the thread, specifying the pager (and 250 exception handler plus region mapper). */ 251 252 l4_cap_idx_t mapped_pager = L4_INVALID_CAP; 253 err = _process.set_pager(pager, &mapped_pager); 254 255 if (err) 256 return err; 257 258 _exec_pager->set_pager(pager, mapped_pager); 259 260 /* Note the pager as the parent of the new task, recording its capability 261 details in the new task. */ 262 263 err = _process.set_parent(pager, &mapped_pager); 264 265 if (err) 266 return err; 267 268 _exec_pager->set_parent(pager, mapped_pager); 269 270 /* Populate a thread stack with argument and environment details for the 271 region mapper, plus the initial server capability and region details. */ 272 273 const char *argv[] = {_rm_filename}; 274 Stack rm_st(*_rm_stack); 275 276 rm_st.set_init_caps(rm_init_caps); 277 rm_st.set_regions(rm_regions); 278 rm_st.populate(1, argv, envp); 279 280 /* Start the region mapper thread in the appropriate stack. */ 281 282 l4_cap_idx_t thread, mapped_thread; 283 err = _process.thread_start(_rm_payload->entry_point(), rm_st, &thread, &mapped_thread); 284 285 if (err) 286 return err; 287 288 _exec_pager->set_thread(thread, mapped_thread); 289 return L4_EOK; 290 } 291 292 /* Configure a thread for a program, populate its stack, and start the 293 thread. */ 294 295 long ProcessCreating::start_program(l4_cap_idx_t monitor, int argc, 296 const char *argv[], l4_cap_idx_t reader, 297 l4_cap_idx_t writer) 298 { 299 /* NOTE: Environment vector is currently not defined. */ 300 301 const char *envp[] = {NULL}; 302 303 /* Configure the environment for the thread, specifying the pager (and 304 exception handler plus region mapper). */ 305 306 l4_cap_idx_t mapped_pager = _mapped_internal_pager; 307 long err = _process.set_pager(_internal_pager, &mapped_pager); 308 309 if (err) 310 return err; 311 312 _monitor->set_pager(_internal_pager, _mapped_internal_pager); 313 314 /* Note the monitor as the parent of the new task, recording its capability 315 details in the new task. */ 316 317 l4_cap_idx_t mapped_parent = L4_INVALID_CAP; 318 err = _process.set_parent(monitor, &mapped_parent); 319 320 if (err) 321 return err; 322 323 _monitor->set_parent(monitor, mapped_parent); 324 325 /* Obtain the filesystem capability for exporting to the task. */ 326 327 l4_cap_idx_t fsserver_cap = _process.allocate_cap(); 328 l4_cap_idx_t fsserver = l4re_env_get_cap(ENV_FILESYSTEM_SERVER_NAME); 329 330 /* Obtain the process server capability. */ 331 332 l4_cap_idx_t prserver_cap = _process.allocate_cap(); 333 l4_cap_idx_t prserver = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); 334 335 /* Obtain the pipe server capability. */ 336 337 l4_cap_idx_t pipeserver_cap = _process.allocate_cap(); 338 l4_cap_idx_t pipeserver = l4re_env_get_cap(ENV_PIPE_SERVER_NAME); 339 340 /* Also reserve capabilities for the reader and writer. If the reader or 341 writer are invalid capabilities, these will not actually be transferred. */ 342 343 l4_cap_idx_t reader_cap = _process.allocate_cap(); 344 l4_cap_idx_t writer_cap = _process.allocate_cap(); 345 346 /* Define the capabilities to be mapped for the filesystem. */ 347 348 struct ipc_mapped_cap program_mapped_caps[] = { 349 {fsserver_cap, fsserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 350 {prserver_cap, prserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 351 {pipeserver_cap, pipeserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 352 {reader_cap, reader, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 353 {writer_cap, writer, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 354 {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}, 355 }; 356 357 /* Map these additional capabilities. */ 358 359 err = _process.map_capabilities(program_mapped_caps, false); 360 361 if (err) 362 return err; 363 364 /* Define initial capabilities to be acquired by the region mapper via the 365 l4re_env API. Each capability index is assigned above when mapping the 366 capability and encoded in the entry below. */ 367 368 if (l4_is_invalid_cap(reader)) 369 reader_cap = L4_INVALID_CAP; 370 371 if (l4_is_invalid_cap(writer)) 372 writer_cap = L4_INVALID_CAP; 373 374 l4re_env_cap_entry_t program_init_caps[] = { 375 l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS), 376 l4re_env_cap_entry_t(ENV_PROCESS_SERVER_NAME, prserver_cap, L4_CAP_FPAGE_RWS), 377 l4re_env_cap_entry_t(ENV_PIPE_SERVER_NAME, pipeserver_cap, L4_CAP_FPAGE_RWS), 378 l4re_env_cap_entry_t(ENV_INPUT_STREAM_NAME, reader_cap, L4_CAP_FPAGE_R), 379 l4re_env_cap_entry_t(ENV_OUTPUT_STREAM_NAME, writer_cap, L4_CAP_FPAGE_W), 380 l4re_env_cap_entry_t() 381 }; 382 383 /* Populate a thread stack with argument and environment details for the 384 actual program. The server capability should be assigned to the region 385 mapper capability slot already. */ 386 387 Stack program_st(*_program_stack); 388 389 program_st.set_init_caps(program_init_caps); 390 program_st.populate(argc, argv, envp); 391 392 /* Start the program thread in the appropriate stack. */ 393 394 l4_cap_idx_t thread, mapped_thread; 395 err = _process.thread_start(_program_payload->entry_point(), program_st, &thread, &mapped_thread); 396 397 if (err) 398 return err; 399 400 _monitor->set_thread(thread, mapped_thread); 401 return L4_EOK; 402 } 403 404 /* Start a new process for the payload indicated by the first of the given 405 program arguments, employing the given reader and writer pipes, and returning 406 a reference to the process monitor as an object for interacting with the 407 process. */ 408 409 long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t reader, 410 l4_cap_idx_t writer, l4_cap_idx_t process) 411 { 412 /* Open the program file, handling any error conditions. If successfully 413 opened, it will be closed when the process terminates. */ 414 415 file_t *file = client_open(argv[0], O_RDONLY); 416 long err; 417 418 if (file == NULL) 419 return -L4_EIO; 420 421 if (!client_opened(file)) 422 { 423 err = file->error; 424 client_close(file); 425 return err; 426 } 427 428 /* Initialise the different elements of the process. */ 429 430 l4_cap_idx_t pager; 431 432 err = init_region_mapper(); 433 if (err) 434 return err; 435 436 err = init_program(file); 437 if (err) 438 return err; 439 440 err = init_external_pager(&pager); 441 if (err) 442 return err; 443 444 err = configure_task(); 445 if (err) 446 return err; 447 448 err = allocate_internal_pager(); 449 if (err) 450 return err; 451 452 err = start_region_mapper(pager); 453 if (err) 454 return err; 455 456 err = start_program(process, argc, argv, reader, writer); 457 if (err) 458 return err; 459 460 /* Discard instances created to initialise the process. The region mapper 461 relies on resources associated with its payload and stack and so these 462 cannot be deleted immediately. Instead, they are released when the pager is 463 deallocated. 464 465 NOTE: The region mapper payload could be retained instead of being 466 reconstructed each time. */ 467 468 delete _program_payload; 469 delete _program_stack; 470 471 /* Close the file given that it was used to construct the payload and 472 separate file references are now used by the created process. */ 473 474 client_close(file); 475 476 return L4_EOK; 477 } 478 479 /* Start the given program, notifying the process monitor upon any error. */ 480 481 long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t reader, 482 l4_cap_idx_t writer, l4_cap_idx_t process) 483 { 484 std::lock_guard<std::mutex> guard(_lock); 485 486 long err = _start(argc, argv, reader, writer, process); 487 488 /* Discard the reader and writer since they will not be used in this task. */ 489 490 ipc_cap_free_um(reader); 491 ipc_cap_free_um(writer); 492 493 /* Communicate the error using the signal value. */ 494 495 if (err) 496 _monitor->notify_all(NOTIFY_TASK_ERROR, (notify_values_t) {0, err}); 497 498 return err; 499 } 500 501 /* vim: tabstop=2 expandtab shiftwidth=2 502 */