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