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