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