1 /* 2 * Support for executing code in new tasks and threads. 3 * 4 * Copyright (C) 2022, 2023 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, 10 * L4_PAGESIZE); 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) {0, 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 writer) 288 { 289 /* NOTE: Environment vector is currently not defined. */ 290 291 const char *envp[] = {NULL}; 292 293 /* Configure the environment for the thread, specifying the pager (and 294 exception handler plus region mapper). */ 295 296 l4_cap_idx_t mapped_pager = _mapped_internal_pager; 297 long err = _process.set_pager(_internal_pager, &mapped_pager); 298 299 if (err) 300 return err; 301 302 _monitor->set_pager(_internal_pager, _mapped_internal_pager); 303 304 /* Note the monitor as the parent of the new task, recording its capability 305 details in the new task. */ 306 307 l4_cap_idx_t mapped_parent = L4_INVALID_CAP; 308 err = _process.set_parent(monitor, &mapped_parent); 309 310 if (err) 311 return err; 312 313 _monitor->set_parent(monitor, mapped_parent); 314 315 /* Obtain the filesystem capability for exporting to the task. */ 316 317 l4_cap_idx_t fsserver_cap = _process.allocate_cap(); 318 l4_cap_idx_t fsserver = l4re_env_get_cap(ENV_FILESYSTEM_SERVER_NAME); 319 320 /* Also reserve a capability for the writer. */ 321 322 l4_cap_idx_t writer_cap = _process.allocate_cap(); 323 324 /* Define the capabilities to be mapped for the filesystem. */ 325 326 struct ipc_mapped_cap program_mapped_caps[] = { 327 {fsserver_cap, fsserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 328 {writer_cap, writer, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 329 {0, L4_INVALID_CAP, 0, 0}, 330 }; 331 332 /* Map these additional capabilities. */ 333 334 err = _process.map_capabilities(program_mapped_caps, false); 335 336 if (err) 337 return err; 338 339 /* Define initial capabilities to be acquired by the region mapper via the 340 l4re_env API. Each capability index is assigned above when mapping the 341 capability and encoded in the entry below. */ 342 343 l4re_env_cap_entry_t program_init_caps[] = { 344 l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS), 345 l4re_env_cap_entry_t(ENV_OUTPUT_STREAM_NAME, writer_cap, L4_CAP_FPAGE_W), 346 l4re_env_cap_entry_t() 347 }; 348 349 /* Populate a thread stack with argument and environment details for the 350 actual program. The server capability should be assigned to the region 351 mapper capability slot already. */ 352 353 Stack program_st(*_program_stack); 354 355 program_st.set_init_caps(program_init_caps); 356 program_st.populate(argc, argv, envp); 357 358 /* Start the program thread in the appropriate stack. */ 359 360 l4_cap_idx_t thread, mapped_thread; 361 err = _process.thread_start(_program_payload->entry_point(), program_st, &thread, &mapped_thread); 362 363 if (err) 364 return err; 365 366 _monitor->set_thread(thread, mapped_thread); 367 return L4_EOK; 368 } 369 370 /* Start a new process for the payload indicated by the first of the given 371 program arguments, employing the given writer pipe, and returning a 372 reference to the process monitor as an object for interacting with the 373 process. */ 374 375 long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t writer, 376 l4_cap_idx_t process) 377 { 378 /* Open the program file, handling any error conditions. If successfully 379 opened, it will be closed when the process terminates. */ 380 381 file_t *file = client_open(argv[0], O_RDONLY); 382 long err; 383 384 if (file == NULL) 385 return -L4_EIO; 386 387 if (!client_opened(file)) 388 { 389 err = file->error; 390 client_close(file); 391 return err; 392 } 393 394 /* Initialise the different elements of the process. */ 395 396 l4_cap_idx_t pager; 397 398 err = init_region_mapper(); 399 if (err) 400 return err; 401 402 err = init_program(file); 403 if (err) 404 return err; 405 406 err = init_external_pager(&pager); 407 if (err) 408 return err; 409 410 err = configure_task(); 411 if (err) 412 return err; 413 414 err = allocate_internal_pager(); 415 if (err) 416 return err; 417 418 err = start_region_mapper(pager); 419 if (err) 420 return err; 421 422 err = start_program(process, argc, argv, writer); 423 if (err) 424 return err; 425 426 /* Discard instances created to initialise the process. The region mapper 427 relies on resources associated with its payload and stack and so these 428 cannot be deleted immediately. Instead, they are released when the pager is 429 deallocated. 430 431 NOTE: The region mapper payload could be retained instead of being 432 reconstructed each time. */ 433 434 delete _program_payload; 435 delete _program_stack; 436 437 /* Close the file given that it was used to construct the payload and 438 separate file references are now used by the created process. */ 439 440 client_close(file); 441 442 return L4_EOK; 443 } 444 445 /* Start the given program, notifying the process monitor upon any error. */ 446 447 long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t writer, 448 l4_cap_idx_t process) 449 { 450 std::lock_guard<std::mutex> guard(_lock); 451 452 long err = _start(argc, argv, writer, process); 453 454 /* Communicate the error using the signal value. */ 455 456 if (err) 457 _monitor->notify_all(NOTIFY_TASK_ERROR, (notify_values_t) {0, err}); 458 459 return err; 460 } 461 462 /* vim: tabstop=2 expandtab shiftwidth=2 463 */