| /* Copyright (c) 2017 Google Inc. |
| * See LICENSE for details. |
| * |
| * Set up paging, using the minphys and maxphys in the vm struct. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/mman.h> |
| #include <ros/arch/mmu.h> |
| #include <vmm/vmm.h> |
| #include <vmm/util.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <errno.h> |
| #include <parlib/uthread.h> |
| #include <parlib/arch/arch.h> |
| |
| static bool debug; |
| |
| struct ptp { |
| uintptr_t pte[NPTENTRIES]; |
| }; |
| |
| #define PAGE_RESOLUTION PML3_PTE_REACH |
| |
| /* We put the page tables after 4Gb, where it exactly is doesn't matter as long |
| * as it's accessible by the guest. */ |
| #define PAGE_TABLE_ROOT_START 0x100000000 |
| |
| static void check_jumbo_pages(void *arg) |
| { |
| uint32_t edx; |
| |
| parlib_cpuid(0x80000001, 0x0, NULL, NULL, NULL, &edx); |
| if (!(edx & (1 << 26))) |
| panic("1 GB Jumbo Pages are not supported on this hardware!"); |
| } |
| |
| /* |
| * This function assumes that after the p512 page table, there is memory mapped |
| * (though not necessarily populated) for each PML3 page table. This assumes |
| * a total of 2M + 4K memory mapped. PML3 table n is located at 4K*(n+1) from |
| * the start of the p512 table. |
| * This function does a 1:1 mapping of va to pa. vm->root must be set |
| * */ |
| void add_pte_entries(struct virtual_machine *vm, uintptr_t start, uintptr_t end) |
| { |
| struct ptp *p512; |
| uintptr_t cur_page, aligned_start, aligned_end, pml4, pml3; |
| static parlib_once_t once = PARLIB_ONCE_INIT; |
| |
| /* We check once if we can use 1Gb pages and die if we can't. */ |
| parlib_run_once(&once, check_jumbo_pages, NULL); |
| |
| uth_mutex_lock(&vm->mtx); |
| p512 = vm->root; |
| if (!p512) |
| panic("vm->root page table pointer was not set!"); |
| |
| /* We align the start down and the end up to make sure we cover the full |
| * area. */ |
| aligned_start = ALIGN_DOWN(start, PAGE_RESOLUTION); |
| aligned_end = ALIGN(end, PAGE_RESOLUTION); |
| |
| cur_page = aligned_start; |
| /* We always do end-1 because end from /proc/self/maps is not inclusive |
| * */ |
| for (pml4 = PML4(start); pml4 <= PML4(end - 1); pml4++) { |
| struct ptp *p1 = p512 + pml4 + 1; |
| |
| /* Create the PML4 entry. Rather than check, I just overwrite |
| * it. */ |
| p512->pte[pml4] = (uintptr_t) p1 | PTE_KERN_RW; |
| |
| for (pml3 = PML3(cur_page); |
| pml3 < NPTENTRIES && cur_page < aligned_end; |
| pml3++, cur_page += PML3_PTE_REACH) { |
| /* Create the PML3 entry. */ |
| p1->pte[pml3] = cur_page | PTE_KERN_RW | PTE_PS; |
| } |
| } |
| uth_mutex_unlock(&vm->mtx); |
| } |
| |
| /* This function sets up the default page tables for the guest. It parses |
| * /proc/self/maps to figure out what pages are mapped for the uthread, and |
| * sets up a 1:1 mapping for the vm guest. This function can be called |
| * multiple times after startup to update the page tables, though regular |
| * vmms should call add_pte_entries if they mmap something for the guest after |
| * calling setup_paging to avoid having to parse /proc/self/maps again. */ |
| void setup_paging(struct virtual_machine *vm) |
| { |
| FILE *maps; |
| char *line = NULL; |
| size_t line_sz; |
| char *strtok_save; |
| char *p; |
| uintptr_t first, second; |
| |
| /* How many page table pages do we need? |
| * If we create 1G PTEs for the whole space, it just takes 2M + 4k worth |
| * of memory. Perhaps we should just identity map the whole space |
| * upfront. Right now we don't MAP_POPULATE because we don't expect all |
| * the PTEs to be used. */ |
| if (!vm->root) |
| vm->root = mmap((void *)PAGE_TABLE_ROOT_START, 0x201000, |
| PROT_READ | PROT_WRITE, |
| MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
| |
| if (vm->root == MAP_FAILED || (uintptr_t)vm->root >= BRK_START) |
| panic("page table page alloc"); |
| |
| /* We parse /proc/self/maps to figure out the currently mapped memory. |
| * This way all the memory that's available to the HR3 process is also |
| * reflected in the page tables. Our smallest PTEs here are 1Gb so there |
| * may be memory locations described in the page tables that are not |
| * mapped though. /proc/self/maps parsing code courtesy of Barret. */ |
| maps = fopen("/proc/self/maps", "r"); |
| if (!maps) |
| panic("unable to open /proc/self/maps"); |
| |
| /* returns -1 on error or EOF. */ |
| while (getline(&line, &line_sz, maps) >= 0) { |
| if (debug) |
| fprintf(stderr, "Got line %s", line); |
| |
| p = strchr(line, ' '); |
| /* No space, probably an error */ |
| if (!p) |
| continue; |
| *p = '\0'; |
| p = strtok_r(line, "-", &strtok_save); |
| /* No first element! */ |
| if (!p) |
| continue; |
| first = strtoul(p, NULL, 16); |
| p = strtok_r(NULL, "-", &strtok_save); |
| /* No second element! */ |
| if (!p) |
| continue; |
| second = strtoul(p, NULL, 16); |
| |
| if (debug) |
| printf("first %p, second %p\n\n", first, second); |
| |
| add_pte_entries(vm, first, second); |
| |
| } |
| free(line); |
| fclose(maps); |
| } |