blob: 7e6707dbd105ec186575a62eb3dc85fc71d868b2 [file] [log] [blame]
/* 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);
}