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 /* Also reserve capabilities for the reader and writer. If the reader or 336 writer are invalid capabilities, these will not actually be transferred. */ 337 338 l4_cap_idx_t reader_cap = _process.allocate_cap(); 339 l4_cap_idx_t writer_cap = _process.allocate_cap(); 340 341 /* Define the capabilities to be mapped for the filesystem. */ 342 343 struct ipc_mapped_cap program_mapped_caps[] = { 344 {fsserver_cap, fsserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 345 {prserver_cap, prserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 346 {reader_cap, reader, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 347 {writer_cap, writer, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 348 {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}, 349 }; 350 351 /* Map these additional capabilities. */ 352 353 err = _process.map_capabilities(program_mapped_caps, false); 354 355 if (err) 356 return err; 357 358 /* Define initial capabilities to be acquired by the region mapper via the 359 l4re_env API. Each capability index is assigned above when mapping the 360 capability and encoded in the entry below. */ 361 362 if (l4_is_invalid_cap(reader)) 363 reader_cap = L4_INVALID_CAP; 364 365 if (l4_is_invalid_cap(writer)) 366 writer_cap = L4_INVALID_CAP; 367 368 l4re_env_cap_entry_t program_init_caps[] = { 369 l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS), 370 l4re_env_cap_entry_t(ENV_PROCESS_SERVER_NAME, prserver_cap, L4_CAP_FPAGE_RWS), 371 l4re_env_cap_entry_t(ENV_INPUT_STREAM_NAME, reader_cap, L4_CAP_FPAGE_R), 372 l4re_env_cap_entry_t(ENV_OUTPUT_STREAM_NAME, writer_cap, L4_CAP_FPAGE_W), 373 l4re_env_cap_entry_t() 374 }; 375 376 /* Populate a thread stack with argument and environment details for the 377 actual program. The server capability should be assigned to the region 378 mapper capability slot already. */ 379 380 Stack program_st(*_program_stack); 381 382 program_st.set_init_caps(program_init_caps); 383 program_st.populate(argc, argv, envp); 384 385 /* Start the program thread in the appropriate stack. */ 386 387 l4_cap_idx_t thread, mapped_thread; 388 err = _process.thread_start(_program_payload->entry_point(), program_st, &thread, &mapped_thread); 389 390 if (err) 391 return err; 392 393 _monitor->set_thread(thread, mapped_thread); 394 return L4_EOK; 395 } 396 397 /* Start a new process for the payload indicated by the first of the given 398 program arguments, employing the given reader and writer pipes, and returning 399 a reference to the process monitor as an object for interacting with the 400 process. */ 401 402 long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t reader, 403 l4_cap_idx_t writer, l4_cap_idx_t process) 404 { 405 /* Open the program file, handling any error conditions. If successfully 406 opened, it will be closed when the process terminates. */ 407 408 file_t *file = client_open(argv[0], O_RDONLY); 409 long err; 410 411 if (file == NULL) 412 return -L4_EIO; 413 414 if (!client_opened(file)) 415 { 416 err = file->error; 417 client_close(file); 418 return err; 419 } 420 421 /* Initialise the different elements of the process. */ 422 423 l4_cap_idx_t pager; 424 425 err = init_region_mapper(); 426 if (err) 427 return err; 428 429 err = init_program(file); 430 if (err) 431 return err; 432 433 err = init_external_pager(&pager); 434 if (err) 435 return err; 436 437 err = configure_task(); 438 if (err) 439 return err; 440 441 err = allocate_internal_pager(); 442 if (err) 443 return err; 444 445 err = start_region_mapper(pager); 446 if (err) 447 return err; 448 449 err = start_program(process, argc, argv, reader, writer); 450 if (err) 451 return err; 452 453 /* Discard instances created to initialise the process. The region mapper 454 relies on resources associated with its payload and stack and so these 455 cannot be deleted immediately. Instead, they are released when the pager is 456 deallocated. 457 458 NOTE: The region mapper payload could be retained instead of being 459 reconstructed each time. */ 460 461 delete _program_payload; 462 delete _program_stack; 463 464 /* Close the file given that it was used to construct the payload and 465 separate file references are now used by the created process. */ 466 467 client_close(file); 468 469 return L4_EOK; 470 } 471 472 /* Start the given program, notifying the process monitor upon any error. */ 473 474 long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t reader, 475 l4_cap_idx_t writer, l4_cap_idx_t process) 476 { 477 std::lock_guard<std::mutex> guard(_lock); 478 479 long err = _start(argc, argv, reader, writer, process); 480 481 /* Discard the reader and writer since they will not be used in this task. */ 482 483 ipc_cap_free_um(reader); 484 ipc_cap_free_um(writer); 485 486 /* Communicate the error using the signal value. */ 487 488 if (err) 489 _monitor->notify_all(NOTIFY_TASK_ERROR, (notify_values_t) {0, err}); 490 491 return err; 492 } 493 494 /* vim: tabstop=2 expandtab shiftwidth=2 495 */