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 "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, PagerObject, &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 /* Create an unbound IPC gate for the region mapper. */ 176 177 l4_cap_idx_t ipc_gate = ipc_cap_alloc(); 178 179 if (l4_is_invalid_cap(ipc_gate)) 180 { 181 printf("Could not allocate IPC gate capability.\n"); 182 return 1; 183 } 184 185 err = l4_error(l4_factory_create_gate(l4re_env()->factory, ipc_gate, L4_INVALID_CAP, 0)); 186 187 if (err) 188 { 189 printf("Could not create IPC gate.\n"); 190 return 1; 191 } 192 193 /* Define regions employing dataspaces to provide program segments. 194 195 Define capabilities for mapping, including region dataspace capabilities, 196 the stack dataspace capability, and the server capability. 197 198 Here, the arrays are sized for the maximum number of regions and 199 capabilities, but in practice only the loadable segments are used, leaving 200 fewer elements utilised. A terminating entry is employed to indicate the 201 limit of utilised elements. */ 202 203 struct exec_region rm_regions[rm_payload->segments() + 2]; 204 struct ipc_mapped_cap rm_mapped_caps[rm_payload->segments() + 3]; 205 l4_cap_idx_t mapped_cap; 206 unsigned int rm_index = 0; 207 208 for (unsigned int i = 0; i < program_payload->segments(); i++) 209 { 210 Segment *s = program_payload->segment(i); 211 212 if (s->loadable()) 213 { 214 mapped_cap = process.allocate_cap(); 215 rm_regions[rm_index] = s->exec_region(); 216 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, rm_regions[rm_index].ds, L4_CAP_FPAGE_RWS, 0}; 217 rm_regions[rm_index].ds = mapped_cap; 218 rm_index++; 219 } 220 } 221 222 /* Introduce the stack region and capability. */ 223 224 mapped_cap = process.allocate_cap(); 225 rm_regions[rm_index] = program_stack.exec_region(); 226 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, program_stack.exec_region().ds, L4_CAP_FPAGE_RWS, 0}; 227 rm_regions[rm_index].ds = mapped_cap; 228 rm_index++; 229 230 /* Terminate the region array. */ 231 232 rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; 233 234 /* Introduce the server capability. */ 235 236 l4_cap_idx_t ipc_gate_cap = process.allocate_cap(); 237 238 printf("Mapping %lx to %lx in task.\n", ipc_gate, ipc_gate_cap); 239 240 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {ipc_gate_cap, ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; 241 rm_index++; 242 243 /* Terminate the capability array. */ 244 245 rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; 246 247 /* Map these additional capabilities. */ 248 249 printf("Map additional capabilities...\n"); 250 251 process.map_capabilities(rm_mapped_caps, false); 252 253 /* Define the IPC gate as an initial capability to be acquired by the region 254 mapper via the l4re_env API. The capability index is assigned above when 255 mapping the capability and encoded in the entry below. */ 256 257 l4re_env_cap_entry_t rm_init_caps[] = { 258 l4re_env_cap_entry_t("server", ipc_gate_cap, L4_CAP_FPAGE_RWS), 259 l4re_env_cap_entry_t() 260 }; 261 262 /* NOTE: Environment vector is currently not defined. */ 263 264 char *envp[] = {NULL}; 265 266 /* Populate a thread stack with argument and environment details for the 267 region mapper, plus the initial server capability and region details. */ 268 269 printf("Populating region mapper stack...\n"); 270 271 Stack rm_st(rm_stack); 272 273 rm_st.set_init_caps(rm_init_caps); 274 rm_st.set_regions(rm_regions); 275 rm_st.populate(1, argv + 1, envp); 276 277 /* Start the region mapper thread in the appropriate stack. */ 278 279 printf("Run region mapper thread...\n"); 280 281 err = process.thread_start(rm_payload->entry_point(), rm_st); 282 283 if (err) 284 { 285 printf("Could not run thread for region mapper.\n"); 286 return 1; 287 } 288 289 /* Configure the environment for the thread, specifying the pager (and 290 exception handler plus region mapper). */ 291 292 err = process.configure_thread(ipc_gate, ipc_gate_cap); 293 294 if (err) 295 { 296 printf("Could not configure task.\n"); 297 return 1; 298 } 299 300 /* Populate a thread stack with argument and environment details for the 301 actual program. The server capability should be assigned to the region 302 mapper capability slot already. */ 303 304 printf("Populating program stack...\n"); 305 306 Stack program_st(program_stack); 307 308 program_st.populate(argc - 2, argv + 2, envp); 309 310 /* Start the program thread in the appropriate stack. */ 311 312 printf("Run program thread...\n"); 313 314 err = process.thread_start(program_payload->entry_point(), program_st); 315 316 if (err) 317 { 318 printf("Could not run thread for program: %s\n", l4sys_errtostr(err)); 319 return 1; 320 } 321 322 printf("Finished program initiation.\n"); 323 printf("End of test.\n"); 324 325 /* NOTE: Should be able to obtain a notification for when the program 326 finishes, which might be done using a capability deletion IRQ. 327 Eventually, this program will operate as a server, invoking new 328 programs and handling termination. This test would then merely 329 involve the invocation of a utility function. */ 330 331 while (1) 332 l4_sleep_forever(); 333 334 return 0; 335 } 336 337 /* vim: tabstop=2 expandtab shiftwidth=2 338 */