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