/* * Copyright (c) 2015-2019 Contributors as noted in the AUTHORS file * * This file is part of Solo5, a sandboxed execution environment. * * Permission to use, copy, modify, and/or distribute this software * for any purpose with or without fee is hereby granted, provided * that the above copyright notice and this permission notice appear * in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * hvt_cpu_x86_64.c: Common architecture-dependent code supporting x86_64 * backend implementations. */ #include #include #include #include #include #include "hvt_cpu_x86_64.h" void hvt_x86_mem_size(size_t *mem_size) { size_t mem; mem = (*mem_size / X86_GUEST_PAGE_SIZE) * X86_GUEST_PAGE_SIZE; assert (mem <= *mem_size); if (mem == 0) mem = X86_GUEST_PAGE_SIZE; if (mem != *mem_size) warnx("adjusting memory to %zu bytes", mem); if (mem > X86_GUEST_PAGE_SIZE * 512) errx(1, "guest memory size %zu bytes exceeds the max size %u bytes", mem, X86_GUEST_PAGE_SIZE * 512); *mem_size = mem; } void hvt_x86_setup_pagetables(uint8_t *mem, size_t mem_size) { uint64_t *pml4 = (uint64_t *)(mem + X86_PML4_BASE); uint64_t *pdpte = (uint64_t *)(mem + X86_PDPTE_BASE); uint64_t *pde = (uint64_t *)(mem + X86_PDE_BASE); uint64_t *pt0e = (uint64_t *)(mem + X86_PT0E_BASE); uint64_t paddr; /* * For simplicity we currently use 2MB pages and only a single * PML4/PDPTE/PDE. Sanity check that the guest size is a multiple of the * page size and will fit in a single PDE (512 entries). Additionally, * check that the guest size is at least 2MB. */ assert((mem_size & (X86_GUEST_PAGE_SIZE - 1)) == 0); assert(mem_size <= (X86_GUEST_PAGE_SIZE * 512)); assert(mem_size >= X86_GUEST_PAGE_SIZE); memset(pml4, 0, X86_PML4_SIZE); memset(pdpte, 0, X86_PDPTE_SIZE); memset(pde, 0, X86_PDE_SIZE); memset(pt0e, 0, X86_PTE_SIZE); *pml4 = X86_PDPTE_BASE | (X86_PDPT_P | X86_PDPT_RW); *pdpte = X86_PDE_BASE | (X86_PDPT_P | X86_PDPT_RW); /* * PDE[0] is special: use 4kB pages and PT0 for the first 2MB of address * space. */ *pde = X86_PT0E_BASE | (X86_PDPT_P | X86_PDPT_RW); pde++; for (paddr = 0; paddr < X86_GUEST_PAGE_SIZE; paddr += 0x1000, pt0e++) { /* * Leave all pages below X86_PT0_MAP_START not-present in the guest. * * This includes the zero page, boot GDT and the guest's page tables * themselves. */ if (paddr < X86_PT0_MAP_START) continue; /* * Map the remainder of the pages below X86_GUEST_MIN_BASE as read-only; * these are used for input from hvt to the guest only, with the rest * reserved for future use. */ if (paddr < X86_GUEST_MIN_BASE) *pt0e = paddr | X86_PDPT_P; else *pt0e = paddr | (X86_PDPT_P | X86_PDPT_RW); } assert(paddr == X86_GUEST_PAGE_SIZE); for (; paddr < mem_size; paddr += X86_GUEST_PAGE_SIZE, pde++) *pde = paddr | (X86_PDPT_P | X86_PDPT_RW | X86_PDPT_PS); } static struct x86_gdt_desc sreg_to_desc(const struct x86_sreg *sreg) { /* * Translate our shadow register representation to a GDT entry. * * XXX: This function will only work correctly for certain descriptor types * add assertions/tests to verify inputs. * * NOTE: Technically to boot the VCPU we only need the shadow registers * set, everything will work fine with no GDT until the guest attempts to * load a segment register. */ uint32_t limit = (sreg->g) ? ((sreg->limit & 0xfffff000) >> 12) : sreg->limit; struct x86_gdt_desc desc = { .base_lo = sreg->base & 0xffffff, .base_hi = (sreg->base & 0xff000000) >> 24, .limit_lo = limit & 0xffff, .limit_hi = (limit & 0xf0000) >> 16, .type = sreg->type, .s = sreg->s, .dpl = sreg->dpl, .p = sreg->p, .avl = sreg->avl, .l = sreg->l, .db = sreg->db, .g = sreg->g }; return desc; } void hvt_x86_setup_gdt(uint8_t *mem) { struct x86_gdt_desc *gdt = (struct x86_gdt_desc *)(mem + X86_GDT_BASE); struct x86_gdt_desc null = { 0 }; gdt[X86_GDT_NULL] = null; gdt[X86_GDT_CODE] = sreg_to_desc(&hvt_x86_sreg_code); gdt[X86_GDT_DATA] = sreg_to_desc(&hvt_x86_sreg_data); }