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