diff options
| author | Hanjie Wu <hanjiew@andrew.cmu.edu> | 2023-04-22 23:25:43 -0400 |
|---|---|---|
| committer | Hanjie Wu <hanjiew@andrew.cmu.edu> | 2023-04-22 23:25:43 -0400 |
| commit | a7e1cbd3cb53bd4fb43a52a806fd50ab6fa24a24 (patch) | |
| tree | ed823adb61b2b43d7c4f2f5ca45cff0dd9f66df5 /kern/hvcall.c | |
| parent | ed4192548143468362124ee7ee6105c001fe29c0 (diff) | |
pv
Diffstat (limited to 'kern/hvcall.c')
| -rw-r--r-- | kern/hvcall.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/kern/hvcall.c b/kern/hvcall.c new file mode 100644 index 0000000..1513942 --- /dev/null +++ b/kern/hvcall.c @@ -0,0 +1,674 @@ +/** @file syscall_hvcall.c + * + * @brief miscellaneous syscalls. + * + * @author Hanjie Wu (hanjiew) + * @bug No functional bugs + */ + +#include <simics.h> +#include <stdio.h> +#include <string.h> + +#include <common_kern.h> +#include <elf/elf_410.h> +#include <hvcall.h> +#include <hvcall_int.h> +#include <malloc_internal.h> +#include <ureg.h> + +#include <x86/asm.h> +#include <x86/cr.h> +#include <x86/eflags.h> +#include <x86/seg.h> + +#include <asm_instr.h> +#include <assert.h> +#include <console.h> +#include <loader.h> +#include <malloc.h> +#include <mm.h> +#include <paging.h> +#include <sched.h> +#include <sync.h> +#include <timer.h> +#include <usermem.h> + +#define HV_REFPD_OP HV_RESERVED_0 +#define HV_UNREFPD_OP HV_RESERVED_1 + +static void hvcall_exit(stack_frame_t* f); +static void hvcall_iret(stack_frame_t* f); +static void hvcall_setidt(stack_frame_t* f); +static void hvcall_setpd(stack_frame_t* f); +static void hvcall_adjustpg(stack_frame_t* f); +static void hvcall_print(stack_frame_t* f); +static void hvcall_set_color(stack_frame_t* f); +static void hvcall_set_cursor(stack_frame_t* f); +static void hvcall_get_cursor(stack_frame_t* f); +static void hvcall_print_at(stack_frame_t* f); + +static void hvcall_refpd(int ref); + +// 0: not pv, 1: processed +int handle_pv_syscall(stack_frame_t* f, int index) { + if (get_current()->process->pv == NULL) { + return 0; + } + if (f->eip >= USER_MEM_START) { + // inject interrupt + return 1; + } + pv_die("Syscall is not allowed for PV kernels"); + return 1; +} + +void sys_hvcall_real(stack_frame_t* f) { + pv_t* pv = get_current()->process->pv; + if (pv == NULL || f->eip >= USER_MEM_START) { + return; + } + switch (f->eax) { + case HV_MAGIC_OP: + f->eax = (reg_t)HV_MAGIC; + break; + case HV_EXIT_OP: + hvcall_exit(f); + break; + case HV_IRET_OP: + hvcall_iret(f); + break; + case HV_SETIDT_OP: + hvcall_setidt(f); + break; + case HV_DISABLE_OP: + pv_mask_interrupt(pv); + break; + case HV_ENABLE_OP: + pv_unmask_interrupt(pv); + break; + case HV_SETPD_OP: + hvcall_setpd(f); + break; + case HV_ADJUSTPG_OP: + hvcall_adjustpg(f); + break; + case HV_PRINT_OP: + hvcall_print(f); + break; + case HV_SET_COLOR_OP: + hvcall_set_color(f); + break; + case HV_SET_CURSOR_OP: + hvcall_set_cursor(f); + break; + case HV_GET_CURSOR_OP: + hvcall_get_cursor(f); + break; + case HV_PRINT_AT_OP: + hvcall_print_at(f); + break; + case HV_REFPD_OP: + hvcall_refpd(1); + break; + case HV_UNREFPD_OP: + hvcall_refpd(0); + break; + default: + pv_die("Bad hvcall number"); + break; + } +} + +static void hvcall_exit(stack_frame_t* f) { + reg_t esp = f->esp; + int status; + if (copy_from_user(esp, sizeof(int), &status) != 0) { + goto read_arg_fail; + } + get_current()->process->exit_value = status; + kill_current(); + +read_arg_fail: + pv_die("Bad argument address"); +} + +#define EFLAGS_PV_MASK \ + (EFL_CF | EFL_PF | EFL_AF | EFL_ZF | EFL_SF | EFL_TF | EFL_DF | EFL_OF | \ + EFL_RF) + +static void hvcall_iret(stack_frame_t* f) { + thread_t* t = get_current(); + pv_t* pv = t->process->pv; + reg_t esp = f->esp; + reg_t regs[5]; + if (copy_from_user(esp, 5 * sizeof(reg_t), regs) != 0) { + goto read_arg_fail; + } + f->eip = regs[0]; + reg_t eflags_user = (regs[1] & EFLAGS_PV_MASK); + reg_t eflags_kernel = (regs[1] & (~EFLAGS_PV_MASK)); + if ((eflags_kernel & (~(EFL_IF | EFL_RESV1))) != EFL_IOPL_RING0) { + goto bad_eflags; + } + if ((eflags_kernel & EFL_IF) != 0) { + pv_unmask_interrupt(pv); + } else { + pv_mask_interrupt(pv); + } + f->eflags = (eflags_user | DEFAULT_EFLAGS); + f->esp = regs[2]; + if (regs[3] != 0) { + pv->vesp0 = regs[3]; + pv_switch_mode(t->process, 0); + } + f->eax = regs[4]; + return; + +bad_eflags: + pv_die("Bad eflags value in iret"); +read_arg_fail: + pv_die("Bad argument address"); +} + +static void hvcall_setidt(stack_frame_t* f) { + reg_t esp = f->esp; + int index; + va_t eip0; + int is_dpl0; + if (copy_from_user(esp, sizeof(int), &index) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + sizeof(va_t), sizeof(va_t), &eip0) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + 2 * sizeof(va_t), sizeof(int), &is_dpl0) != 0) { + goto read_arg_fail; + } + if (index < 0 || index >= IDT_ENTS) { + goto bad_idt_index; + } + pv_t* pv = get_current()->process->pv; + pv_idt_entry_t* idt = pv_classify_interrupt(pv, index); + idt->eip = eip0; + idt->desc = ((idt->desc & (~VIDT_DPL_MASK)) | + (is_dpl0 != 0 ? VIDT_DPL_0 : VIDT_DPL_3)); + return; + +bad_idt_index: + pv_die("Bad IDT index"); +read_arg_fail: + pv_die("Bad argument address"); +} + +static pv_pd_t* translate_pv_pd(pv_t* pv, pa_t pd, int wp); + +static void hvcall_setpd(stack_frame_t* f) { + thread_t* t = get_current(); + pv_t* pv = t->process->pv; + reg_t esp = f->esp; + pa_t pd; + int wp; + if (copy_from_user(esp, sizeof(pa_t), &pd) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + sizeof(va_t), sizeof(int), &wp) != 0) { + goto read_arg_fail; + } + if ((pd & PAGE_OFFSET_MASK) != 0) { + goto bad_cr3; + } + pv_pd_t* pv_pd = translate_pv_pd(pv, pd, wp); + if (pv_pd == NULL) { + goto alloc_pd_fail; + } + queue_insert_head(&pv->shadow_pds, &pv_pd->pv_link); + select_pv_pd(t->process, pv_pd); + return; + +alloc_pd_fail: + pv_die("Page table translation failed"); +bad_cr3: + pv_die("Bad page directory address"); +read_arg_fail: + pv_die("Bad argument address"); +} + +#define PDE_RESV_MASK 0xf00 +#define PTE_RESV_MASK 0xe00 + +static pv_pd_t* translate_pv_pd(pv_t* pv, pa_t pd, int wp) { + pa_t mem_limit = pv->n_pages * PAGE_SIZE; + pa_t overflow_pa = machine_phys_frames() * PAGE_SIZE; + + char* temp_space = smalloc(4 * PAGE_SIZE); + if (temp_space == NULL) { + goto alloc_temp_fail; + } + page_directory_t* t_pd = (page_directory_t*)(temp_space); + page_table_t* t_pt = (page_table_t*)(temp_space + PAGE_SIZE); + page_directory_t* t_user_pd = + (page_directory_t*)(temp_space + 2 * PAGE_SIZE); + page_table_t* t_user_pt = (page_table_t*)(temp_space + 3 * PAGE_SIZE); + + pv_pd_t* pv_pd = smalloc(sizeof(pv_pd_t)); + if (pv_pd == NULL) { + goto alloc_pv_pd_fail; + } + pa_t cr3 = alloc_user_pages(1); + if (cr3 == BAD_PA) { + goto alloc_cr3_fail; + } + int old_if = save_clear_if(); + memset((void*)map_phys_page(cr3, NULL), 0, PAGE_SIZE); + restore_if(old_if); + + pa_t user_cr3 = alloc_user_pages(1); + if (user_cr3 == BAD_PA) { + free_user_pages(cr3, 1); + goto alloc_user_cr3_fail; + } + old_if = save_clear_if(); + memset((void*)map_phys_page(user_cr3, NULL), 0, PAGE_SIZE); + restore_if(old_if); + + memset(t_pd, 0, PAGE_SIZE); + memset(t_user_pd, 0, PAGE_SIZE); + int i; + for (i = 0; i < USER_PD_START; i++) { + (*t_pd)[i] = (*kernel_pd)[i]; + (*t_user_pd)[i] = (*kernel_pd)[i]; + } + old_if = save_clear_if(); + if (pd >= mem_limit) { + goto bad_pt; + } + pd += pv->mem_base; + for (i = 0; i < NUM_PAGE_ENTRY - USER_PD_START; i++) { + page_directory_t* old_pd = (page_directory_t*)map_phys_page(pd, NULL); + pde_t old_pde = (*old_pd)[i]; + if ((old_pde & (PTE_PRESENT << PTE_P_SHIFT)) == 0) { + continue; + } + pa_t pt_pa = get_page_table(old_pde); + if (pt_pa >= mem_limit) { + goto bad_pt; + } + pt_pa += pv->mem_base; + page_table_t* pt = (page_table_t*)map_phys_page(pt_pa, NULL); + int j; + for (j = 0; j < NUM_PAGE_ENTRY; j++) { + pte_t old_pte = (*pt)[j]; + if ((old_pte & (PTE_PRESENT << PTE_P_SHIFT)) == 0) { + (*t_pt)[j] = BAD_PTE; + (*t_user_pt)[j] = BAD_PTE; + continue; + } + pa_t pa = get_page_base(old_pte); + if (pa > mem_limit) { + pa = overflow_pa; + } else { + pa = pv->mem_base + pa; + } + int old_us = (old_pte & (1 << PTE_US_SHIFT)); + int old_rw = (old_pte & (1 << PTE_RW_SHIFT)); + int new_mask, new_user_mask; + if (wp == 0) { + new_mask = ((1 << PTE_P_SHIFT) | (PTE_RW << PTE_RW_SHIFT) | + (PTE_USER << PTE_US_SHIFT)); + } else { + new_mask = + ((1 << PTE_P_SHIFT) | old_rw | (PTE_USER << PTE_US_SHIFT)); + } + if (old_us != 0) { + new_user_mask = + ((1 << PTE_P_SHIFT) | old_rw | (PTE_USER << PTE_US_SHIFT)); + } else { + new_user_mask = 0; + } + (*t_pt)[j] = + ((pa & PAGE_BASE_MASK) | (old_pte & PTE_RESV_MASK) | new_mask); + (*t_user_pt)[j] = (new_user_mask == 0 ? BAD_PTE + : ((pa & PAGE_BASE_MASK) | + (old_pte & PTE_RESV_MASK) | + new_user_mask)); + } + pa_t new_pt = alloc_user_pages(1); + if (new_pt == BAD_PA) { + goto alloc_pt_fail; + } + pa_t new_user_pt = alloc_user_pages(1); + if (new_user_pt == BAD_PA) { + free_user_pages(new_pt, 1); + goto alloc_pt_fail; + } + memcpy((void*)map_phys_page(new_pt, NULL), t_pt, PAGE_SIZE); + memcpy((void*)map_phys_page(new_user_pt, NULL), t_user_pt, PAGE_SIZE); + pde_t new_pde = ((old_pde & PDE_RESV_MASK) | + (PTE_USER << PTE_US_SHIFT) | (1 << PTE_P_SHIFT)); + if (wp == 0) { + new_pde |= (PTE_RW << PTE_RW_SHIFT); + } else { + new_pde |= (old_pde & (1 << PTE_RW_SHIFT)); + } + (*t_pd)[USER_PD_START + i] = (new_pt | new_pde); + (*t_user_pd)[USER_PD_START + i] = (new_user_pt | new_pde); + } + memcpy((void*)map_phys_page(cr3, NULL), t_pd, PAGE_SIZE); + memcpy((void*)map_phys_page(user_cr3, NULL), t_user_pd, PAGE_SIZE); + restore_if(old_if); + sfree(temp_space, 4 * PAGE_SIZE); + pv_pd->guest_pd = pd; + pv_pd->wp = wp; + pv_pd->cr3 = cr3; + pv_pd->user_cr3 = user_cr3; + pv_pd->refcount = 0; + return pv_pd; + +bad_pt: +alloc_pt_fail: + memcpy((void*)map_phys_page(cr3, NULL), t_pd, PAGE_SIZE); + memcpy((void*)map_phys_page(user_cr3, NULL), t_user_pd, PAGE_SIZE); + restore_if(old_if); + destroy_pd(cr3); + destroy_pd(user_cr3); +alloc_user_cr3_fail: +alloc_cr3_fail: + sfree(pv_pd, sizeof(pv_pd_t)); +alloc_pv_pd_fail: + sfree(temp_space, 4 * PAGE_SIZE); +alloc_temp_fail: + return NULL; +} + +static void hvcall_adjustpg(stack_frame_t* f) { + thread_t* t = get_current(); + pv_t* pv = t->process->pv; + pv_pd_t* pv_pd = pv->active_shadow_pd; + reg_t esp = f->esp; + va_t addr; + if (copy_from_user(esp, sizeof(va_t), &addr) != 0) { + goto read_arg_fail; + } + if ((addr & PAGE_OFFSET_MASK) != 0 || addr >= PV_VM_LIMIT) { + goto bad_addr; + } + pa_t mem_limit = pv->n_pages * PAGE_SIZE; + + pa_t pt_pa, user_pt_pa; + int old_if = save_clear_if(); + page_directory_t* old_pd = + (page_directory_t*)map_phys_page(pv_pd->guest_pd, NULL); + pde_t old_pde = (*old_pd)[get_pd_index(addr)]; + if ((old_pde & (1 << PTE_P_SHIFT)) == 0) { + page_directory_t* pd = + (page_directory_t*)map_phys_page(pv_pd->cr3, NULL); + pde_t* pde = &(*pd)[get_pd_index(addr) + USER_PD_START]; + if (*pde != BAD_PDE) { + pt_pa = get_page_table(*pde); + *pde = BAD_PDE; + free_user_pages(pt_pa, 1); + } + page_directory_t* user_pd = + (page_directory_t*)map_phys_page(pv_pd->user_cr3, NULL); + pde_t* user_pde = &(*user_pd)[get_pd_index(addr) + USER_PD_START]; + if (*user_pde != BAD_PDE) { + user_pt_pa = get_page_table(*user_pde); + *user_pde = BAD_PDE; + free_user_pages(user_pt_pa, 1); + } + } else { + pde_t new_pde = ((old_pde & PDE_RESV_MASK) | + (PTE_USER << PTE_US_SHIFT) | (1 << PTE_P_SHIFT)); + if (pv_pd->wp == 0) { + new_pde |= (PTE_RW << PTE_RW_SHIFT); + } else { + new_pde |= (old_pde & (1 << PTE_RW_SHIFT)); + } + page_directory_t* pd = + (page_directory_t*)map_phys_page(pv_pd->cr3, NULL); + pde_t* pde = &(*pd)[get_pd_index(addr) + USER_PD_START]; + if (*pde != BAD_PDE) { + pt_pa = get_page_table(*pde); + *pde = (pt_pa | new_pde); + } else { + pt_pa = alloc_user_pages(1); + if (pt_pa == BAD_PA) { + goto alloc_pt_fail; + } + *pde = (pt_pa | new_pde); + memset((void*)map_phys_page(pt_pa, NULL), 0, PAGE_SIZE); + } + page_directory_t* user_pd = + (page_directory_t*)map_phys_page(pv_pd->user_cr3, NULL); + pde_t* user_pde = &(*user_pd)[get_pd_index(addr) + USER_PD_START]; + if (*user_pde != BAD_PDE) { + user_pt_pa = get_page_table(*user_pde); + *user_pde = (user_pt_pa | new_pde); + } else { + user_pt_pa = alloc_user_pages(1); + if (user_pt_pa == BAD_PA) { + goto alloc_pt_fail; + } + *user_pde = (user_pt_pa | new_pde); + memset((void*)map_phys_page(user_pt_pa, NULL), 0, PAGE_SIZE); + } + pa_t old_pt_pa = get_page_table(old_pde); + if (old_pt_pa >= mem_limit) { + goto bad_pt; + } + old_pt_pa += pv->mem_base; + page_table_t* old_pt = (page_table_t*)map_phys_page(old_pt_pa, NULL); + pte_t old_pte = (*old_pt)[get_pt_index(addr)]; + pte_t pte, user_pte; + if ((old_pte & (PTE_PRESENT << PTE_P_SHIFT)) == 0) { + user_pte = pte = BAD_PTE; + } else { + pa_t pa = get_page_base(old_pte); + if (pa > mem_limit) { + pa = machine_phys_frames() * PAGE_SIZE; + } else { + pa = pv->mem_base + pa; + } + int old_us = (old_pte & (1 << PTE_US_SHIFT)); + int old_rw = (old_pte & (1 << PTE_RW_SHIFT)); + int new_mask, new_user_mask; + if (pv_pd->wp == 0) { + new_mask = ((1 << PTE_P_SHIFT) | (PTE_RW << PTE_RW_SHIFT) | + (PTE_USER << PTE_US_SHIFT)); + } else { + new_mask = + ((1 << PTE_P_SHIFT) | old_rw | (PTE_USER << PTE_US_SHIFT)); + } + if (old_us != 0) { + new_user_mask = + ((1 << PTE_P_SHIFT) | old_rw | (PTE_USER << PTE_US_SHIFT)); + } else { + new_user_mask = 0; + } + pte = + ((pa & PAGE_BASE_MASK) | (old_pte & PTE_RESV_MASK) | new_mask); + user_pte = (new_user_mask == 0 + ? BAD_PTE + : ((pa & PAGE_BASE_MASK) | + (old_pte & PTE_RESV_MASK) | new_user_mask)); + } + page_table_t* pt = (page_table_t*)map_phys_page(pt_pa, NULL); + (*pt)[get_pt_index(addr)] = pte; + page_table_t* user_pt = (page_table_t*)map_phys_page(user_pt_pa, NULL); + (*user_pt)[get_pt_index(addr)] = user_pte; + } + restore_if(old_if); + invlpg(addr); + return; + +bad_pt: + restore_if(old_if); + pv_die("Bad page table address"); +alloc_pt_fail: + restore_if(old_if); + pv_die("Failed to alloc page table"); +bad_addr: + pv_die("Unaligned virtual address"); +read_arg_fail: + pv_die("Bad argument address"); +} + +static void hvcall_print(stack_frame_t* f) { + reg_t esp = f->esp; + int len; + va_t base; + if (copy_from_user(esp, sizeof(int), &len) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + sizeof(va_t), sizeof(va_t), &base) != 0) { + goto read_arg_fail; + } + if (len < 0) { + goto bad_length; + } + mutex_lock(&console_lock); + int result = print_buf_from_user(base, len); + mutex_unlock(&console_lock); + if (result != 0) { + goto print_fail; + } + return; + +print_fail: + pv_die("Error when printing buffer"); +bad_length: + pv_die("Bad buffer length"); +read_arg_fail: + pv_die("Bad argument address"); +} + +static void hvcall_set_color(stack_frame_t* f) { + reg_t esp = f->esp; + int color; + if (copy_from_user(esp, sizeof(int), &color) != 0) { + goto read_arg_fail; + } + mutex_lock(&console_lock); + int result = set_term_color(color); + mutex_unlock(&console_lock); + if (result != 0) { + goto set_fail; + } + return; + +set_fail: + pv_die("Error when setting color"); +read_arg_fail: + pv_die("Bad argument address"); +} + +static void hvcall_set_cursor(stack_frame_t* f) { + reg_t esp = f->esp; + int row, col; + if (copy_from_user(esp, sizeof(int), &row) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + sizeof(va_t), sizeof(int), &col) != 0) { + goto read_arg_fail; + } + mutex_lock(&console_lock); + int result = set_cursor(row, col); + mutex_unlock(&console_lock); + if (result != 0) { + goto set_fail; + } + return; + +set_fail: + pv_die("Error when setting cursor position"); +read_arg_fail: + pv_die("Bad argument address"); +} + +static void hvcall_get_cursor(stack_frame_t* f) { + reg_t esp = f->esp; + va_t prow, pcol; + if (copy_from_user(esp, sizeof(va_t), &prow) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + sizeof(va_t), sizeof(va_t), &pcol) != 0) { + goto read_arg_fail; + } + int row, col; + mutex_lock(&console_lock); + get_cursor(&row, &col); + mutex_unlock(&console_lock); + if (copy_to_user(prow, sizeof(int), &row) != 0) { + goto bad_arg; + } + if (copy_to_user(pcol, sizeof(int), &col) != 0) { + goto bad_arg; + } + return; + +bad_arg: +read_arg_fail: + pv_die("Bad argument address"); +} + +static void hvcall_print_at(stack_frame_t* f) { + reg_t esp = f->esp; + int len; + va_t base; + int row, col, old_row, old_col; + int color, old_color; + if (copy_from_user(esp, sizeof(int), &len) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + sizeof(va_t), sizeof(va_t), &base) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + 2 * sizeof(va_t), sizeof(int), &row) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + 3 * sizeof(va_t), sizeof(int), &col) != 0) { + goto read_arg_fail; + } + if (copy_from_user(esp + 4 * sizeof(va_t), sizeof(int), &color) != 0) { + goto read_arg_fail; + } + mutex_lock(&console_lock); + get_cursor(&old_row, &old_col); + if (set_cursor(row, col) != 0) { + goto bad_pos; + } + get_term_color(&old_color); + if (set_term_color(color) != 0) { + goto bad_color; + } + if (print_buf_from_user(base, len) != 0) { + goto bad_print; + } + set_term_color(old_color); + set_cursor(old_row, old_col); + mutex_unlock(&console_lock); + return; + +bad_print: + set_term_color(old_color); +bad_color: + set_cursor(old_row, old_col); +bad_pos: + mutex_unlock(&console_lock); + pv_die("Bad argument"); +read_arg_fail: + pv_die("Bad argument address"); +} + +static void hvcall_refpd(int ref) { + pv_t* pv = get_current()->process->pv; + if (ref == 1) { + pv->active_shadow_pd->refcount++; + } else { + pv->active_shadow_pd->refcount--; + if (pv->active_shadow_pd->refcount <= 0) { + pv_die("Page table destroyed by kerenl"); + } + } +} |
