1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <string.h>
8#include <stdio.h>
9#include <stdlib.h>
10
11#include <utils/util.h>
12#include <sel4/sel4.h>
13#include <vka/object.h>
14#include <vka/capops.h>
15#include <sel4utils/mapping.h>
16#include <sel4utils/api.h>
17
18#include <sel4vm/guest_vm.h>
19#include <sel4vm/guest_vm_exits.h>
20
21#include <sel4vm/boot.h>
22#include <sel4vm/guest_memory.h>
23#include <sel4vm/guest_memory_helpers.h>
24
25#include "vm_boot.h"
26#include "guest_vspace.h"
27#include "guest_memory.h"
28#include "guest_state.h"
29#include "vmcs.h"
30#include "processor/decode.h"
31#include "processor/apicdef.h"
32#include "processor/lapic.h"
33#include "processor/platfeature.h"
34
35#define VM_VMCS_CR0_MASK           (X86_CR0_PG | X86_CR0_PE)
36#define VM_VMCS_CR0_VALUE          VM_VMCS_CR0_MASK
37/* We need to own the PSE and PAE bits up until the guest has actually turned on paging,
38 * then it can control them
39 */
40#define VM_VMCS_CR4_MASK           (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_VMXE)
41#define VM_VMCS_CR4_VALUE          (X86_CR4_PSE | X86_CR4_VMXE)
42
43#define GUEST_PAGE_DIR 0x10000000
44
45static int make_guest_page_dir_continued(void *access_addr, void *vaddr, void *cookie)
46{
47    /* Write into this frame as the init page directory: 4M pages, 1 to 1 mapping. */
48    uint32_t *pd = vaddr;
49    for (int i = 0; i < 1024; i++) {
50        /* Present, write, user, page size 4M */
51        pd[i] = (i << PAGE_BITS_4M) | 0x87;
52    }
53    return 0;
54}
55
56static vm_frame_t pd_alloc_iterator(uintptr_t addr, void *cookie)
57{
58    int ret;
59    vka_object_t object;
60    vm_frame_t frame_result = { seL4_CapNull, seL4_NoRights, 0, 0 };
61    vm_t *vm = (vm_t *)cookie;
62    if (!vm) {
63        return frame_result;
64    }
65    int page_size = seL4_PageBits;
66    uintptr_t frame_start = ROUND_DOWN(addr, BIT(page_size));
67    ret = vka_alloc_frame_maybe_device(vm->vka, page_size, false, &object);
68    if (ret) {
69        ZF_LOGE("Failed to allocate frame for address 0x%x", (unsigned int)addr);
70        return frame_result;
71    }
72    frame_result.cptr = object.cptr;
73    frame_result.rights = seL4_AllRights;
74    frame_result.vaddr = frame_start;
75    frame_result.size_bits = page_size;
76    return frame_result;
77}
78
79
80static int make_guest_page_dir(vm_t *vm)
81{
82    /* Create a 4K Page to be our 1-1 pd */
83    /* This is constructed with magical new memory that we will not tell Linux about */
84    vm_memory_reservation_t *pd_reservation = vm_reserve_memory_at(vm, GUEST_PAGE_DIR, BIT(seL4_PageBits),
85                                                                   default_error_fault_callback, NULL);
86    if (!pd_reservation) {
87        ZF_LOGE("Failed to reserve page for initial guest pd");
88        return -1;
89    }
90    int err = map_vm_memory_reservation(vm, pd_reservation, pd_alloc_iterator, (void *)vm);
91    if (err) {
92        ZF_LOGE("Failed to map page for initial guest pd");
93    }
94    printf("Guest page dir allocated at 0x%x. Creating 1-1 entries\n", (unsigned int)GUEST_PAGE_DIR);
95    vm->arch.guest_pd = GUEST_PAGE_DIR;
96    return vspace_access_page_with_callback(&vm->mem.vm_vspace, &vm->mem.vmm_vspace, (void *)GUEST_PAGE_DIR,
97                                            seL4_PageBits, seL4_AllRights, 1, make_guest_page_dir_continued, NULL);
98}
99
100int vm_init_arch(vm_t *vm)
101{
102    int err;
103
104    if (!vm) {
105        ZF_LOGE("Failed to initialise vm arch: Invalid vm");
106        return -1;
107    }
108
109    vm->arch.vmcall_handlers = NULL;
110    vm->arch.vmcall_num_handlers = 0;
111    vm->arch.ioport_list.num_ioports = 0;
112    vm->arch.ioport_list.ioports = NULL;
113
114    /* Create an EPT which is the pd for all the vcpu tcbs */
115    err = vka_alloc_ept_pml4(vm->vka, &vm->mem.vm_vspace_root);
116    if (err) {
117        return -1;
118    }
119    /* Assign an ASID */
120    err = simple_ASIDPool_assign(vm->simple, vm->mem.vm_vspace_root.cptr);
121    if (err != seL4_NoError) {
122        ZF_LOGE("Failed to assign ASID pool to EPT root");
123        return -1;
124    }
125    /* Install the guest PD */
126    err = seL4_TCB_SetEPTRoot(simple_get_tcb(vm->simple), vm->mem.vm_vspace_root.cptr);
127    assert(err == seL4_NoError);
128    /* Initialize a vspace for the guest */
129    err = vm_init_guest_vspace(&vm->mem.vmm_vspace, &vm->mem.vmm_vspace,
130                               &vm->mem.vm_vspace, vm->vka, vm->mem.vm_vspace_root.cptr);
131    if (err) {
132        return err;
133    }
134
135    /* Bind our interrupt pending callback */
136    err = seL4_TCB_BindNotification(simple_get_init_cap(vm->simple, seL4_CapInitThreadTCB), vm->host_endpoint);
137    assert(err == seL4_NoError);
138    return err;
139}
140
141int vm_create_vcpu_arch(vm_t *vm, vm_vcpu_t *vcpu)
142{
143    int err;
144    err = seL4_X86_VCPU_SetTCB(vcpu->vcpu.cptr, simple_get_tcb(vm->simple));
145    assert(err == seL4_NoError);
146    /* All LAPICs are created enabled, in virtual wire mode */
147    vm_create_lapic(vcpu, 1);
148    vcpu->vcpu_arch.guest_state = calloc(1, sizeof(guest_state_t));
149    if (!vcpu->vcpu_arch.guest_state) {
150        return -1;
151    }
152
153    /* Create our 4K page 1-1 pd */
154    err = make_guest_page_dir(vm);
155    if (err) {
156        return -1;
157    }
158
159    vm_guest_state_initialise(vcpu->vcpu_arch.guest_state);
160    /* Set the initial CR state */
161    vcpu->vcpu_arch.guest_state->virt.cr.cr0_mask = VM_VMCS_CR0_MASK;
162    vcpu->vcpu_arch.guest_state->virt.cr.cr0_shadow = 0;
163    vcpu->vcpu_arch.guest_state->virt.cr.cr0_host_bits = VM_VMCS_CR0_VALUE;
164    vcpu->vcpu_arch.guest_state->virt.cr.cr4_mask = VM_VMCS_CR4_MASK;
165    vcpu->vcpu_arch.guest_state->virt.cr.cr4_shadow = 0;
166    vcpu->vcpu_arch.guest_state->virt.cr.cr4_host_bits = VM_VMCS_CR4_VALUE;
167    /* Set the initial CR states */
168    vm_guest_state_set_cr0(vcpu->vcpu_arch.guest_state, vcpu->vcpu_arch.guest_state->virt.cr.cr0_host_bits);
169    vm_guest_state_set_cr3(vcpu->vcpu_arch.guest_state, vm->arch.guest_pd);
170    vm_guest_state_set_cr4(vcpu->vcpu_arch.guest_state, vcpu->vcpu_arch.guest_state->virt.cr.cr4_host_bits);
171    /* Init guest OS vcpu state. */
172    vm_vmcs_init_guest(vcpu);
173    return 0;
174}
175