|  | /* Copyright (c) 2015 Google Inc. | 
|  | * Ron Minnich <rminnich@google.com> | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * VMM.h */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <ros/vmm.h> | 
|  | #include <vmm/sched.h> | 
|  | #include <vmm/linux_bootparam.h> | 
|  | #include <parlib/stdio.h> | 
|  | #include <libelf.h> | 
|  |  | 
|  | // We need to reserve an area of the low 4G for thinks like tables, APIC, and | 
|  | // so on. So far, 256 MiB has been more than enough, so ... | 
|  | #define MiB 0x100000ull | 
|  | #define MinMemory (16*MiB) | 
|  | #define GiB (0x40000000ULL) | 
|  | #define _4GiB (0x100000000ULL) | 
|  | // BIOS conventions from 1978 make it smart to reserve the low 64k | 
|  | #define LOW64K 65536 | 
|  | // The RESERVED area is for all the random junk like devices, ACPI, etc. | 
|  | // We just give it the top 1 GiB of the 32-bit address space, which | 
|  | // nicely translates to one GiB PTE. | 
|  | #define RESERVED 0xC0000000ULL | 
|  | #define RESERVEDSIZE (_4GiB - RESERVED) | 
|  | // Start the VM at 16 MiB, a standard number for 64 bit kernels on amd64 | 
|  | #define KERNSTART 0x1000000 | 
|  |  | 
|  | #define VM_PAGE_FAULT			14 | 
|  |  | 
|  | // APIC Guest Physical Address, a well known constant. | 
|  | #define APIC_GPA			0xfee00000ULL | 
|  |  | 
|  | /* The listing of VIRTIO MMIO devices. We currently only expect to have 2, | 
|  | * console and network. Only the console is fully implemented right now.*/ | 
|  | enum { | 
|  | VIRTIO_MMIO_CONSOLE_DEV, | 
|  | VIRTIO_MMIO_NETWORK_DEV, | 
|  | VIRTIO_MMIO_BLOCK_DEV, | 
|  |  | 
|  | /* This should always be the last entry. */ | 
|  | VIRTIO_MMIO_MAX_NUM_DEV, | 
|  | }; | 
|  |  | 
|  | /* Structure to encapsulate all of the bookkeeping for a VM. */ | 
|  | struct virtual_machine { | 
|  | /* Big mutext for pagetables and __gths/ nr_gpcs */ | 
|  | uth_mutex_t			mtx; | 
|  | struct guest_thread		**__gths; | 
|  | unsigned int			gth_array_elem; | 
|  | unsigned int			nr_gpcs; | 
|  | /* up_gpcs should not need synchronization. only the BSP should be | 
|  | * making startup vmcalls. For security's sake we might still want to | 
|  | * lock in the future. TODO(ganshun) | 
|  | * up_gpcs refers to the number of guest pcores that have been started | 
|  | * so far. */ | 
|  | unsigned int			up_gpcs; | 
|  |  | 
|  | /* TODO: put these in appropriate structures.  e.g., virtio things in | 
|  | * something related to virtio.  low4k in something related to the | 
|  | * guest's memory. */ | 
|  | uint8_t				*low4k; | 
|  | struct virtio_mmio_dev *virtio_mmio_devices[VIRTIO_MMIO_MAX_NUM_DEV]; | 
|  |  | 
|  | /* minimum and maximum physical memory addresses. When we set up the | 
|  | * initial default page tables we use this range. Note that even if the | 
|  | * "physical" memory has holes, we'll create PTEs for it. This seems | 
|  | * enough for now but we shall see. */ | 
|  | uintptr_t			minphys; | 
|  | uintptr_t			maxphys; | 
|  |  | 
|  | /* Default root pointer to use if one is not set in a guest thread. We | 
|  | * expect this to be the common case, where all guests share a page | 
|  | * table. It's not required however. setup_paging now updates this to | 
|  | * point to the initial set of page tables for the guest. */ | 
|  | void			*root; | 
|  |  | 
|  | /* Default value for whether guest threads halt on an exit. */ | 
|  | bool				halt_exit; | 
|  | /* Override for vmcall (vthreads) */ | 
|  | bool (*vmcall)(struct guest_thread *gth, struct vm_trapframe *); | 
|  | }; | 
|  |  | 
|  | struct elf_aux { | 
|  | unsigned long v[2]; | 
|  | }; | 
|  |  | 
|  | /* TODO: x86-specific */ | 
|  | #define VMM_MAX_INSN_SZ 15 | 
|  | typedef int (*emu_mem_access)(struct guest_thread *gth, uintptr_t gpa, | 
|  | unsigned long *regp, size_t size, | 
|  | bool store); | 
|  | int emulate_mem_insn(struct guest_thread *gth, uint8_t *insn, | 
|  | emu_mem_access access, int *advance); | 
|  | int io(struct guest_thread *vm_thread); | 
|  | void showstatus(FILE *f, struct guest_thread *vm_thread); | 
|  | int gva2gpa(struct guest_thread *vm_thread, uint64_t va, uint64_t *pa); | 
|  | int rippa(struct guest_thread *vm_thread, uint64_t *pa); | 
|  | int fetch_insn(struct guest_thread *gth, uint8_t *insn); | 
|  | int msrio(struct guest_thread *vm_thread, struct vmm_gpcore_init *gpci, | 
|  | uint32_t opcode); | 
|  | int do_ioapic(struct guest_thread *vm_thread, uint64_t gpa, uint64_t *regp, | 
|  | bool store); | 
|  | bool handle_vmexit(struct guest_thread *gth); | 
|  | int __apic_access(struct guest_thread *gth, uint64_t gpa, uint64_t *regp, | 
|  | size_t size, bool store); | 
|  | int vmm_interrupt_guest(struct virtual_machine *vm, unsigned int gpcoreid, | 
|  | unsigned int vector); | 
|  | uintptr_t load_elf(char *filename, uint64_t offset, uint64_t *highest, | 
|  | Elf64_Ehdr *ehdr_out); | 
|  | ssize_t setup_initrd(char *filename, void *membase, size_t memsize); | 
|  |  | 
|  | /* Lookup helpers */ | 
|  |  | 
|  | static struct virtual_machine *gth_to_vm(struct guest_thread *gth) | 
|  | { | 
|  | return ((struct vmm_thread*)gth)->vm; | 
|  | } | 
|  |  | 
|  | static struct vm_trapframe *gth_to_vmtf(struct guest_thread *gth) | 
|  | { | 
|  | return >h->uthread.u_ctx.tf.vm_tf; | 
|  | } | 
|  |  | 
|  | static struct vmm_gpcore_init *gth_to_gpci(struct guest_thread *gth) | 
|  | { | 
|  | return >h->gpci; | 
|  | } | 
|  |  | 
|  | static struct guest_thread *gpcid_to_gth(struct virtual_machine *vm, | 
|  | unsigned int gpc_id) | 
|  | { | 
|  | struct guest_thread **array; | 
|  | struct guest_thread *gth; | 
|  |  | 
|  | /* Syncing with any dynamic growth of __gths */ | 
|  | do { | 
|  | array = ACCESS_ONCE(vm->__gths); | 
|  | gth = array[gpc_id]; | 
|  | rmb();	/* read ret before rereading array pointer */ | 
|  | } while (array != ACCESS_ONCE(vm->__gths)); | 
|  | return gth; | 
|  | } | 
|  |  | 
|  | static struct vm_trapframe *gpcid_to_vmtf(struct virtual_machine *vm, | 
|  | unsigned int gpc_id) | 
|  | { | 
|  | return gth_to_vmtf(gpcid_to_gth(vm, gpc_id)); | 
|  | } | 
|  |  | 
|  | static struct virtual_machine *get_my_vm(void) | 
|  | { | 
|  | return ((struct vmm_thread*)current_uthread)->vm; | 
|  | } | 
|  |  | 
|  | /* memory helpers */ | 
|  | void *init_e820map(struct virtual_machine *vm, struct boot_params *bp); | 
|  | void checkmemaligned(uintptr_t memstart, size_t memsize); | 
|  | void mmap_memory(struct virtual_machine *vm, uintptr_t memstart, | 
|  | size_t memsize); | 
|  | bool mmap_file(const char *path, uintptr_t memstart, size_t memsize, | 
|  | uint64_t protections, size_t offset); | 
|  | void add_pte_entries(struct virtual_machine *vm, uintptr_t start, | 
|  | uintptr_t end); | 
|  | void setup_paging(struct virtual_machine *vm); | 
|  | void *setup_biostables(struct virtual_machine *vm, | 
|  | void *a, void *smbiostable); | 
|  | void *populate_stack(uintptr_t *stack, int argc, char *argv[], | 
|  | int envc, char *envp[], | 
|  | int auxc, struct elf_aux auxv[]); | 
|  | /* For vthreads */ | 
|  | struct guest_thread *create_guest_thread(struct virtual_machine *vm, | 
|  | unsigned int gpcoreid, | 
|  | struct vmm_gpcore_init *gpci); |