blob: b8593e556c55a59170205cf40fba4364d79e2775 [file] [log] [blame] [edit]
/* Copyright (c) 2017 Google Inc.
* See LICENSE for details.
*
* Memory, paging, e820, bootparams and other helpers */
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <ros/arch/mmu.h>
#include <vmm/linux_bootparam.h>
#include <vmm/vmm.h>
#include <err.h>
#define ALIGNED(p, a) (!(((uintptr_t)(p)) & ((a)-1)))
static char *entrynames[] = {
[E820_RAM] "E820_RAM",
[E820_RESERVED] "E820_RESERVED",
[E820_ACPI] "E820_ACPI",
[E820_NVS] "E820_NVS",
[E820_UNUSABLE] "E820_UNUSABLE",
};
static void dumpe820(struct e820entry *e, int nr)
{
for (int i = 0; i < nr; i++) {
fprintf(stderr, "%d:%p %p %p %s\n",
i, e[i].addr, e[i].size, e[i].type,
entrynames[e[i].type]);
}
}
// e820map creates an e820 map in the bootparams struct. If we've
// gotten here, then memsize and memstart are valid. It returns
// pointer to the first page after the map for our bump allocator. We
// assume the ranges passed in are validated already.
void *init_e820map(struct boot_params *bp,
unsigned long long memstart,
unsigned long long memsize)
{
unsigned long long lowmem = 0;
// Everything in Linux at this level is PGSIZE.
memset(bp, 0, PGSIZE);
bp->e820_entries = 0;
// The first page is always reserved.
bp->e820_map[bp->e820_entries].addr = 0;
bp->e820_map[bp->e820_entries].size = PGSIZE;
bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
/* Give it just a tiny bit of memory -- 60k -- at low memory. */
bp->e820_map[bp->e820_entries].addr = PGSIZE;
bp->e820_map[bp->e820_entries].size = LOW64K - PGSIZE;
bp->e820_map[bp->e820_entries++].type = E820_RAM;
// All other memory from 64k to memstart is reserved.
bp->e820_map[bp->e820_entries].addr = LOW64K;
bp->e820_map[bp->e820_entries].size = memstart - LOW64K;
bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
// If memory starts below RESERVED, then add an entry for memstart to
// the smaller of RESERVED or memsize.
if (memstart < RESERVED) {
bp->e820_map[bp->e820_entries].addr = memstart;
if (memstart + memsize > RESERVED)
bp->e820_map[bp->e820_entries].size = RESERVED - memstart;
else
bp->e820_map[bp->e820_entries].size = memsize;
lowmem = bp->e820_map[bp->e820_entries].size;
bp->e820_map[bp->e820_entries++].type = E820_RAM;
}
bp->e820_map[bp->e820_entries].addr = RESERVED;
bp->e820_map[bp->e820_entries].size = RESERVEDSIZE;
bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
if ((memstart + memsize) > RESERVED) {
bp->e820_map[bp->e820_entries].addr = MAX(memstart, _4GiB);
bp->e820_map[bp->e820_entries].size = memsize - lowmem;
bp->e820_map[bp->e820_entries++].type = E820_RAM;
}
dumpe820(bp->e820_map, bp->e820_entries);
return (void *)bp + PGSIZE;
}
/* checkmemaligned verifies alignment attributes of your memory space.
* It terminates your process with extreme prejudice if they are
* incorrect in some way. */
void checkmemaligned(uintptr_t memstart, size_t memsize)
{
if (!ALIGNED(memstart, PML1_REACH))
errx(1, "memstart (%#x) wrong: must be aligned to %#x",
memstart, PML1_REACH);
if (!ALIGNED(memsize, PML1_REACH))
errx(1, "memsize (%#x) wrong: must be aligned to %#x",
memsize, PML1_REACH);
}
// memory allocates memory for the VM. It's a complicated mess because of the
// break for APIC and other things. We just go ahead and leave the region from
// RESERVED to _4GiB for that. The memory is either split, all low, or all
// high. This code is designed for a kernel. Dune-style code does not need it
// as it does not have the RESERVED restrictions. Dune-style code can use this,
// however, by setting memstart to 4 GiB. This code can be called multiple
// times with more ranges. It does not check for overlaps.
void mmap_memory(struct virtual_machine *vm, uintptr_t memstart, size_t memsize)
{
void *r1, *r2;
unsigned long r1size = memsize;
// Let's do some minimal validation, so we don't drive
// people crazy.
checkmemaligned(memstart, memsize);
if ((memstart >= RESERVED) && (memstart < _4GiB))
errx(1, "memstart (%#x) wrong: must be < %#x or >= %#x\n",
memstart, RESERVED, _4GiB);
if (memstart < MinMemory)
errx(1, "memstart (%#x) wrong: must be > %#x\n",
memstart, MinMemory);
// Note: this test covers the split case as well as the
// 'all above 4G' case.
if ((memstart + memsize) > RESERVED) {
unsigned long long r2start = MAX(memstart, _4GiB);
r1size = memstart < RESERVED ? RESERVED - memstart : 0;
r2 = mmap((void *)r2start, memsize - r1size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (r2 != (void *)r2start) {
fprintf(stderr,
"High region: Could not mmap 0x%lx bytes at 0x%lx\n",
memsize, r2start);
exit(1);
}
if (memstart >= _4GiB)
goto done;
}
r1 = mmap((void *)memstart, r1size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (r1 != (void *)memstart) {
fprintf(stderr, "Low region: Could not mmap 0x%lx bytes at 0x%lx\n",
memsize, memstart);
exit(1);
}
done:
if ((vm->minphys == 0) || (vm->minphys > memstart))
vm->minphys = memstart;
if (vm->maxphys < memstart + memsize - 1)
vm->maxphys = memstart + memsize - 1;
}