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