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 <fsserver/resource_server.h> 27 #include <ipc/cap_alloc.h> 28 #include <ipc/map.h> 29 #include <systypes/fcntl.h> 30 31 #include <stdio.h> 32 33 #include "process_creating.h" 34 35 36 37 /* Process stack configuration. */ 38 39 static const offset_t initial_stack_size = 16 * L4_PAGESIZE; 40 41 42 43 /* Initialise the process creator with the details of a region mapper. */ 44 45 ProcessCreating::ProcessCreating(const char *rm_filename) 46 : _rm_filename(rm_filename) 47 { 48 } 49 50 /* Initialise the memory segments of the region mapper. These are mapped into 51 this task so that we may access them, allowing the external pager in this 52 task to use them. */ 53 54 long ProcessCreating::init_region_mapper() 55 { 56 long err = exec_get_payload(_rm_filename, &_rm_payload, true); 57 58 if (err) 59 return err; 60 61 _rm_stack = new ExplicitSegment(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); 62 return _rm_stack->allocate(true); 63 } 64 65 /* Initialise the memory segments of the actual program. These are not mapped 66 into this task, instead being accessed by the region mapper in the new 67 task. */ 68 69 long ProcessCreating::init_program(file_t *file) 70 { 71 long err = exec_get_payload_file(file, &_program_payload, false); 72 73 if (err) 74 return err; 75 76 _program_stack = new ExplicitSegment(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW); 77 return _program_stack->allocate(true); 78 } 79 80 /* Initialise an external system-level pager serving the region mapper in a 81 created task. The allocated regions requested by the region mapper are 82 constrained to an area of memory that must not overlap with the area reserved 83 for the program being run. */ 84 85 long ProcessCreating::init_external_pager(l4_cap_idx_t *pager) 86 { 87 _exec_pager = new ExternalPager(0, 10 * L4_PAGESIZE); 88 89 /* Initialise pager regions for the region mapper. */ 90 91 for (unsigned int i = 0; i < _rm_payload->segments(); i++) 92 { 93 if (_rm_payload->segment(i)->loadable()) 94 _exec_pager->add(_rm_payload->segment(i)->region()); 95 } 96 97 /* Include the region mapper's stack region. */ 98 99 _exec_pager->add(_rm_stack->region()); 100 _exec_pager->set_payload(_rm_payload); 101 _exec_pager->set_stack(_rm_stack); 102 103 /* Start the pager in a separate thread. */ 104 105 long err = ResourceServer(_exec_pager).start_thread(pager); 106 107 if (err) 108 return err; 109 110 return L4_EOK; 111 } 112 113 /* Configure the environment for the task. */ 114 115 long ProcessCreating::configure_task(l4_cap_idx_t pager) 116 { 117 l4_cap_idx_t task, mapped_task; 118 long err = _process.configure_task(&task, &mapped_task); 119 120 if (err) 121 return err; 122 123 /* Record the task details in the pager for eventual resource deallocation. */ 124 125 _exec_pager->set_task(task, mapped_task); 126 127 /* Note the pager as the parent of the new task, recording its capability 128 details in the new task. */ 129 130 l4_cap_idx_t mapped_parent; 131 err = _process.set_parent(pager, &mapped_parent); 132 133 if (err) 134 return err; 135 136 _exec_pager->set_parent(pager, mapped_parent); 137 return L4_EOK; 138 } 139 140 /* Create an unbound IPC gate for the region mapper and allocate it in the 141 created process. */ 142 143 long ProcessCreating::create_ipc_gate() 144 { 145 _ipc_gate_cap = _process.allocate_cap(); 146 _ipc_gate = ipc_cap_alloc(); 147 148 if (l4_is_invalid_cap(_ipc_gate)) 149 return -L4_ENOMEM; 150 151 long err = l4_error(l4_factory_create_gate(l4re_env()->factory, _ipc_gate, L4_INVALID_CAP, 0)); 152 153 if (err) 154 return err; 155 156 /* The gate is retained because even after being mapped to the new task, 157 releasing it will cause it to be deallocated. */ 158 159 _exec_pager->set_gate(_ipc_gate); 160 return L4_EOK; 161 } 162 163 /* Initialise and assign a region in a list to the created process. */ 164 165 void ProcessCreating::init_region(struct exec_region *regions, 166 struct ipc_mapped_cap *mapped_caps, 167 struct exec_region &r, unsigned int &index) 168 { 169 l4_cap_idx_t mapped_cap = _process.allocate_cap(); 170 171 mapped_caps[index] = (struct ipc_mapped_cap) {mapped_cap, r.ds, L4_CAP_FPAGE_RWS, 0}; 172 173 /* Change the region definition to use the allocated capability in the created 174 process. */ 175 176 regions[index] = r; 177 regions[index].ds = mapped_cap; 178 index++; 179 } 180 181 /* Initialise the region mapper with details of the payload program regions 182 and of the associated capabilities, configure the region mapper thread, 183 populate its stack, and start the thread. */ 184 185 long ProcessCreating::start_region_mapper(l4_cap_idx_t pager) 186 { 187 /* Define regions employing dataspaces to provide program segments. */ 188 189 struct exec_region rm_regions[_program_payload->segments() + 2]; 190 191 /* Define capabilities for mapping, including region dataspace capabilities, 192 the stack dataspace capability, and the server capability. */ 193 194 struct ipc_mapped_cap rm_mapped_caps[_program_payload->segments() + 3]; 195 196 /* Here, the arrays are sized for the maximum number of regions and 197 capabilities, but in practice only the loadable segments are used, leaving 198 fewer elements utilised. A terminating entry is employed to indicate the 199 limit of utilised elements. */ 200 201 unsigned int rm_index = 0; 202 203 for (unsigned int i = 0; i < _program_payload->segments(); i++) 204 { 205 Segment *s = _program_payload->segment(i); 206 207 if (s->loadable()) 208 init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index); 209 } 210 211 /* Introduce the stack region and capability. */ 212 213 init_region(rm_regions, rm_mapped_caps, _program_stack->exec_region(), rm_index); 214 215 /* Terminate the region array. */ 216 217 rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; 218 219 /* Introduce the server capability and terminate the capability array. */ 220 221 rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_ipc_gate_cap, _ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; 222 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; 223 224 /* Map these additional capabilities. */ 225 226 _process.map_capabilities(rm_mapped_caps, false); 227 228 /* Define the IPC gate as an initial capability to be acquired by the region 229 mapper via the l4re_env API. The capability index is assigned above when 230 mapping the capability and encoded in the entry below. */ 231 232 l4re_env_cap_entry_t rm_init_caps[] = { 233 l4re_env_cap_entry_t(ENV_INTERNAL_PAGER_NAME, _ipc_gate_cap, L4_CAP_FPAGE_RWS), 234 l4re_env_cap_entry_t() 235 }; 236 237 /* NOTE: Environment vector is currently not defined. */ 238 239 const char *envp[] = {NULL}; 240 241 /* Configure the environment for the thread, specifying the pager (and 242 exception handler plus region mapper). */ 243 244 l4_cap_idx_t mapped_pager = L4_INVALID_CAP; 245 long err = _process.configure_thread(pager, &mapped_pager); 246 247 if (err) 248 return err; 249 250 _exec_pager->set_pager(pager, mapped_pager); 251 252 /* Populate a thread stack with argument and environment details for the 253 region mapper, plus the initial server capability and region details. */ 254 255 const char *argv[] = {_rm_filename}; 256 Stack rm_st(*_rm_stack); 257 258 rm_st.set_init_caps(rm_init_caps); 259 rm_st.set_regions(rm_regions); 260 rm_st.populate(1, argv, envp); 261 262 /* Start the region mapper thread in the appropriate stack. */ 263 264 l4_cap_idx_t thread, mapped_thread; 265 err = _process.thread_start(_rm_payload->entry_point(), rm_st, &thread, &mapped_thread); 266 267 if (err) 268 return err; 269 270 _exec_pager->add_thread(thread, mapped_thread); 271 return L4_EOK; 272 } 273 274 /* Configure a thread for a program, populate its stack, and start the 275 thread. */ 276 277 long ProcessCreating::start_program(int argc, const char *argv[]) 278 { 279 /* NOTE: Environment vector is currently not defined. */ 280 281 const char *envp[] = {NULL}; 282 283 /* Configure the environment for the thread, specifying the pager (and 284 exception handler plus region mapper). */ 285 286 l4_cap_idx_t mapped_pager = _ipc_gate_cap; 287 long err = _process.configure_thread(_ipc_gate, &mapped_pager); 288 289 if (err) 290 return err; 291 292 /* Populate a thread stack with argument and environment details for the 293 actual program. The server capability should be assigned to the region 294 mapper capability slot already. */ 295 296 Stack program_st(*_program_stack); 297 298 program_st.populate(argc, argv, envp); 299 300 /* Start the program thread in the appropriate stack. */ 301 302 l4_cap_idx_t thread, mapped_thread; 303 err = _process.thread_start(_program_payload->entry_point(), program_st, &thread, &mapped_thread); 304 305 if (err) 306 return err; 307 308 _exec_pager->add_thread(thread, mapped_thread); 309 return L4_EOK; 310 } 311 312 /* Start a new process for the payload indicated by the first of the given 313 program arguments, returning a reference to the pager as an object for 314 interacting with the process. */ 315 316 long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t *process) 317 { 318 file_t *file = client_open(argv[0], O_RDONLY); 319 long err; 320 321 /* Open the program file, handling any error conditions. If successfully 322 opened, it will be closed when the process terminates. */ 323 324 if (file == NULL) 325 return -L4_EIO; 326 327 if (!client_opened(file)) 328 { 329 err = file->error; 330 client_close(file); 331 return err; 332 } 333 334 /* Initialise the different elements of the process. */ 335 336 err = init_region_mapper(); 337 if (err) 338 return err; 339 340 err = init_program(file); 341 if (err) 342 return err; 343 344 err = init_external_pager(process); 345 if (err) 346 return err; 347 348 err = configure_task(*process); 349 if (err) 350 return err; 351 352 err = create_ipc_gate(); 353 if (err) 354 return err; 355 356 err = start_region_mapper(*process); 357 if (err) 358 return err; 359 360 err = start_program(argc, argv); 361 if (err) 362 return err; 363 364 /* Discard instances created to initialise the process. The region mapper 365 relies on resources associated with its payload and stack and so these 366 cannot be deleted immediately. Instead, they are released when the pager is 367 deallocated. 368 369 NOTE: The region mapper payload could be retained instead of being 370 reconstructed each time. */ 371 372 delete _program_payload; 373 delete _program_stack; 374 375 return L4_EOK; 376 } 377 378 /* vim: tabstop=2 expandtab shiftwidth=2 379 */