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/re/l4aux.h> 24 #include <l4/sys/err.h> 25 #include <l4/util/elf.h> 26 #include <l4/util/util.h> 27 28 #include <exec/elf.h> 29 #include <exec/process.h> 30 #include <fsclient/client.h> 31 #include <ipc/mem_ipc.h> 32 #include <ipc/server.h> 33 #include <mem/memory_utils.h> 34 #include <systypes/fcntl.h> 35 36 #include <map> 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include <pthread-l4.h> 43 #include <pthread.h> 44 45 #include "pager_object_interface.h" 46 #include "pager_object_server.h" 47 48 49 50 /* A simple system pager also acting as a region mapper. */ 51 52 typedef std::map<l4_addr_t, MappedRegion> MappedRegions; 53 54 class ExecPager : public PagerObject 55 { 56 protected: 57 MappedRegions _regions; 58 59 public: 60 virtual void add(MappedRegion region) 61 { 62 _regions[region.map_start] = region; 63 } 64 65 /* Notification methods. */ 66 67 virtual long exception(l4_exc_regs_t regs, 68 l4_snd_fpage_t *region); 69 70 virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, 71 l4_snd_fpage_t *region); 72 73 /* Region manager/mapper methods. */ 74 75 virtual long attach(address_t *start, offset_t size, map_flags_t flags, 76 l4_cap_idx_t ds, address_t offset, unsigned char align); 77 78 }; 79 80 /* Handle a general exception. */ 81 82 long ExecPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) 83 { 84 (void) region; 85 86 printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); 87 88 printf("r15 = %lx\n", regs.r15); 89 printf("r14 = %lx\n", regs.r14); 90 printf("r13 = %lx\n", regs.r13); 91 printf("r12 = %lx\n", regs.r12); 92 printf("r11 = %lx\n", regs.r11); 93 printf("r10 = %lx\n", regs.r10); 94 printf("r9 = %lx\n", regs.r9); 95 printf("r8 = %lx\n", regs.r8); 96 printf("rdi = %lx\n", regs.rdi); 97 printf("rsi = %lx\n", regs.rsi); 98 printf("rbp = %lx\n", regs.rbp); 99 printf("pfa = %lx\n", regs.pfa); 100 printf("rbx = %lx\n", regs.rbx); 101 printf("rdx = %lx\n", regs.rdx); 102 printf("rcx = %lx\n", regs.rcx); 103 printf("rax = %lx\n", regs.rax); 104 printf("trapno = %lx\n", regs.trapno); 105 printf("err = %lx\n", regs.err); 106 printf("ip = %lx\n", regs.ip); 107 printf("flags = %lx\n", regs.flags); 108 printf("sp = %lx\n", regs.sp); 109 printf("ss = %lx\n", regs.ss); 110 printf("fs_base = %lx\n", regs.fs_base); 111 printf("gs_base = %lx\n", regs.gs_base); 112 113 return L4_EOK; 114 } 115 116 #define DEBUG 0 117 118 /* Handle a page fault using any configured regions. */ 119 120 long ExecPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) 121 { 122 l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; 123 124 #if DEBUG 125 printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); 126 #endif 127 128 MappedRegions::iterator it = _regions.upper_bound(addr); 129 130 if (it != _regions.begin()) 131 it--; 132 else 133 { 134 printf("not mapped!\n"); 135 return -L4_ENOMEM; 136 } 137 138 MappedRegion &r = it->second; 139 140 if ((addr >= r.map_start) && (addr < r.map_start + r.size)) 141 { 142 l4_addr_t page_addr = trunc(addr, L4_PAGESIZE); 143 144 region->fpage = l4_fpage(r.start + (page_addr - r.map_start), L4_PAGESHIFT, r.flags); 145 region->snd_base = page_addr; 146 147 #if DEBUG 148 printf("%lx...%lx from %lx...%lx offset %lx size %d rights %x\n", 149 r.map_start, region->snd_base, 150 r.start, l4_fpage_memaddr(region->fpage), 151 addr - r.map_start, 152 l4_fpage_size(region->fpage), 153 l4_fpage_rights(region->fpage)); 154 155 printf("%lx -> ", addr); 156 157 for (unsigned int i = 0; i < sizeof(l4_umword_t); i++) 158 printf("%02x", *((unsigned char *)(r.start + (addr - r.map_start) + i))); 159 160 printf("\n"); 161 #endif 162 163 if (r.flags & L4RE_RM_F_W) 164 l4_touch_rw((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); 165 else 166 l4_touch_ro((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); 167 168 return L4_EOK; 169 } 170 171 #if DEBUG 172 printf("not mapped!\n"); 173 #endif 174 175 return -L4_ENOMEM; 176 } 177 178 /* Attach a region for provision when page faults occur. This is required in 179 the initialisation of a program by the C library which requires a region 180 mapper. */ 181 182 long ExecPager::attach(address_t *start, offset_t size, map_flags_t flags, 183 l4_cap_idx_t ds, address_t offset, unsigned char align) 184 { 185 #if DEBUG 186 printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); 187 #endif 188 189 if (align < L4_PAGESHIFT) 190 align = L4_PAGESHIFT; 191 192 offset_t increment = 1UL << align; 193 offset_t region_size = round(size, increment); 194 195 /* Either attempt to find an address for the specified region, starting from 196 any indicated address. */ 197 198 if (flags & L4RE_RM_F_SEARCH_ADDR) 199 { 200 address_t region_start = trunc(*start, increment); 201 MappedRegions::iterator it = _regions.upper_bound(*start); 202 203 if (!region_start) 204 region_start += increment; 205 206 #if DEBUG 207 printf("-> search from %lx -> %lx...\n", *start, region_start); 208 #endif 209 210 /* Before last known region. */ 211 212 while (it != _regions.end()) 213 { 214 MappedRegions::iterator next = it; 215 MappedRegion &r = it->second; 216 address_t start_limit; 217 address_t end_limit = r.map_start; 218 219 /* Consider any preceding region. If no such region exists, choose an 220 address at the start of memory. */ 221 222 if (it == _regions.begin()) 223 start_limit = L4_PAGESIZE; 224 else 225 { 226 it--; 227 MappedRegion &pr = it->second; 228 start_limit = pr.map_start + pr.size; 229 it = next; 230 } 231 232 /* Test against the limits. */ 233 234 if (region_start < start_limit) 235 region_start = round(start_limit, increment); 236 237 /* Investigate subsequent regions if not enough space exists between the 238 preceding region (or start of memory) and the current region. */ 239 240 if ((region_start + region_size) > end_limit) 241 { 242 it++; 243 if (it == _regions.end()) 244 return -L4_ENOMEM; 245 } 246 else 247 break; 248 } 249 250 /* Attach the provided dataspace. 251 NOTE: This is only done in this implementation to support the paging 252 mechanism. In a region mapper residing within the actual task, the 253 dataspace's map operation would be invoked to obtain mappings. */ 254 255 l4_addr_t ds_start; 256 257 long err = ipc_attach_dataspace(ds, size, (void **) &ds_start); 258 259 if (err) 260 return err; 261 262 l4_touch_rw((const void *) ds_start, size); 263 264 #if DEBUG 265 printf("-> added region for %lx size %ld (%d)\n", region_start, region_size, page_order(region_size)); 266 #endif 267 268 add(MappedRegion(ds_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, region_start)); 269 270 *start = region_start; 271 return L4_EOK; 272 } 273 274 /* Or attempt to add the specified region at a specific address. */ 275 276 else 277 { 278 // NOTE: To be implemented. 279 280 #if DEBUG 281 printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size)); 282 #endif 283 284 return -L4_ENOMEM; 285 } 286 } 287 288 289 290 static ExecPager exec_pager; 291 292 static void init_pager(ipc_server_config_type *config) 293 { 294 ipc_server_init_config(config); 295 296 config->expected_items = PagerObject_expected_items; 297 config->handler = (ipc_server_handler_type) handle_PagerObject; 298 config->handler_obj = static_cast<PagerObject *>(&exec_pager); 299 } 300 301 static long start_pager(ipc_server_config_type *config, pthread_t thread) 302 { 303 config->config_thread = 1; 304 config->thread = pthread_l4_cap(thread); 305 306 printf("Starting pager thread...\n"); 307 return ipc_server_start_config(config); 308 } 309 310 311 312 int main(int argc, char *argv[]) 313 { 314 long err; 315 316 if (argc < 2) 317 { 318 printf("Need a program to run.\n"); 319 return 1; 320 } 321 322 /* Obtain the payload as a dataspace. */ 323 324 file_t *file = client_open(argv[1], O_RDONLY); 325 326 if (file == NULL) 327 { 328 printf("Could not read file: %s\n", argv[1]); 329 return 1; 330 } 331 332 /* Obtain metadata from the file. */ 333 334 char buf[4096]; 335 offset_t nread; 336 337 nread = client_read(file, buf, EI_NIDENT); 338 if ((nread < EI_NIDENT) || memcmp(buf, "\x7f" "ELF", 4)) 339 { 340 printf("Not an ELF payload: %s\n", argv[1]); 341 return 1; 342 } 343 344 Payload *payload = get_payload(buf); 345 346 if (payload == NULL) 347 { 348 printf("Unrecognised object size.\n"); 349 return 1; 350 } 351 352 client_seek(file, 0, SEEK_SET); 353 nread = client_read(file, buf, payload->header_extent()); 354 355 if (nread < payload->header_extent()) 356 { 357 printf("Header incomplete.\n"); 358 return 1; 359 } 360 361 printf("Program start: %lx\n", payload->entry_point()); 362 363 client_seek(file, 0, SEEK_SET); 364 nread = client_read(file, buf, payload->program_header_extent()); 365 366 if (nread < payload->program_header_extent()) 367 { 368 printf("Program headers incomplete.\n"); 369 return 1; 370 } 371 372 /* Make appropriate segments, although program segments could be made 373 interoperable with these. */ 374 375 Segment *segments[payload->segments() + 1]; 376 377 for (unsigned int i = 0; i < payload->segments(); i++) 378 { 379 ProgramSegment *ps = payload->segment(i); 380 381 printf("Segment(0x%lx, 0x%lx, 0x%x, 0x%lx, 0x%lx): %s\n", 382 ps->region_address(), 383 ps->region_size(), 384 ps->region_flags(), 385 ps->file_offset(), 386 ps->file_contents(), 387 ps->loadable() ? "loadable" : "other"); 388 389 if (ps->loadable()) 390 segments[i] = ps->segment(); 391 else 392 segments[i] = NULL; 393 } 394 395 /* Copy the payload regions to new dataspaces. */ 396 397 address_t program_start = payload->entry_point(); 398 offset_t initial_stack_size = 16 * L4_PAGESIZE; 399 400 Segment stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); 401 402 segments[payload->segments()] = &stack; 403 404 for (unsigned int i = 0; i < payload->segments() + 1; i++) 405 { 406 Segment *segment = segments[i]; 407 408 if (segment == NULL) 409 continue; 410 411 if (segment->file_contents()) 412 { 413 flags_t flags; 414 415 if ((segment->region_flags() & L4RE_RM_F_RW) == L4RE_RM_F_RW) 416 flags = O_RDWR; 417 else if (segment->region_flags() & L4RE_RM_F_W) 418 flags = O_WRONLY; 419 else 420 flags = O_RDONLY; 421 422 file_t *file = client_open(argv[1], flags); 423 424 if (file == NULL) 425 { 426 printf("Could not open file for segment.\n"); 427 return 1; 428 } 429 430 err = segment->fill(file); 431 432 if (err) 433 { 434 printf("Could not fill segment from file.\n"); 435 return 1; 436 } 437 } 438 else 439 { 440 err = segment->allocate(); 441 442 if (err) 443 { 444 printf("Could not allocate segment.\n"); 445 return 1; 446 } 447 } 448 } 449 450 /* Start the pager. */ 451 452 ipc_server_config_type config; 453 pthread_t pager_thread; 454 pthread_attr_t attr; 455 456 pthread_attr_init(&attr); 457 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 458 459 init_pager(&config); 460 461 for (unsigned int i = 0; i < payload->segments() + 1; i++) 462 { 463 Segment *segment = segments[i]; 464 465 if (segment == NULL) 466 continue; 467 468 exec_pager.add(segment->region()); 469 } 470 471 err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); 472 473 if (err) 474 { 475 printf("Could not start pager thread.\n"); 476 return 1; 477 } 478 479 err = start_pager(&config, pager_thread); 480 481 if (err) 482 { 483 printf("Could not start pager.\n"); 484 return 1; 485 } 486 487 /* Configure the environment for the task, specifying the pager (and exception 488 handler plus region mapper). */ 489 490 Process process; 491 492 err = process.configure(config.server); 493 494 if (err) 495 { 496 printf("Could not configure task.\n"); 497 return 1; 498 } 499 500 /* Populate a thread stack with argument and environment details. */ 501 502 Stack st(stack); 503 504 /* NOTE: Environment vector is currently not defined. */ 505 506 char *envp[] = {NULL}; 507 508 st.populate(argc - 1, argv + 1, envp); 509 510 /* Start the new thread in the given stack. */ 511 512 printf("Run thread...\n"); 513 514 err = process.thread_start(program_start, st); 515 516 if (err) 517 { 518 printf("Could not run thread.\n"); 519 return 1; 520 } 521 522 printf("Finished.\n"); 523 while (1); 524 525 return 0; 526 } 527 528 /* vim: tabstop=2 expandtab shiftwidth=2 529 */