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