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 <ipc/cap_alloc.h> 25 #include <ipc/map.h> 26 27 #include <stdio.h> 28 29 #include <pthread-l4.h> 30 #include <pthread.h> 31 32 #include "parent_pager_object_server.h" 33 #include "process_creating.h" 34 35 36 37 /* Process stack configuration. */ 38 39 static const offset_t initial_stack_size = 16 * L4_PAGESIZE; 40 41 42 43 /* Initialise the process creator with the details of a region mapper. */ 44 45 ProcessCreating::ProcessCreating(const char *rm_filename) 46 : _rm_filename(rm_filename), 47 _rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW), 48 _program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW) 49 { 50 } 51 52 /* Start the system pager in a separate thread. */ 53 54 long ProcessCreating::start_pager() 55 { 56 pthread_t pager_thread; 57 pthread_attr_t attr; 58 59 pthread_attr_init(&attr); 60 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 61 62 ipc_server_init_for(&_config, ParentPagerObject, _exec_pager); 63 64 long err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &_config); 65 66 if (err) 67 return err; 68 69 return ipc_server_start_config_thread(&_config, pthread_l4_cap(pager_thread)); 70 } 71 72 /* Initialise the memory segments of the region mapper. These are mapped into 73 this task so that we may access them, allowing the external pager in this 74 task to use them. */ 75 76 long ProcessCreating::init_region_mapper() 77 { 78 long err = exec_get_payload(_rm_filename, &_rm_payload, true); 79 80 if (err) 81 return err; 82 83 return _rm_stack.allocate(true); 84 } 85 86 /* Initialise the memory segments of the actual program. These are not mapped 87 into this task, instead being accessed by the region mapper in the new 88 task. */ 89 90 long ProcessCreating::init_program(file_t *file) 91 { 92 long err = exec_get_payload_file(file, &_program_payload, false); 93 94 if (err) 95 return err; 96 97 return _program_stack.allocate(true); 98 } 99 100 /* Initialise an external system-level pager serving the region mapper in a 101 created task. The allocated regions requested by the region mapper are 102 constrained to an area of memory that must not overlap with the area reserved 103 for the program being run. */ 104 105 long ProcessCreating::init_external_pager() 106 { 107 _exec_pager = new ExternalPager(0, 10 * L4_PAGESIZE); 108 109 /* Initialise pager regions for the region mapper. */ 110 111 for (unsigned int i = 0; i < _rm_payload->segments(); i++) 112 { 113 if (_rm_payload->segment(i)->loadable()) 114 _exec_pager->add(_rm_payload->segment(i)->region()); 115 } 116 117 /* Include the region mapper's stack region. */ 118 119 _exec_pager->add(_rm_stack.region()); 120 121 /* Start the pager in a separate thread. */ 122 123 return start_pager(); 124 } 125 126 /* Configure the environment for the task. */ 127 128 long ProcessCreating::configure_task() 129 { 130 long err = _process.configure_task(); 131 132 if (err) 133 return err; 134 135 return _process.set_parent(_config.server); 136 } 137 138 /* Create an unbound IPC gate for the region mapper and allocate it in the 139 created process. */ 140 141 long ProcessCreating::create_ipc_gate() 142 { 143 _ipc_gate_cap = _process.allocate_cap(); 144 _ipc_gate = ipc_cap_alloc(); 145 146 if (l4_is_invalid_cap(_ipc_gate)) 147 return -L4_ENOMEM; 148 149 return l4_error(l4_factory_create_gate(l4re_env()->factory, _ipc_gate, L4_INVALID_CAP, 0)); 150 } 151 152 /* Initialise and assign a region in a list to the created process. */ 153 154 void ProcessCreating::init_region(struct exec_region *regions, 155 struct ipc_mapped_cap *mapped_caps, 156 struct exec_region &r, unsigned int &index) 157 { 158 l4_cap_idx_t mapped_cap = _process.allocate_cap(); 159 160 mapped_caps[index] = (struct ipc_mapped_cap) {mapped_cap, r.ds, L4_CAP_FPAGE_RWS, 0}; 161 162 /* Change the region definition to use the allocated capability in the created 163 process. */ 164 165 regions[index] = r; 166 regions[index].ds = mapped_cap; 167 index++; 168 } 169 170 /* Initialise the region mapper with details of the payload program regions 171 and of the associated capabilities, configure the region mapper thread, 172 populate its stack, and start the thread. */ 173 174 long ProcessCreating::start_region_mapper() 175 { 176 /* Define regions employing dataspaces to provide program segments. */ 177 178 struct exec_region rm_regions[_rm_payload->segments() + 2]; 179 180 /* Define capabilities for mapping, including region dataspace capabilities, 181 the stack dataspace capability, and the server capability. */ 182 183 struct ipc_mapped_cap rm_mapped_caps[_rm_payload->segments() + 3]; 184 185 /* Here, the arrays are sized for the maximum number of regions and 186 capabilities, but in practice only the loadable segments are used, leaving 187 fewer elements utilised. A terminating entry is employed to indicate the 188 limit of utilised elements. */ 189 190 unsigned int rm_index = 0; 191 192 for (unsigned int i = 0; i < _program_payload->segments(); i++) 193 { 194 Segment *s = _program_payload->segment(i); 195 196 if (s->loadable()) 197 init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index); 198 } 199 200 /* Introduce the stack region and capability. */ 201 202 init_region(rm_regions, rm_mapped_caps, _program_stack.exec_region(), rm_index); 203 204 /* Terminate the region array. */ 205 206 rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; 207 208 /* Introduce the server capability and terminate the capability array. */ 209 210 rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_ipc_gate_cap, _ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; 211 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; 212 213 /* Map these additional capabilities. */ 214 215 _process.map_capabilities(rm_mapped_caps, false); 216 217 /* Define the IPC gate as an initial capability to be acquired by the region 218 mapper via the l4re_env API. The capability index is assigned above when 219 mapping the capability and encoded in the entry below. */ 220 221 l4re_env_cap_entry_t rm_init_caps[] = { 222 l4re_env_cap_entry_t("server", _ipc_gate_cap, L4_CAP_FPAGE_RWS), 223 l4re_env_cap_entry_t() 224 }; 225 226 /* NOTE: Environment vector is currently not defined. */ 227 228 const char *envp[] = {NULL}; 229 230 /* Configure the environment for the thread, specifying the pager (and 231 exception handler plus region mapper). */ 232 233 long err = _process.configure_thread(_config.server); 234 235 if (err) 236 return err; 237 238 /* Populate a thread stack with argument and environment details for the 239 region mapper, plus the initial server capability and region details. */ 240 241 const char *argv[] = {_rm_filename}; 242 Stack rm_st(_rm_stack); 243 244 rm_st.set_init_caps(rm_init_caps); 245 rm_st.set_regions(rm_regions); 246 rm_st.populate(1, argv, envp); 247 248 /* Start the region mapper thread in the appropriate stack. */ 249 250 return _process.thread_start(_rm_payload->entry_point(), rm_st); 251 } 252 253 /* Configure a thread for a program, populate its stack, and start the 254 thread. */ 255 256 long ProcessCreating::start_program(int argc, const char *argv[]) 257 { 258 /* NOTE: Environment vector is currently not defined. */ 259 260 const char *envp[] = {NULL}; 261 262 /* Configure the environment for the thread, specifying the pager (and 263 exception handler plus region mapper). */ 264 265 long err = _process.configure_thread(_ipc_gate, _ipc_gate_cap); 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 return _process.thread_start(_program_payload->entry_point(), program_st); 281 } 282 283 /* Start a new process for the given payload, providing the indicated program 284 arguments, returning a reference to the pager. */ 285 286 long ProcessCreating::start(file_t *file, int argc, const char *argv[], 287 l4_cap_idx_t *process) 288 { 289 long err; 290 291 err = init_region_mapper(); 292 if (err) 293 return err; 294 295 err = init_program(file); 296 if (err) 297 return err; 298 299 err = init_external_pager(); 300 if (err) 301 return err; 302 303 err = configure_task(); 304 if (err) 305 return err; 306 307 err = create_ipc_gate(); 308 if (err) 309 return err; 310 311 err = start_region_mapper(); 312 if (err) 313 return err; 314 315 err = start_program(argc, argv); 316 if (err) 317 return err; 318 319 *process = _config.server; 320 321 /* Discard instances created to initialise the process. 322 NOTE: The region mapper payload could be retained instead of being 323 reconstructed each time. */ 324 325 delete _rm_payload; 326 delete _program_payload; 327 328 return L4_EOK; 329 } 330 331 /* vim: tabstop=2 expandtab shiftwidth=2 332 */