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