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