1 /* 2 * Support for executing code in new tasks and threads. 3 * 4 * Copyright (C) 2022 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 #include <l4/sys/err.h> 24 #include <l4/util/util.h> 25 26 #include <exec/elf.h> 27 #include <exec/external_pager.h> 28 #include <exec/memory.h> 29 #include <exec/process.h> 30 #include <ipc/cap_alloc.h> 31 #include <ipc/map.h> 32 #include <ipc/server.h> 33 34 #include <stdio.h> 35 36 #include <pthread-l4.h> 37 #include <pthread.h> 38 39 #include "parent_pager_object_server.h" 40 41 42 43 /* External system-level pager for the region mapper in a created task. The 44 allocated regions requested by the region mapper are constrained to an area 45 of memory that must not overlap with the area reserved for the program being 46 run. */ 47 48 static ExternalPager exec_pager(0, 10 * L4_PAGESIZE); 49 50 static const offset_t initial_stack_size = 16 * L4_PAGESIZE; 51 52 53 54 /* Start the system pager in a separate thread. */ 55 56 static long start_pager(ipc_server_config_type &config) 57 { 58 pthread_t pager_thread; 59 pthread_attr_t attr; 60 61 pthread_attr_init(&attr); 62 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 63 64 ipc_server_init_for(&config, ParentPagerObject, &exec_pager); 65 66 long err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); 67 68 if (err) 69 return err; 70 71 return ipc_server_start_config_thread(&config, pthread_l4_cap(pager_thread)); 72 } 73 74 75 76 int main(int argc, char *argv[]) 77 { 78 long err; 79 80 if (argc < 3) 81 { 82 printf("Need a program to run as the region mapper and a main program.\n"); 83 return 1; 84 } 85 86 /* Define the different payloads. */ 87 88 char *rm_filename = argv[1]; 89 char *program_filename = argv[2]; 90 91 /* Initialise the memory segments of the region mapper. These are mapped into 92 this task so that we may access them. */ 93 94 ExplicitSegment rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); 95 Payload *rm_payload; 96 97 if (exec_get_payload(rm_filename, &rm_payload, true)) 98 { 99 printf("Could not initialise region mapper: %s\n", rm_filename); 100 return 1; 101 } 102 103 if (rm_stack.allocate(true)) 104 { 105 printf("Could not allocate region mapper stack.\n"); 106 return 1; 107 } 108 109 /* Initialise the memory segments of the actual program. These are not mapped 110 into this task, instead being accessed by the region mapper in the new 111 task. */ 112 113 ExplicitSegment program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW); 114 Payload *program_payload; 115 116 if (exec_get_payload(program_filename, &program_payload, false)) 117 { 118 printf("Could not initialise program: %s\n", program_filename); 119 return 1; 120 } 121 122 if (program_stack.allocate(true)) 123 { 124 printf("Could not allocate program stack.\n"); 125 return 1; 126 } 127 128 /* Initialise pager regions for the region mapper. */ 129 130 for (unsigned int i = 0; i < rm_payload->segments(); i++) 131 { 132 if (rm_payload->segment(i)->loadable()) 133 exec_pager.add(rm_payload->segment(i)->region()); 134 } 135 136 exec_pager.add(rm_stack.region()); 137 138 /* Start the pager in a separate thread. */ 139 140 ipc_server_config_type config; 141 142 printf("Starting pager thread...\n"); 143 144 err = start_pager(config); 145 146 if (err) 147 { 148 printf("Could not start pager.\n"); 149 return 1; 150 } 151 152 /* Configure the environment for the task, reserving two threads. */ 153 154 Process process; 155 156 err = process.configure_task(); 157 158 if (err) 159 { 160 printf("Could not configure task.\n"); 161 return 1; 162 } 163 164 /* Configure the environment for the thread, specifying the pager (and 165 exception handler plus region mapper). */ 166 167 err = process.configure_thread(config.server); 168 169 if (err) 170 { 171 printf("Could not configure thread.\n"); 172 return 1; 173 } 174 175 err = process.set_parent(config.server); 176 177 if (err) 178 { 179 printf("Could not map parent to task for internal pager.\n"); 180 return 1; 181 } 182 183 /* Create an unbound IPC gate for the region mapper. */ 184 185 l4_cap_idx_t ipc_gate = ipc_cap_alloc(); 186 187 if (l4_is_invalid_cap(ipc_gate)) 188 { 189 printf("Could not allocate IPC gate capability.\n"); 190 return 1; 191 } 192 193 err = l4_error(l4_factory_create_gate(l4re_env()->factory, ipc_gate, L4_INVALID_CAP, 0)); 194 195 if (err) 196 { 197 printf("Could not create IPC gate.\n"); 198 return 1; 199 } 200 201 /* Define regions employing dataspaces to provide program segments. 202 203 Define capabilities for mapping, including region dataspace capabilities, 204 the stack dataspace capability, and the server capability. 205 206 Here, the arrays are sized for the maximum number of regions and 207 capabilities, but in practice only the loadable segments are used, leaving 208 fewer elements utilised. A terminating entry is employed to indicate the 209 limit of utilised elements. */ 210 211 struct exec_region rm_regions[rm_payload->segments() + 2]; 212 struct ipc_mapped_cap rm_mapped_caps[rm_payload->segments() + 3]; 213 l4_cap_idx_t mapped_cap; 214 unsigned int rm_index = 0; 215 216 for (unsigned int i = 0; i < program_payload->segments(); i++) 217 { 218 Segment *s = program_payload->segment(i); 219 220 if (s->loadable()) 221 { 222 mapped_cap = process.allocate_cap(); 223 rm_regions[rm_index] = s->exec_region(); 224 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, rm_regions[rm_index].ds, L4_CAP_FPAGE_RWS, 0}; 225 rm_regions[rm_index].ds = mapped_cap; 226 rm_index++; 227 } 228 } 229 230 /* Introduce the stack region and capability. */ 231 232 mapped_cap = process.allocate_cap(); 233 rm_regions[rm_index] = program_stack.exec_region(); 234 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, program_stack.exec_region().ds, L4_CAP_FPAGE_RWS, 0}; 235 rm_regions[rm_index].ds = mapped_cap; 236 rm_index++; 237 238 /* Terminate the region array. */ 239 240 rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; 241 242 /* Introduce the server capability. */ 243 244 l4_cap_idx_t ipc_gate_cap = process.allocate_cap(); 245 246 printf("Mapping %lx to %lx in task.\n", ipc_gate, ipc_gate_cap); 247 248 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {ipc_gate_cap, ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; 249 rm_index++; 250 251 /* Terminate the capability array. */ 252 253 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; 254 255 /* Map these additional capabilities. */ 256 257 printf("Map additional capabilities...\n"); 258 259 process.map_capabilities(rm_mapped_caps, false); 260 261 /* Define the IPC gate as an initial capability to be acquired by the region 262 mapper via the l4re_env API. The capability index is assigned above when 263 mapping the capability and encoded in the entry below. */ 264 265 l4re_env_cap_entry_t rm_init_caps[] = { 266 l4re_env_cap_entry_t("server", ipc_gate_cap, L4_CAP_FPAGE_RWS), 267 l4re_env_cap_entry_t() 268 }; 269 270 /* NOTE: Environment vector is currently not defined. */ 271 272 char *envp[] = {NULL}; 273 274 /* Populate a thread stack with argument and environment details for the 275 region mapper, plus the initial server capability and region details. */ 276 277 printf("Populating region mapper stack...\n"); 278 279 Stack rm_st(rm_stack); 280 281 rm_st.set_init_caps(rm_init_caps); 282 rm_st.set_regions(rm_regions); 283 rm_st.populate(1, argv + 1, envp); 284 285 /* Start the region mapper thread in the appropriate stack. */ 286 287 printf("Run region mapper thread...\n"); 288 289 err = process.thread_start(rm_payload->entry_point(), rm_st); 290 291 if (err) 292 { 293 printf("Could not run thread for region mapper.\n"); 294 return 1; 295 } 296 297 /* Configure the environment for the thread, specifying the pager (and 298 exception handler plus region mapper). */ 299 300 err = process.configure_thread(ipc_gate, ipc_gate_cap); 301 302 if (err) 303 { 304 printf("Could not configure task.\n"); 305 return 1; 306 } 307 308 err = process.set_parent(config.server); 309 310 if (err) 311 { 312 printf("Could not map parent to task for payload.\n"); 313 return 1; 314 } 315 316 /* Populate a thread stack with argument and environment details for the 317 actual program. The server capability should be assigned to the region 318 mapper capability slot already. */ 319 320 printf("Populating program stack...\n"); 321 322 Stack program_st(program_stack); 323 324 program_st.populate(argc - 2, argv + 2, envp); 325 326 /* Start the program thread in the appropriate stack. */ 327 328 printf("Run program thread...\n"); 329 330 err = process.thread_start(program_payload->entry_point(), program_st); 331 332 if (err) 333 { 334 printf("Could not run thread for program: %s\n", l4sys_errtostr(err)); 335 return 1; 336 } 337 338 printf("Finished program initiation.\n"); 339 printf("End of test.\n"); 340 341 /* NOTE: Should be able to obtain a notification for when the program 342 finishes, which might be done using a capability deletion IRQ. 343 Eventually, this program will operate as a server, invoking new 344 programs and handling termination. This test would then merely 345 involve the invocation of a utility function. */ 346 347 while (1) 348 l4_sleep_forever(); 349 350 return 0; 351 } 352 353 /* vim: tabstop=2 expandtab shiftwidth=2 354 */