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