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