1996feed4SSergio Andres Gomez Del Real // This software is licensed under the terms of the GNU General Public 2996feed4SSergio Andres Gomez Del Real // License version 2, as published by the Free Software Foundation, and 3996feed4SSergio Andres Gomez Del Real // may be copied, distributed, and modified under those terms. 4996feed4SSergio Andres Gomez Del Real // 5996feed4SSergio Andres Gomez Del Real // This program is distributed in the hope that it will be useful, 6996feed4SSergio Andres Gomez Del Real // but WITHOUT ANY WARRANTY; without even the implied warranty of 7996feed4SSergio Andres Gomez Del Real // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8996feed4SSergio Andres Gomez Del Real // GNU General Public License for more details. 9996feed4SSergio Andres Gomez Del Real #include "qemu/osdep.h" 10996feed4SSergio Andres Gomez Del Real #include "qemu-common.h" 11996feed4SSergio Andres Gomez Del Real #include "qemu/error-report.h" 12996feed4SSergio Andres Gomez Del Real 13996feed4SSergio Andres Gomez Del Real #include "sysemu/hvf.h" 14996feed4SSergio Andres Gomez Del Real #include "hvf-i386.h" 1569e0a03cSPaolo Bonzini #include "vmcs.h" 1669e0a03cSPaolo Bonzini #include "vmx.h" 1769e0a03cSPaolo Bonzini #include "x86.h" 1869e0a03cSPaolo Bonzini #include "x86_descr.h" 1969e0a03cSPaolo Bonzini #include "x86_mmu.h" 2069e0a03cSPaolo Bonzini #include "x86_decode.h" 2169e0a03cSPaolo Bonzini #include "x86_emu.h" 2269e0a03cSPaolo Bonzini #include "x86_task.h" 2369e0a03cSPaolo Bonzini #include "x86hvf.h" 24996feed4SSergio Andres Gomez Del Real 25996feed4SSergio Andres Gomez Del Real #include <Hypervisor/hv.h> 26996feed4SSergio Andres Gomez Del Real #include <Hypervisor/hv_vmx.h> 27996feed4SSergio Andres Gomez Del Real 28996feed4SSergio Andres Gomez Del Real #include "exec/address-spaces.h" 29996feed4SSergio Andres Gomez Del Real #include "exec/exec-all.h" 30996feed4SSergio Andres Gomez Del Real #include "exec/ioport.h" 31996feed4SSergio Andres Gomez Del Real #include "hw/i386/apic_internal.h" 32996feed4SSergio Andres Gomez Del Real #include "hw/boards.h" 33996feed4SSergio Andres Gomez Del Real #include "qemu/main-loop.h" 34996feed4SSergio Andres Gomez Del Real #include "strings.h" 35996feed4SSergio Andres Gomez Del Real #include "sysemu/accel.h" 36996feed4SSergio Andres Gomez Del Real #include "sysemu/sysemu.h" 37996feed4SSergio Andres Gomez Del Real #include "target/i386/cpu.h" 38996feed4SSergio Andres Gomez Del Real 39996feed4SSergio Andres Gomez Del Real // TODO: taskswitch handling 40996feed4SSergio Andres Gomez Del Real static void save_state_to_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) 41996feed4SSergio Andres Gomez Del Real { 42996feed4SSergio Andres Gomez Del Real X86CPU *x86_cpu = X86_CPU(cpu); 43996feed4SSergio Andres Gomez Del Real CPUX86State *env = &x86_cpu->env; 44996feed4SSergio Andres Gomez Del Real 45996feed4SSergio Andres Gomez Del Real /* CR3 and ldt selector are not saved intentionally */ 46996feed4SSergio Andres Gomez Del Real tss->eip = EIP(env); 47996feed4SSergio Andres Gomez Del Real tss->eflags = EFLAGS(env); 48996feed4SSergio Andres Gomez Del Real tss->eax = EAX(env); 49996feed4SSergio Andres Gomez Del Real tss->ecx = ECX(env); 50996feed4SSergio Andres Gomez Del Real tss->edx = EDX(env); 51996feed4SSergio Andres Gomez Del Real tss->ebx = EBX(env); 52996feed4SSergio Andres Gomez Del Real tss->esp = ESP(env); 53996feed4SSergio Andres Gomez Del Real tss->ebp = EBP(env); 54996feed4SSergio Andres Gomez Del Real tss->esi = ESI(env); 55996feed4SSergio Andres Gomez Del Real tss->edi = EDI(env); 56996feed4SSergio Andres Gomez Del Real 57*6701d81dSPaolo Bonzini tss->es = vmx_read_segment_selector(cpu, R_ES).sel; 58*6701d81dSPaolo Bonzini tss->cs = vmx_read_segment_selector(cpu, R_CS).sel; 59*6701d81dSPaolo Bonzini tss->ss = vmx_read_segment_selector(cpu, R_SS).sel; 60*6701d81dSPaolo Bonzini tss->ds = vmx_read_segment_selector(cpu, R_DS).sel; 61*6701d81dSPaolo Bonzini tss->fs = vmx_read_segment_selector(cpu, R_FS).sel; 62*6701d81dSPaolo Bonzini tss->gs = vmx_read_segment_selector(cpu, R_GS).sel; 63996feed4SSergio Andres Gomez Del Real } 64996feed4SSergio Andres Gomez Del Real 65996feed4SSergio Andres Gomez Del Real static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) 66996feed4SSergio Andres Gomez Del Real { 67996feed4SSergio Andres Gomez Del Real X86CPU *x86_cpu = X86_CPU(cpu); 68996feed4SSergio Andres Gomez Del Real CPUX86State *env = &x86_cpu->env; 69996feed4SSergio Andres Gomez Del Real 70996feed4SSergio Andres Gomez Del Real wvmcs(cpu->hvf_fd, VMCS_GUEST_CR3, tss->cr3); 71996feed4SSergio Andres Gomez Del Real 72996feed4SSergio Andres Gomez Del Real RIP(env) = tss->eip; 73996feed4SSergio Andres Gomez Del Real EFLAGS(env) = tss->eflags | 2; 74996feed4SSergio Andres Gomez Del Real 75996feed4SSergio Andres Gomez Del Real /* General purpose registers */ 76996feed4SSergio Andres Gomez Del Real RAX(env) = tss->eax; 77996feed4SSergio Andres Gomez Del Real RCX(env) = tss->ecx; 78996feed4SSergio Andres Gomez Del Real RDX(env) = tss->edx; 79996feed4SSergio Andres Gomez Del Real RBX(env) = tss->ebx; 80996feed4SSergio Andres Gomez Del Real RSP(env) = tss->esp; 81996feed4SSergio Andres Gomez Del Real RBP(env) = tss->ebp; 82996feed4SSergio Andres Gomez Del Real RSI(env) = tss->esi; 83996feed4SSergio Andres Gomez Del Real RDI(env) = tss->edi; 84996feed4SSergio Andres Gomez Del Real 85*6701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR); 86*6701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES); 87*6701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS); 88*6701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS); 89*6701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS); 90*6701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS); 91*6701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS); 92996feed4SSergio Andres Gomez Del Real 93996feed4SSergio Andres Gomez Del Real #if 0 94*6701d81dSPaolo Bonzini load_segment(cpu, R_LDTR, tss->ldt); 95*6701d81dSPaolo Bonzini load_segment(cpu, R_ES, tss->es); 96*6701d81dSPaolo Bonzini load_segment(cpu, R_CS, tss->cs); 97*6701d81dSPaolo Bonzini load_segment(cpu, R_SS, tss->ss); 98*6701d81dSPaolo Bonzini load_segment(cpu, R_DS, tss->ds); 99*6701d81dSPaolo Bonzini load_segment(cpu, R_FS, tss->fs); 100*6701d81dSPaolo Bonzini load_segment(cpu, R_GS, tss->gs); 101996feed4SSergio Andres Gomez Del Real #endif 102996feed4SSergio Andres Gomez Del Real } 103996feed4SSergio Andres Gomez Del Real 104996feed4SSergio Andres Gomez Del Real static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel, 105996feed4SSergio Andres Gomez Del Real uint64_t old_tss_base, struct x86_segment_descriptor *new_desc) 106996feed4SSergio Andres Gomez Del Real { 107996feed4SSergio Andres Gomez Del Real struct x86_tss_segment32 tss_seg; 108996feed4SSergio Andres Gomez Del Real uint32_t new_tss_base = x86_segment_base(new_desc); 109996feed4SSergio Andres Gomez Del Real uint32_t eip_offset = offsetof(struct x86_tss_segment32, eip); 110996feed4SSergio Andres Gomez Del Real uint32_t ldt_sel_offset = offsetof(struct x86_tss_segment32, ldt); 111996feed4SSergio Andres Gomez Del Real 112996feed4SSergio Andres Gomez Del Real vmx_read_mem(cpu, &tss_seg, old_tss_base, sizeof(tss_seg)); 113996feed4SSergio Andres Gomez Del Real save_state_to_tss32(cpu, &tss_seg); 114996feed4SSergio Andres Gomez Del Real 115996feed4SSergio Andres Gomez Del Real vmx_write_mem(cpu, old_tss_base + eip_offset, &tss_seg.eip, ldt_sel_offset - eip_offset); 116996feed4SSergio Andres Gomez Del Real vmx_read_mem(cpu, &tss_seg, new_tss_base, sizeof(tss_seg)); 117996feed4SSergio Andres Gomez Del Real 118996feed4SSergio Andres Gomez Del Real if (old_tss_sel.sel != 0xffff) { 119996feed4SSergio Andres Gomez Del Real tss_seg.prev_tss = old_tss_sel.sel; 120996feed4SSergio Andres Gomez Del Real 121996feed4SSergio Andres Gomez Del Real vmx_write_mem(cpu, new_tss_base, &tss_seg.prev_tss, sizeof(tss_seg.prev_tss)); 122996feed4SSergio Andres Gomez Del Real } 123996feed4SSergio Andres Gomez Del Real load_state_from_tss32(cpu, &tss_seg); 124996feed4SSergio Andres Gomez Del Real return 0; 125996feed4SSergio Andres Gomez Del Real } 126996feed4SSergio Andres Gomez Del Real 127996feed4SSergio Andres Gomez Del Real void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) 128996feed4SSergio Andres Gomez Del Real { 129996feed4SSergio Andres Gomez Del Real uint64_t rip = rreg(cpu->hvf_fd, HV_X86_RIP); 130996feed4SSergio Andres Gomez Del Real if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && 131996feed4SSergio Andres Gomez Del Real gate_type != VMCS_INTR_T_HWINTR && 132996feed4SSergio Andres Gomez Del Real gate_type != VMCS_INTR_T_NMI)) { 133996feed4SSergio Andres Gomez Del Real int ins_len = rvmcs(cpu->hvf_fd, VMCS_EXIT_INSTRUCTION_LENGTH); 134996feed4SSergio Andres Gomez Del Real macvm_set_rip(cpu, rip + ins_len); 135996feed4SSergio Andres Gomez Del Real return; 136996feed4SSergio Andres Gomez Del Real } 137996feed4SSergio Andres Gomez Del Real 138996feed4SSergio Andres Gomez Del Real load_regs(cpu); 139996feed4SSergio Andres Gomez Del Real 140996feed4SSergio Andres Gomez Del Real struct x86_segment_descriptor curr_tss_desc, next_tss_desc; 141996feed4SSergio Andres Gomez Del Real int ret; 142*6701d81dSPaolo Bonzini x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); 143*6701d81dSPaolo Bonzini uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR); 144996feed4SSergio Andres Gomez Del Real uint32_t desc_limit; 145996feed4SSergio Andres Gomez Del Real struct x86_call_gate task_gate_desc; 146996feed4SSergio Andres Gomez Del Real struct vmx_segment vmx_seg; 147996feed4SSergio Andres Gomez Del Real 148996feed4SSergio Andres Gomez Del Real X86CPU *x86_cpu = X86_CPU(cpu); 149996feed4SSergio Andres Gomez Del Real CPUX86State *env = &x86_cpu->env; 150996feed4SSergio Andres Gomez Del Real 151996feed4SSergio Andres Gomez Del Real x86_read_segment_descriptor(cpu, &next_tss_desc, tss_sel); 152996feed4SSergio Andres Gomez Del Real x86_read_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel); 153996feed4SSergio Andres Gomez Del Real 154996feed4SSergio Andres Gomez Del Real if (reason == TSR_IDT_GATE && gate_valid) { 155996feed4SSergio Andres Gomez Del Real int dpl; 156996feed4SSergio Andres Gomez Del Real 157996feed4SSergio Andres Gomez Del Real ret = x86_read_call_gate(cpu, &task_gate_desc, gate); 158996feed4SSergio Andres Gomez Del Real 159996feed4SSergio Andres Gomez Del Real dpl = task_gate_desc.dpl; 160*6701d81dSPaolo Bonzini x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); 161996feed4SSergio Andres Gomez Del Real if (tss_sel.rpl > dpl || cs.rpl > dpl) 162996feed4SSergio Andres Gomez Del Real ;//DPRINTF("emulate_gp"); 163996feed4SSergio Andres Gomez Del Real } 164996feed4SSergio Andres Gomez Del Real 165996feed4SSergio Andres Gomez Del Real desc_limit = x86_segment_limit(&next_tss_desc); 166996feed4SSergio Andres Gomez Del Real if (!next_tss_desc.p || ((desc_limit < 0x67 && (next_tss_desc.type & 8)) || desc_limit < 0x2b)) { 167996feed4SSergio Andres Gomez Del Real VM_PANIC("emulate_ts"); 168996feed4SSergio Andres Gomez Del Real } 169996feed4SSergio Andres Gomez Del Real 170996feed4SSergio Andres Gomez Del Real if (reason == TSR_IRET || reason == TSR_JMP) { 171996feed4SSergio Andres Gomez Del Real curr_tss_desc.type &= ~(1 << 1); /* clear busy flag */ 172996feed4SSergio Andres Gomez Del Real x86_write_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel); 173996feed4SSergio Andres Gomez Del Real } 174996feed4SSergio Andres Gomez Del Real 175996feed4SSergio Andres Gomez Del Real if (reason == TSR_IRET) 176996feed4SSergio Andres Gomez Del Real EFLAGS(env) &= ~RFLAGS_NT; 177996feed4SSergio Andres Gomez Del Real 178996feed4SSergio Andres Gomez Del Real if (reason != TSR_CALL && reason != TSR_IDT_GATE) 179996feed4SSergio Andres Gomez Del Real old_tss_sel.sel = 0xffff; 180996feed4SSergio Andres Gomez Del Real 181996feed4SSergio Andres Gomez Del Real if (reason != TSR_IRET) { 182996feed4SSergio Andres Gomez Del Real next_tss_desc.type |= (1 << 1); /* set busy flag */ 183996feed4SSergio Andres Gomez Del Real x86_write_segment_descriptor(cpu, &next_tss_desc, tss_sel); 184996feed4SSergio Andres Gomez Del Real } 185996feed4SSergio Andres Gomez Del Real 186996feed4SSergio Andres Gomez Del Real if (next_tss_desc.type & 8) 187996feed4SSergio Andres Gomez Del Real ret = task_switch_32(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); 188996feed4SSergio Andres Gomez Del Real else 189996feed4SSergio Andres Gomez Del Real //ret = task_switch_16(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); 190996feed4SSergio Andres Gomez Del Real VM_PANIC("task_switch_16"); 191996feed4SSergio Andres Gomez Del Real 192996feed4SSergio Andres Gomez Del Real macvm_set_cr0(cpu->hvf_fd, rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0) | CR0_TS); 193996feed4SSergio Andres Gomez Del Real x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); 194*6701d81dSPaolo Bonzini vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); 195996feed4SSergio Andres Gomez Del Real 196996feed4SSergio Andres Gomez Del Real store_regs(cpu); 197996feed4SSergio Andres Gomez Del Real 198996feed4SSergio Andres Gomez Del Real hv_vcpu_invalidate_tlb(cpu->hvf_fd); 199996feed4SSergio Andres Gomez Del Real hv_vcpu_flush(cpu->hvf_fd); 200996feed4SSergio Andres Gomez Del Real } 201