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/memory.h> 28 #include <exec/process.h> 29 #include <ipc/mem_ipc.h> 30 #include <ipc/server.h> 31 #include <mem/memory_utils.h> 32 33 #include <map> 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <pthread-l4.h> 40 #include <pthread.h> 41 42 #include "pager_object_interface.h" 43 #include "pager_object_server.h" 44 45 46 47 /* A simple system pager also acting as a region mapper. */ 48 49 typedef std::map<l4_addr_t, MappedRegion> MappedRegions; 50 51 class ExecPager : public PagerObject 52 { 53 protected: 54 MappedRegions _regions; 55 56 public: 57 virtual void add(MappedRegion region) 58 { 59 _regions[region.map_start] = region; 60 } 61 62 /* Notification methods. */ 63 64 virtual long exception(l4_exc_regs_t regs, 65 l4_snd_fpage_t *region); 66 67 virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, 68 l4_snd_fpage_t *region); 69 70 /* Region manager/mapper methods. */ 71 72 virtual long attach(address_t *start, offset_t size, map_flags_t flags, 73 l4_cap_idx_t ds, address_t offset, unsigned char align); 74 75 }; 76 77 /* Handle a general exception. */ 78 79 long ExecPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) 80 { 81 (void) region; 82 83 printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); 84 85 printf("r15 = %lx\n", regs.r15); 86 printf("r14 = %lx\n", regs.r14); 87 printf("r13 = %lx\n", regs.r13); 88 printf("r12 = %lx\n", regs.r12); 89 printf("r11 = %lx\n", regs.r11); 90 printf("r10 = %lx\n", regs.r10); 91 printf("r9 = %lx\n", regs.r9); 92 printf("r8 = %lx\n", regs.r8); 93 printf("rdi = %lx\n", regs.rdi); 94 printf("rsi = %lx\n", regs.rsi); 95 printf("rbp = %lx\n", regs.rbp); 96 printf("pfa = %lx\n", regs.pfa); 97 printf("rbx = %lx\n", regs.rbx); 98 printf("rdx = %lx\n", regs.rdx); 99 printf("rcx = %lx\n", regs.rcx); 100 printf("rax = %lx\n", regs.rax); 101 printf("trapno = %lx\n", regs.trapno); 102 printf("err = %lx\n", regs.err); 103 printf("ip = %lx\n", regs.ip); 104 printf("flags = %lx\n", regs.flags); 105 printf("sp = %lx\n", regs.sp); 106 printf("ss = %lx\n", regs.ss); 107 printf("fs_base = %lx\n", regs.fs_base); 108 printf("gs_base = %lx\n", regs.gs_base); 109 110 return L4_EOK; 111 } 112 113 #define DEBUG 0 114 115 /* Handle a page fault using any configured regions. */ 116 117 long ExecPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) 118 { 119 l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; 120 121 #if DEBUG 122 printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); 123 #endif 124 125 MappedRegions::iterator it = _regions.upper_bound(addr); 126 127 if (it != _regions.begin()) 128 it--; 129 else 130 { 131 printf("not mapped!\n"); 132 return -L4_ENOMEM; 133 } 134 135 MappedRegion &r = it->second; 136 137 if ((addr >= r.map_start) && (addr < r.map_start + r.size)) 138 { 139 l4_addr_t page_addr = trunc(addr, L4_PAGESIZE); 140 141 region->fpage = l4_fpage(r.start + (page_addr - r.map_start), L4_PAGESHIFT, r.flags); 142 region->snd_base = page_addr; 143 144 #if DEBUG 145 printf("%lx...%lx from %lx...%lx offset %lx size %d rights %x\n", 146 r.map_start, region->snd_base, 147 r.start, l4_fpage_memaddr(region->fpage), 148 addr - r.map_start, 149 l4_fpage_size(region->fpage), 150 l4_fpage_rights(region->fpage)); 151 152 printf("%lx -> ", addr); 153 154 for (unsigned int i = 0; i < sizeof(l4_umword_t); i++) 155 printf("%02x", *((unsigned char *)(r.start + (addr - r.map_start) + i))); 156 157 printf("\n"); 158 #endif 159 160 if (r.flags & L4RE_RM_F_W) 161 l4_touch_rw((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); 162 else 163 l4_touch_ro((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); 164 165 return L4_EOK; 166 } 167 168 #if DEBUG 169 printf("not mapped!\n"); 170 #endif 171 172 return -L4_ENOMEM; 173 } 174 175 /* Attach a region for provision when page faults occur. This is required in 176 the initialisation of a program by the C library which requires a region 177 mapper. */ 178 179 long ExecPager::attach(address_t *start, offset_t size, map_flags_t flags, 180 l4_cap_idx_t ds, address_t offset, unsigned char align) 181 { 182 #if DEBUG 183 printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); 184 #endif 185 186 if (align < L4_PAGESHIFT) 187 align = L4_PAGESHIFT; 188 189 offset_t increment = 1UL << align; 190 offset_t region_size = round(size, increment); 191 192 /* Either attempt to find an address for the specified region, starting from 193 any indicated address. */ 194 195 if (flags & L4RE_RM_F_SEARCH_ADDR) 196 { 197 address_t region_start = trunc(*start, increment); 198 MappedRegions::iterator it = _regions.upper_bound(*start); 199 200 if (!region_start) 201 region_start += increment; 202 203 #if DEBUG 204 printf("-> search from %lx -> %lx...\n", *start, region_start); 205 #endif 206 207 /* Before last known region. */ 208 209 while (it != _regions.end()) 210 { 211 MappedRegions::iterator next = it; 212 MappedRegion &r = it->second; 213 address_t start_limit; 214 address_t end_limit = r.map_start; 215 216 /* Consider any preceding region. If no such region exists, choose an 217 address at the start of memory. */ 218 219 if (it == _regions.begin()) 220 start_limit = L4_PAGESIZE; 221 else 222 { 223 it--; 224 MappedRegion &pr = it->second; 225 start_limit = pr.map_start + pr.size; 226 it = next; 227 } 228 229 /* Test against the limits. */ 230 231 if (region_start < start_limit) 232 region_start = round(start_limit, increment); 233 234 /* Investigate subsequent regions if not enough space exists between the 235 preceding region (or start of memory) and the current region. */ 236 237 if ((region_start + region_size) > end_limit) 238 { 239 it++; 240 if (it == _regions.end()) 241 return -L4_ENOMEM; 242 } 243 else 244 break; 245 } 246 247 /* Attach the provided dataspace. 248 NOTE: This is only done in this implementation to support the paging 249 mechanism. In a region mapper residing within the actual task, the 250 dataspace's map operation would be invoked to obtain mappings. */ 251 252 l4_addr_t ds_start; 253 254 long err = ipc_attach_dataspace(ds, size, (void **) &ds_start); 255 256 if (err) 257 return err; 258 259 l4_touch_rw((const void *) ds_start, size); 260 261 #if DEBUG 262 printf("-> added region for %lx size %ld (%d)\n", region_start, region_size, page_order(region_size)); 263 #endif 264 265 add(MappedRegion(ds_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, region_start)); 266 267 *start = region_start; 268 return L4_EOK; 269 } 270 271 /* Or attempt to add the specified region at a specific address. */ 272 273 else 274 { 275 // NOTE: To be implemented. 276 277 #if DEBUG 278 printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size)); 279 #endif 280 281 return -L4_ENOMEM; 282 } 283 } 284 285 286 287 static ExecPager exec_pager; 288 289 static void init_pager(ipc_server_config_type *config) 290 { 291 ipc_server_init_config(config); 292 293 config->expected_items = PagerObject_expected_items; 294 config->handler = (ipc_server_handler_type) handle_PagerObject; 295 config->handler_obj = static_cast<PagerObject *>(&exec_pager); 296 } 297 298 static long start_pager(ipc_server_config_type *config, pthread_t thread) 299 { 300 config->config_thread = 1; 301 config->thread = pthread_l4_cap(thread); 302 303 printf("Starting pager thread...\n"); 304 return ipc_server_start_config(config); 305 } 306 307 308 309 int main(int argc, char *argv[]) 310 { 311 long err; 312 313 if (argc < 2) 314 { 315 printf("Need a program to run.\n"); 316 return 1; 317 } 318 319 /* Initialise the memory of the new task. */ 320 321 offset_t initial_stack_size = 16 * L4_PAGESIZE; 322 ExplicitSegment stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); 323 Payload *payload; 324 325 if (exec_get_payload(argv[1], &payload)) 326 { 327 printf("Could not initialise program.\n"); 328 return 1; 329 } 330 331 if (stack.allocate()) 332 { 333 printf("Could not allocate stack.\n"); 334 return 1; 335 } 336 337 /* Start the pager. */ 338 339 ipc_server_config_type config; 340 pthread_t pager_thread; 341 pthread_attr_t attr; 342 343 pthread_attr_init(&attr); 344 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 345 346 init_pager(&config); 347 348 for (unsigned int i = 0; i < payload->segments(); i++) 349 { 350 if (payload->segment(i)->loadable()) 351 exec_pager.add(payload->segment(i)->region()); 352 } 353 354 exec_pager.add(stack.region()); 355 356 err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); 357 358 if (err) 359 { 360 printf("Could not start pager thread.\n"); 361 return 1; 362 } 363 364 err = start_pager(&config, pager_thread); 365 366 if (err) 367 { 368 printf("Could not start pager.\n"); 369 return 1; 370 } 371 372 /* Configure the environment for the task, specifying the pager (and exception 373 handler plus region mapper). */ 374 375 Process process; 376 377 err = process.configure(config.server); 378 379 if (err) 380 { 381 printf("Could not configure task.\n"); 382 return 1; 383 } 384 385 /* Populate a thread stack with argument and environment details. */ 386 387 Stack st(stack); 388 389 /* NOTE: Environment vector is currently not defined. */ 390 391 char *envp[] = {NULL}; 392 393 st.populate(argc - 1, argv + 1, envp); 394 395 /* Start the new thread in the given stack. */ 396 397 printf("Run thread...\n"); 398 399 err = process.thread_start(payload->entry_point(), st); 400 401 if (err) 402 { 403 printf("Could not run thread.\n"); 404 return 1; 405 } 406 407 printf("Finished.\n"); 408 while (1); 409 410 return 0; 411 } 412 413 /* vim: tabstop=2 expandtab shiftwidth=2 414 */