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