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