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/sys/factory.h> 25 #include <l4/sys/task.h> 26 #include <l4/sys/thread.h> 27 #include <l4/util/elf.h> 28 #include <l4/util/util.h> 29 30 #include <fsclient/client.h> 31 #include <ipc/cap_alloc.h> 32 #include <ipc/mem_ipc.h> 33 #include <ipc/server.h> 34 #include <mem/memory_utils.h> 35 #include <systypes/fcntl.h> 36 37 #include <map> 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 43 #include <pthread-l4.h> 44 #include <pthread.h> 45 46 #include "pager_object_interface.h" 47 #include "pager_object_server.h" 48 49 50 51 /* Region data structures. */ 52 53 class Region 54 { 55 public: 56 l4_addr_t start; 57 unsigned int log2size; 58 l4_umword_t flags; 59 l4_addr_t map_start; 60 61 explicit Region() 62 : start(0), log2size(0), flags(0), map_start(0) 63 { 64 } 65 66 explicit Region(l4_addr_t start, unsigned int log2size, l4_umword_t flags, 67 l4_addr_t map_start) 68 : start(start), log2size(log2size), flags(flags), map_start(map_start) 69 { 70 } 71 }; 72 73 typedef std::map<l4_addr_t, Region> Regions; 74 75 76 77 /* A simple system pager also acting as a region mapper. */ 78 79 class ExecPager : public PagerObject 80 { 81 protected: 82 Regions _regions; 83 84 public: 85 virtual void add(Region region) 86 { 87 _regions[region.map_start] = region; 88 } 89 90 /* Notification methods. */ 91 92 virtual long exception(l4_exc_regs_t regs, 93 l4_snd_fpage_t *region); 94 95 virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, 96 l4_snd_fpage_t *region); 97 98 /* Region manager/mapper methods. */ 99 100 virtual long attach(address_t *start, offset_t size, map_flags_t flags, 101 l4_cap_idx_t ds, address_t offset, unsigned char align); 102 103 }; 104 105 /* Handle a general exception. */ 106 107 long ExecPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) 108 { 109 (void) region; 110 111 printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); 112 113 printf("r15 = %lx\n", regs.r15); 114 printf("r14 = %lx\n", regs.r14); 115 printf("r13 = %lx\n", regs.r13); 116 printf("r12 = %lx\n", regs.r12); 117 printf("r11 = %lx\n", regs.r11); 118 printf("r10 = %lx\n", regs.r10); 119 printf("r9 = %lx\n", regs.r9); 120 printf("r8 = %lx\n", regs.r8); 121 printf("rdi = %lx\n", regs.rdi); 122 printf("rsi = %lx\n", regs.rsi); 123 printf("rbp = %lx\n", regs.rbp); 124 printf("pfa = %lx\n", regs.pfa); 125 printf("rbx = %lx\n", regs.rbx); 126 printf("rdx = %lx\n", regs.rdx); 127 printf("rcx = %lx\n", regs.rcx); 128 printf("rax = %lx\n", regs.rax); 129 printf("trapno = %lx\n", regs.trapno); 130 printf("err = %lx\n", regs.err); 131 printf("ip = %lx\n", regs.ip); 132 printf("flags = %lx\n", regs.flags); 133 printf("sp = %lx\n", regs.sp); 134 printf("ss = %lx\n", regs.ss); 135 printf("fs_base = %lx\n", regs.fs_base); 136 printf("gs_base = %lx\n", regs.gs_base); 137 138 return L4_EOK; 139 } 140 141 /* Handle a page fault using any configured regions. */ 142 143 long ExecPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) 144 { 145 l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; 146 147 #if 0 148 printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); 149 #endif 150 151 Regions::iterator it = _regions.upper_bound(addr); 152 153 if (it != _regions.begin()) 154 it--; 155 else 156 { 157 printf("not mapped!\n"); 158 return -L4_ENOMEM; 159 } 160 161 Region &r = it->second; 162 163 if ((addr >= r.map_start) && (addr < r.map_start + (1UL << r.log2size))) 164 { 165 l4_addr_t page_addr = trunc(addr, L4_PAGESIZE); 166 167 region->fpage = l4_fpage(r.start + (page_addr - r.map_start), L4_PAGESHIFT, r.flags); 168 region->snd_base = page_addr; 169 170 #if 0 171 printf("%lx...%lx from %lx...%lx size %d rights %x\n", 172 r.map_start, region->snd_base, 173 r.start, l4_fpage_memaddr(region->fpage), 174 l4_fpage_size(region->fpage), 175 l4_fpage_rights(region->fpage)); 176 printf("%lx -> ", addr); 177 178 for (unsigned int i = 0; i < sizeof(l4_umword_t); i++) 179 printf("%02x", *((unsigned char *)(r.start + (addr - r.map_start) + i))); 180 181 printf("\n"); 182 #endif 183 184 return L4_EOK; 185 } 186 187 #if 0 188 printf("not mapped!\n"); 189 #endif 190 191 return -L4_ENOMEM; 192 } 193 194 /* Attach a region for provision when page faults occur. This is required in 195 the initialisation of a program by the C library which requires a region 196 mapper. */ 197 198 long ExecPager::attach(address_t *start, offset_t size, map_flags_t flags, 199 l4_cap_idx_t ds, address_t offset, unsigned char align) 200 { 201 #if 0 202 printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); 203 #endif 204 205 if (align < L4_PAGESHIFT) 206 align = L4_PAGESHIFT; 207 208 offset_t increment = 1UL << align; 209 offset_t region_size = round(size, increment); 210 211 /* Either attempt to find an address for the specified region, starting from 212 any indicated address. */ 213 214 if (flags & L4RE_RM_F_SEARCH_ADDR) 215 { 216 address_t region_start = trunc(*start, increment); 217 Regions::iterator it = _regions.upper_bound(*start); 218 219 if (!region_start) 220 region_start += increment; 221 222 #if 0 223 printf("-> search from %lx -> %lx...\n", *start, region_start); 224 #endif 225 226 /* Before last known region. */ 227 228 while (it != _regions.end()) 229 { 230 Regions::iterator next = it; 231 Region &r = it->second; 232 address_t start_limit; 233 address_t end_limit = r.map_start; 234 235 /* Consider any preceding region. If no such region exists, choose an 236 address at the start of memory. */ 237 238 if (it == _regions.begin()) 239 start_limit = L4_PAGESIZE; 240 else 241 { 242 it--; 243 Region &pr = it->second; 244 start_limit = pr.map_start + (1UL << pr.log2size); 245 it = next; 246 } 247 248 /* Test against the limits. */ 249 250 if (region_start < start_limit) 251 region_start = round(start_limit, increment); 252 253 /* Investigate subsequent regions if not enough space exists between the 254 preceding region (or start of memory) and the current region. */ 255 256 if ((region_start + region_size) > end_limit) 257 { 258 it++; 259 if (it == _regions.end()) 260 return -L4_ENOMEM; 261 } 262 else 263 break; 264 } 265 266 /* Attach the provided dataspace. 267 NOTE: This is only done in this implementation to support the paging 268 mechanism. In a region mapper residing within the actual task, the 269 dataspace's map operation would be invoked to obtain mappings. */ 270 271 l4_addr_t ds_start; 272 273 long err = ipc_attach_dataspace(ds, size, (void **) &ds_start); 274 275 if (err) 276 return err; 277 278 l4_touch_rw((const void *) ds_start, size); 279 280 #if 0 281 printf("-> added region for %lx size %ld (%d)\n", region_start, region_size, page_order(region_size)); 282 #endif 283 284 add(Region(ds_start, page_order(region_size), flags & L4RE_DS_F_RIGHTS_MASK, region_start)); 285 286 *start = region_start; 287 return L4_EOK; 288 } 289 290 /* Or attempt to add the specified region at a specific address. */ 291 292 else 293 { 294 // NOTE: To be implemented. 295 296 return -L4_ENOMEM; 297 } 298 } 299 300 301 302 static ExecPager exec_pager; 303 304 static void init_pager(ipc_server_config_type *config) 305 { 306 ipc_server_init_config(config); 307 308 config->expected_items = PagerObject_expected_items; 309 config->handler = (ipc_server_handler_type) handle_PagerObject; 310 config->handler_obj = static_cast<PagerObject *>(&exec_pager); 311 } 312 313 static long start_pager(ipc_server_config_type *config, pthread_t thread) 314 { 315 config->config_thread = 1; 316 config->thread = pthread_l4_cap(thread); 317 318 printf("Starting pager thread...\n"); 319 return ipc_server_start_config(config); 320 } 321 322 323 324 /* UTCB properties. 325 See: moe/server/src/loader.cc */ 326 327 enum ipc_exec_utcb 328 { 329 Default_max_threads = 16, 330 #ifdef ARCH_mips 331 Utcb_area_start = 0x73000000, 332 #else 333 Utcb_area_start = 0xb3000000, 334 #endif 335 }; 336 337 338 339 /* Capability mapping definitions for the new task. */ 340 341 struct mapped_cap 342 { 343 l4_cap_idx_t cap; 344 unsigned char rights; 345 l4_umword_t spot; 346 }; 347 348 static long map_capabilities(l4_cap_idx_t task, struct mapped_cap mapped_caps[]) 349 { 350 long err = L4_EOK; 351 int i = 0; 352 353 while (l4_is_valid_cap(mapped_caps[i].cap) && !err) 354 { 355 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 356 l4_obj_fpage(mapped_caps[i].cap, 0, mapped_caps[i].rights), 357 l4_map_obj_control(mapped_caps[i].spot, L4_MAP_ITEM_MAP))); 358 i++; 359 } 360 361 return err; 362 } 363 364 365 366 int main(int argc, char *argv[]) 367 { 368 long err; 369 370 if (argc < 2) 371 { 372 printf("Need a program to run.\n"); 373 return 1; 374 } 375 376 /* Allocate capabilities for the task and thread. */ 377 378 l4_cap_idx_t caps[2]; 379 380 err = ipc_cap_alloc_many(caps, 2); 381 382 if (err) 383 { 384 printf("Could not allocate capabilities.\n"); 385 return 1; 386 } 387 388 l4_cap_idx_t &task = caps[0]; 389 l4_cap_idx_t &thread = caps[1]; 390 391 /* Obtain the payload as a dataspace. */ 392 393 file_t *file = client_open(argv[1], O_RDONLY); 394 395 if (file == NULL) 396 { 397 printf("Could not read file: %s\n", argv[1]); 398 return 1; 399 } 400 401 /* Copy the payload regions to new dataspaces. 402 NOTE: This should be directed by the ELF metadata. */ 403 404 char *program_buf; 405 offset_t nread; 406 offset_t program_region_contents = 0x28466; 407 offset_t program_region_size = round(program_region_contents, L4_PAGESIZE); 408 l4re_ds_t program_region_ds; 409 410 err = ipc_allocate_align(program_region_size, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RWX, 411 L4_PAGESHIFT, (void **) &program_buf, &program_region_ds); 412 413 if (err) 414 { 415 printf("Could not reserve program memory.\n"); 416 return 1; 417 } 418 419 nread = client_read(file, program_buf, program_region_contents); 420 421 printf("Read %ld from file into %p.\n", nread, program_buf); 422 423 if (memcmp(program_buf + 0xae3, "\x31\xed", 2)) 424 { 425 printf("Did not find expected instructions at start.\n"); 426 return 1; 427 } 428 429 offset_t data_region_start = 0x102a360; 430 offset_t data_region_size = round(0x8068, L4_PAGESIZE); 431 offset_t data_region_base = trunc(data_region_start, L4_PAGESIZE); 432 offset_t data_region_offset = data_region_start - data_region_base; 433 434 char *data_buf; 435 offset_t data_region_contents = 0x2058; 436 l4re_ds_t data_region_ds; 437 438 err = ipc_allocate_align(data_region_size, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW, 439 L4_PAGESHIFT, (void **) &data_buf, &data_region_ds); 440 441 if (err) 442 { 443 printf("Could not reserve data memory.\n"); 444 return 1; 445 } 446 447 memset(data_buf, 0, data_region_size); 448 449 client_seek(file, 0x29360, SEEK_SET); 450 nread = client_read(file, data_buf + data_region_offset, data_region_contents); 451 452 printf("Read %ld from file into %p in region %p with size %ld for %lx.\n", 453 nread, data_buf + data_region_offset, data_buf, data_region_size, data_region_base); 454 455 /* UTCB location and size. */ 456 457 l4_addr_t utcb_start = Utcb_area_start; 458 int utcb_log2size = page_order(Default_max_threads * L4_UTCB_OFFSET); 459 460 /* Round up to at least one page. */ 461 462 if (utcb_log2size < L4_PAGESHIFT) 463 utcb_log2size = L4_PAGESHIFT; 464 465 l4_fpage_t utcb_fpage = l4_fpage(utcb_start, utcb_log2size, 0); 466 467 /* KIP allocation. */ 468 469 l4_addr_t kip_start = (l4_addr_t) l4re_kip(); 470 471 printf("KIP at %lx.\n", kip_start); 472 473 /* Stack allocation. */ 474 475 l4_addr_t stack_buf; 476 offset_t stack_size = 16 * L4_PAGESIZE; 477 l4_addr_t stack_region_base = 0x8000000 - stack_size; 478 l4re_ds_t stack_ds; 479 480 err = ipc_allocate_align(stack_size, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW, 481 L4_PAGESHIFT, (void **) &stack_buf, &stack_ds); 482 483 if (err) 484 { 485 printf("Could not reserve stack.\n"); 486 return 1; 487 } 488 489 /* Populate stack with additional capabilities. */ 490 491 l4re_env_cap_entry_t *stack_env_cap = (l4re_env_cap_entry_t *) (stack_buf + stack_size); 492 493 /* Special invalid/terminating environment capability entry. */ 494 495 *(--stack_env_cap) = l4re_env_cap_entry_t(); 496 497 printf("Stack region end: %p\n", stack_env_cap); 498 499 l4_addr_t caps_start = (l4_addr_t) stack_env_cap; 500 l4_umword_t *stack_element = (l4_umword_t *) stack_env_cap; 501 502 /* Loader flags, debugging flags, and the KIP capability index. 503 See: generate_l4aux in Remote_app_model */ 504 505 *(--stack_element) = 0; 506 *(--stack_element) = 0; 507 *(--stack_element) = 0x14 << L4_CAP_SHIFT; 508 509 printf("Stack L4 aux elements: %p / %lx\n", stack_element, ((l4_addr_t) stack_element - stack_buf) + stack_region_base); 510 511 /* Populate stack with standard capabilities. */ 512 513 l4re_env_t *env = (l4re_env_t *) stack_element; 514 515 env--; 516 env->factory = L4_BASE_FACTORY_CAP; 517 env->main_thread = L4_BASE_THREAD_CAP; 518 env->log = L4_BASE_LOG_CAP; 519 env->scheduler = L4_BASE_SCHEDULER_CAP; 520 env->rm = 0x11 << L4_CAP_SHIFT; 521 env->mem_alloc = 0x12 << L4_CAP_SHIFT; 522 env->first_free_cap = 0x15; 523 env->caps = (l4re_env_cap_entry_t *) (caps_start - stack_buf) + stack_region_base; 524 env->utcb_area = utcb_fpage; 525 env->first_free_utcb = utcb_start + L4_UTCB_OFFSET; 526 527 /* Populate stack with AUXV and environment pointer. */ 528 529 stack_element = (l4_umword_t *) env; 530 531 printf("Stack L4 env elements: %p / %lx\n", stack_element, ((l4_addr_t) stack_element - stack_buf) + stack_region_base); 532 533 /* AUXV NULL. */ 534 535 *(--stack_element) = 0; 536 *(--stack_element) = 0; 537 538 /* L4Re global environment pointer. */ 539 540 *(--stack_element) = ((l4_addr_t) env - stack_buf) + stack_region_base; 541 *(--stack_element) = 0xf1; 542 543 /* Apparently required entries. */ 544 545 *(--stack_element) = L4_PAGESIZE; 546 *(--stack_element) = AT_PAGESZ; 547 548 *(--stack_element) = 0; 549 *(--stack_element) = AT_UID; 550 551 *(--stack_element) = 0; 552 *(--stack_element) = AT_EUID; 553 554 *(--stack_element) = 0; 555 *(--stack_element) = AT_GID; 556 557 *(--stack_element) = 0; 558 *(--stack_element) = AT_EGID; 559 560 char *stack_envp = (char *) stack_element; 561 562 printf("Stack L4 envp: %p / %lx\n", stack_element, ((l4_addr_t) stack_element - stack_buf) + stack_region_base); 563 564 /* Populate stack with argument values. */ 565 566 char *stack_arg = stack_envp; 567 568 stack_arg = (char *) trunc((offset_t) stack_arg - strlen(argv[1]) - 1, sizeof(l4_umword_t)); 569 570 memset(stack_arg, 0, stack_envp - stack_arg); 571 memcpy(stack_arg, argv[1], strlen(argv[1])); 572 573 printf("Stack L4 program argument: %p / %lx\n", stack_arg, ((l4_addr_t) stack_arg - stack_buf) + stack_region_base); 574 575 /* Populate stack with the environment pointer. */ 576 577 stack_element = (l4_umword_t *) stack_arg; 578 579 *(--stack_element) = (l4_umword_t) ((l4_addr_t) stack_envp - stack_buf) + stack_region_base; 580 581 /* Populate stack with argument pointers and count. */ 582 /* NOTE: Just one argument currently. */ 583 584 *(--stack_element) = (l4_umword_t) ((l4_addr_t) stack_arg - stack_buf) + stack_region_base; 585 *(--stack_element) = 1; 586 587 printf("Stack L4 start: %p / %lx\n", stack_element, ((l4_addr_t) stack_element - stack_buf) + stack_region_base); 588 printf("%ld %lx %lx\n", stack_element[0], stack_element[1], stack_element[2]); 589 590 l4_addr_t stack_start = ((l4_addr_t) stack_element - stack_buf) + stack_region_base; 591 592 /* Create a new task and thread. */ 593 594 err = l4_error(l4_factory_create_task(l4re_env()->factory, task, utcb_fpage)); 595 596 if (err) 597 { 598 printf("Could not create task.\n"); 599 return 1; 600 } 601 602 err = l4_error(l4_factory_create_thread(l4re_env()->factory, thread)); 603 604 if (err) 605 { 606 printf("Could not create thread.\n"); 607 return 1; 608 } 609 610 /* Start the pager. */ 611 612 ipc_server_config_type config; 613 pthread_t pager_thread; 614 pthread_attr_t attr; 615 616 pthread_attr_init(&attr); 617 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 618 619 init_pager(&config); 620 621 exec_pager.add(Region((l4_addr_t) program_buf, page_order(program_region_size), L4_FPAGE_RX, 0x1000000)); 622 exec_pager.add(Region((l4_addr_t) data_buf, page_order(data_region_size), L4_FPAGE_RW, data_region_base)); 623 exec_pager.add(Region((l4_addr_t) stack_buf, page_order(stack_size), L4_FPAGE_RW, stack_region_base)); 624 625 err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); 626 627 if (err) 628 { 629 printf("Could not start pager thread.\n"); 630 return 1; 631 } 632 633 err = start_pager(&config, pager_thread); 634 635 if (err) 636 { 637 printf("Could not start pager.\n"); 638 return 1; 639 } 640 641 /* Define capability mappings for the new task. */ 642 643 struct mapped_cap mapped_caps[] = { 644 {config.server, L4_CAP_FPAGE_RWS, 0x10 << L4_CAP_SHIFT}, 645 {config.server, L4_CAP_FPAGE_RWS, 0x11 << L4_CAP_SHIFT}, 646 {task, L4_CAP_FPAGE_RWS, L4_BASE_TASK_CAP}, 647 {thread, L4_CAP_FPAGE_RWS, L4_BASE_THREAD_CAP}, 648 {l4re_env()->factory, L4_CAP_FPAGE_RWS, L4_BASE_FACTORY_CAP}, 649 {l4re_env()->log, L4_CAP_FPAGE_RWS, L4_BASE_LOG_CAP}, 650 {l4re_env()->scheduler, L4_CAP_FPAGE_RWS, L4_BASE_SCHEDULER_CAP}, 651 {l4re_env()->mem_alloc, L4_CAP_FPAGE_RWS, 0x12 << L4_CAP_SHIFT}, 652 {L4_INVALID_CAP, 0, 0}, 653 }; 654 655 err = map_capabilities(task, mapped_caps); 656 657 if (err) 658 { 659 printf("Could not capabilities into task.\n"); 660 return 1; 661 } 662 663 /* Map the KIP into the task. */ 664 665 err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, 666 l4_fpage(kip_start, L4_PAGESHIFT, L4_FPAGE_RX), 667 kip_start)); 668 669 if (err) 670 { 671 printf("Could not map KIP into task.\n"); 672 return 1; 673 } 674 675 /* Configure the thread with the region manager acting as pager and exception 676 handler. The UTCB will be situated at an address supported by a dataspace 677 attached to the new task. */ 678 679 printf("Configure thread...\n"); 680 681 l4_thread_control_start(); 682 l4_thread_control_pager(0x10 << L4_CAP_SHIFT); 683 l4_thread_control_exc_handler(0x10 << L4_CAP_SHIFT); 684 l4_thread_control_bind((l4_utcb_t *) utcb_start, task); 685 err = l4_error(l4_thread_control_commit(thread)); 686 687 if (err) 688 { 689 printf("Could not configure thread.\n"); 690 return 1; 691 } 692 693 /* Start the new thread. */ 694 695 printf("Schedule thread...\n"); 696 697 printf("Stack at 0x%lx mapped to region at 0x%lx.\n", stack_start, stack_region_base); 698 699 err = l4_error(l4_thread_ex_regs(thread, 0x1000ae3, stack_start, 0)); 700 701 if (err) 702 { 703 printf("Could not set thread registers.\n"); 704 return 1; 705 } 706 707 printf("Run thread...\n"); 708 709 l4_sched_param_t sp = l4_sched_param(L4RE_MAIN_THREAD_PRIO); 710 711 err = l4_error(l4_scheduler_run_thread(l4re_env()->scheduler, thread, &sp)); 712 713 if (err) 714 { 715 printf("Could not run thread.\n"); 716 return 1; 717 } 718 719 printf("Finished.\n"); 720 while (1); 721 722 return 0; 723 } 724 725 /* vim: tabstop=2 expandtab shiftwidth=2 726 */