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