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