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_creator.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 ProcessCreator::ProcessCreator(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 ProcessCreator::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 ProcessCreator::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 ProcessCreator::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 ProcessCreator::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 ProcessCreator::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 ProcessCreator::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 ProcessCreator::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. */ 171 172 long ProcessCreator::start_region_mapper() 173 { 174 /* Define regions employing dataspaces to provide program segments. */ 175 176 struct exec_region rm_regions[_rm_payload->segments() + 2]; 177 178 /* Define capabilities for mapping, including region dataspace capabilities, 179 the stack dataspace capability, and the server capability. */ 180 181 struct ipc_mapped_cap rm_mapped_caps[_rm_payload->segments() + 3]; 182 183 /* Here, the arrays are sized for the maximum number of regions and 184 capabilities, but in practice only the loadable segments are used, leaving 185 fewer elements utilised. A terminating entry is employed to indicate the 186 limit of utilised elements. */ 187 188 unsigned int rm_index = 0; 189 190 for (unsigned int i = 0; i < _program_payload->segments(); i++) 191 { 192 Segment *s = _program_payload->segment(i); 193 194 if (s->loadable()) 195 init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index); 196 } 197 198 /* Introduce the stack region and capability. */ 199 200 init_region(rm_regions, rm_mapped_caps, _program_stack.exec_region(), rm_index); 201 202 /* Terminate the region array. */ 203 204 rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; 205 206 /* Introduce the server capability and terminate the capability array. */ 207 208 rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_ipc_gate_cap, _ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; 209 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; 210 211 /* Map these additional capabilities. */ 212 213 _process.map_capabilities(rm_mapped_caps, false); 214 215 /* Define the IPC gate as an initial capability to be acquired by the region 216 mapper via the l4re_env API. The capability index is assigned above when 217 mapping the capability and encoded in the entry below. */ 218 219 l4re_env_cap_entry_t rm_init_caps[] = { 220 l4re_env_cap_entry_t("server", _ipc_gate_cap, L4_CAP_FPAGE_RWS), 221 l4re_env_cap_entry_t() 222 }; 223 224 /* NOTE: Environment vector is currently not defined. */ 225 226 const char *envp[] = {NULL}; 227 228 /* Configure the environment for the thread, specifying the pager (and 229 exception handler plus region mapper). */ 230 231 long err = _process.configure_thread(_config.server); 232 233 if (err) 234 return err; 235 236 /* Populate a thread stack with argument and environment details for the 237 region mapper, plus the initial server capability and region details. */ 238 239 const char *argv[] = {_rm_filename}; 240 Stack rm_st(_rm_stack); 241 242 rm_st.set_init_caps(rm_init_caps); 243 rm_st.set_regions(rm_regions); 244 rm_st.populate(1, argv, envp); 245 246 /* Start the region mapper thread in the appropriate stack. */ 247 248 return _process.thread_start(_rm_payload->entry_point(), rm_st); 249 } 250 251 long ProcessCreator::start_program(int argc, const char *argv[]) 252 { 253 /* NOTE: Environment vector is currently not defined. */ 254 255 const char *envp[] = {NULL}; 256 257 /* Configure the environment for the thread, specifying the pager (and 258 exception handler plus region mapper). */ 259 260 long err = _process.configure_thread(_ipc_gate, _ipc_gate_cap); 261 262 if (err) 263 return err; 264 265 /* Populate a thread stack with argument and environment details for the 266 actual program. The server capability should be assigned to the region 267 mapper capability slot already. */ 268 269 Stack program_st(_program_stack); 270 271 program_st.populate(argc, argv, envp); 272 273 /* Start the program thread in the appropriate stack. */ 274 275 return _process.thread_start(_program_payload->entry_point(), program_st); 276 } 277 278 long ProcessCreator::start(file_t *file, int argc, const char *argv[]) 279 { 280 long err; 281 282 err = init_region_mapper(); 283 if (err) 284 return err; 285 286 err = init_program(file); 287 if (err) 288 return err; 289 290 err = init_external_pager(); 291 if (err) 292 return err; 293 294 err = configure_task(); 295 if (err) 296 return err; 297 298 err = create_ipc_gate(); 299 if (err) 300 return err; 301 302 err = start_region_mapper(); 303 if (err) 304 return err; 305 306 return start_program(argc, argv); 307 } 308 309 void ProcessCreator::set_notifier(Notifier *notifier) 310 { 311 _exec_pager.set_notifier(notifier); 312 } 313 314 /* vim: tabstop=2 expandtab shiftwidth=2 315 */