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" 10895f9fdfSPaolo Bonzini #include "panic.h" 11996feed4SSergio Andres Gomez Del Real #include "qemu-common.h" 12996feed4SSergio Andres Gomez Del Real #include "qemu/error-report.h" 13996feed4SSergio Andres Gomez Del Real 14996feed4SSergio Andres Gomez Del Real #include "sysemu/hvf.h" 15996feed4SSergio Andres Gomez Del Real #include "hvf-i386.h" 1669e0a03cSPaolo Bonzini #include "vmcs.h" 1769e0a03cSPaolo Bonzini #include "vmx.h" 1869e0a03cSPaolo Bonzini #include "x86.h" 1969e0a03cSPaolo Bonzini #include "x86_descr.h" 2069e0a03cSPaolo Bonzini #include "x86_mmu.h" 2169e0a03cSPaolo Bonzini #include "x86_decode.h" 2269e0a03cSPaolo Bonzini #include "x86_emu.h" 2369e0a03cSPaolo Bonzini #include "x86_task.h" 2469e0a03cSPaolo Bonzini #include "x86hvf.h" 25996feed4SSergio Andres Gomez Del Real 26996feed4SSergio Andres Gomez Del Real #include <Hypervisor/hv.h> 27996feed4SSergio Andres Gomez Del Real #include <Hypervisor/hv_vmx.h> 28996feed4SSergio Andres Gomez Del Real 29996feed4SSergio Andres Gomez Del Real #include "hw/i386/apic_internal.h" 30996feed4SSergio Andres Gomez Del Real #include "qemu/main-loop.h" 31996feed4SSergio Andres Gomez Del Real #include "sysemu/accel.h" 32996feed4SSergio Andres Gomez Del Real #include "target/i386/cpu.h" 33996feed4SSergio Andres Gomez Del Real 34996feed4SSergio Andres Gomez Del Real // TODO: taskswitch handling 35996feed4SSergio Andres Gomez Del Real static void save_state_to_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) 36996feed4SSergio Andres Gomez Del Real { 37996feed4SSergio Andres Gomez Del Real X86CPU *x86_cpu = X86_CPU(cpu); 38996feed4SSergio Andres Gomez Del Real CPUX86State *env = &x86_cpu->env; 39996feed4SSergio Andres Gomez Del Real 40996feed4SSergio Andres Gomez Del Real /* CR3 and ldt selector are not saved intentionally */ 415d32173fSRoman Bolshakov tss->eip = (uint32_t)env->eip; 42*967f4da2SRoman Bolshakov tss->eflags = (uint32_t)env->eflags; 43996feed4SSergio Andres Gomez Del Real tss->eax = EAX(env); 44996feed4SSergio Andres Gomez Del Real tss->ecx = ECX(env); 45996feed4SSergio Andres Gomez Del Real tss->edx = EDX(env); 46996feed4SSergio Andres Gomez Del Real tss->ebx = EBX(env); 47996feed4SSergio Andres Gomez Del Real tss->esp = ESP(env); 48996feed4SSergio Andres Gomez Del Real tss->ebp = EBP(env); 49996feed4SSergio Andres Gomez Del Real tss->esi = ESI(env); 50996feed4SSergio Andres Gomez Del Real tss->edi = EDI(env); 51996feed4SSergio Andres Gomez Del Real 526701d81dSPaolo Bonzini tss->es = vmx_read_segment_selector(cpu, R_ES).sel; 536701d81dSPaolo Bonzini tss->cs = vmx_read_segment_selector(cpu, R_CS).sel; 546701d81dSPaolo Bonzini tss->ss = vmx_read_segment_selector(cpu, R_SS).sel; 556701d81dSPaolo Bonzini tss->ds = vmx_read_segment_selector(cpu, R_DS).sel; 566701d81dSPaolo Bonzini tss->fs = vmx_read_segment_selector(cpu, R_FS).sel; 576701d81dSPaolo Bonzini tss->gs = vmx_read_segment_selector(cpu, R_GS).sel; 58996feed4SSergio Andres Gomez Del Real } 59996feed4SSergio Andres Gomez Del Real 60996feed4SSergio Andres Gomez Del Real static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) 61996feed4SSergio Andres Gomez Del Real { 62996feed4SSergio Andres Gomez Del Real X86CPU *x86_cpu = X86_CPU(cpu); 63996feed4SSergio Andres Gomez Del Real CPUX86State *env = &x86_cpu->env; 64996feed4SSergio Andres Gomez Del Real 65996feed4SSergio Andres Gomez Del Real wvmcs(cpu->hvf_fd, VMCS_GUEST_CR3, tss->cr3); 66996feed4SSergio Andres Gomez Del Real 675d32173fSRoman Bolshakov env->eip = tss->eip; 68*967f4da2SRoman Bolshakov env->eflags = tss->eflags | 2; 69996feed4SSergio Andres Gomez Del Real 70996feed4SSergio Andres Gomez Del Real /* General purpose registers */ 71996feed4SSergio Andres Gomez Del Real RAX(env) = tss->eax; 72996feed4SSergio Andres Gomez Del Real RCX(env) = tss->ecx; 73996feed4SSergio Andres Gomez Del Real RDX(env) = tss->edx; 74996feed4SSergio Andres Gomez Del Real RBX(env) = tss->ebx; 75996feed4SSergio Andres Gomez Del Real RSP(env) = tss->esp; 76996feed4SSergio Andres Gomez Del Real RBP(env) = tss->ebp; 77996feed4SSergio Andres Gomez Del Real RSI(env) = tss->esi; 78996feed4SSergio Andres Gomez Del Real RDI(env) = tss->edi; 79996feed4SSergio Andres Gomez Del Real 806701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR); 816701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES); 826701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS); 836701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS); 846701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS); 856701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS); 866701d81dSPaolo Bonzini vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS); 87996feed4SSergio Andres Gomez Del Real } 88996feed4SSergio Andres Gomez Del Real 89996feed4SSergio Andres Gomez Del Real static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel, 90996feed4SSergio Andres Gomez Del Real uint64_t old_tss_base, struct x86_segment_descriptor *new_desc) 91996feed4SSergio Andres Gomez Del Real { 92996feed4SSergio Andres Gomez Del Real struct x86_tss_segment32 tss_seg; 93996feed4SSergio Andres Gomez Del Real uint32_t new_tss_base = x86_segment_base(new_desc); 94996feed4SSergio Andres Gomez Del Real uint32_t eip_offset = offsetof(struct x86_tss_segment32, eip); 95996feed4SSergio Andres Gomez Del Real uint32_t ldt_sel_offset = offsetof(struct x86_tss_segment32, ldt); 96996feed4SSergio Andres Gomez Del Real 97996feed4SSergio Andres Gomez Del Real vmx_read_mem(cpu, &tss_seg, old_tss_base, sizeof(tss_seg)); 98996feed4SSergio Andres Gomez Del Real save_state_to_tss32(cpu, &tss_seg); 99996feed4SSergio Andres Gomez Del Real 100996feed4SSergio Andres Gomez Del Real vmx_write_mem(cpu, old_tss_base + eip_offset, &tss_seg.eip, ldt_sel_offset - eip_offset); 101996feed4SSergio Andres Gomez Del Real vmx_read_mem(cpu, &tss_seg, new_tss_base, sizeof(tss_seg)); 102996feed4SSergio Andres Gomez Del Real 103996feed4SSergio Andres Gomez Del Real if (old_tss_sel.sel != 0xffff) { 104996feed4SSergio Andres Gomez Del Real tss_seg.prev_tss = old_tss_sel.sel; 105996feed4SSergio Andres Gomez Del Real 106996feed4SSergio Andres Gomez Del Real vmx_write_mem(cpu, new_tss_base, &tss_seg.prev_tss, sizeof(tss_seg.prev_tss)); 107996feed4SSergio Andres Gomez Del Real } 108996feed4SSergio Andres Gomez Del Real load_state_from_tss32(cpu, &tss_seg); 109996feed4SSergio Andres Gomez Del Real return 0; 110996feed4SSergio Andres Gomez Del Real } 111996feed4SSergio Andres Gomez Del Real 112996feed4SSergio 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) 113996feed4SSergio Andres Gomez Del Real { 114996feed4SSergio Andres Gomez Del Real uint64_t rip = rreg(cpu->hvf_fd, HV_X86_RIP); 115996feed4SSergio Andres Gomez Del Real if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && 116996feed4SSergio Andres Gomez Del Real gate_type != VMCS_INTR_T_HWINTR && 117996feed4SSergio Andres Gomez Del Real gate_type != VMCS_INTR_T_NMI)) { 118996feed4SSergio Andres Gomez Del Real int ins_len = rvmcs(cpu->hvf_fd, VMCS_EXIT_INSTRUCTION_LENGTH); 119996feed4SSergio Andres Gomez Del Real macvm_set_rip(cpu, rip + ins_len); 120996feed4SSergio Andres Gomez Del Real return; 121996feed4SSergio Andres Gomez Del Real } 122996feed4SSergio Andres Gomez Del Real 123996feed4SSergio Andres Gomez Del Real load_regs(cpu); 124996feed4SSergio Andres Gomez Del Real 125996feed4SSergio Andres Gomez Del Real struct x86_segment_descriptor curr_tss_desc, next_tss_desc; 126996feed4SSergio Andres Gomez Del Real int ret; 1276701d81dSPaolo Bonzini x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); 1286701d81dSPaolo Bonzini uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR); 129996feed4SSergio Andres Gomez Del Real uint32_t desc_limit; 130996feed4SSergio Andres Gomez Del Real struct x86_call_gate task_gate_desc; 131996feed4SSergio Andres Gomez Del Real struct vmx_segment vmx_seg; 132996feed4SSergio Andres Gomez Del Real 133996feed4SSergio Andres Gomez Del Real X86CPU *x86_cpu = X86_CPU(cpu); 134996feed4SSergio Andres Gomez Del Real CPUX86State *env = &x86_cpu->env; 135996feed4SSergio Andres Gomez Del Real 136996feed4SSergio Andres Gomez Del Real x86_read_segment_descriptor(cpu, &next_tss_desc, tss_sel); 137996feed4SSergio Andres Gomez Del Real x86_read_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel); 138996feed4SSergio Andres Gomez Del Real 139996feed4SSergio Andres Gomez Del Real if (reason == TSR_IDT_GATE && gate_valid) { 140996feed4SSergio Andres Gomez Del Real int dpl; 141996feed4SSergio Andres Gomez Del Real 142996feed4SSergio Andres Gomez Del Real ret = x86_read_call_gate(cpu, &task_gate_desc, gate); 143996feed4SSergio Andres Gomez Del Real 144996feed4SSergio Andres Gomez Del Real dpl = task_gate_desc.dpl; 1456701d81dSPaolo Bonzini x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); 146996feed4SSergio Andres Gomez Del Real if (tss_sel.rpl > dpl || cs.rpl > dpl) 147996feed4SSergio Andres Gomez Del Real ;//DPRINTF("emulate_gp"); 148996feed4SSergio Andres Gomez Del Real } 149996feed4SSergio Andres Gomez Del Real 150996feed4SSergio Andres Gomez Del Real desc_limit = x86_segment_limit(&next_tss_desc); 151996feed4SSergio Andres Gomez Del Real if (!next_tss_desc.p || ((desc_limit < 0x67 && (next_tss_desc.type & 8)) || desc_limit < 0x2b)) { 152996feed4SSergio Andres Gomez Del Real VM_PANIC("emulate_ts"); 153996feed4SSergio Andres Gomez Del Real } 154996feed4SSergio Andres Gomez Del Real 155996feed4SSergio Andres Gomez Del Real if (reason == TSR_IRET || reason == TSR_JMP) { 156996feed4SSergio Andres Gomez Del Real curr_tss_desc.type &= ~(1 << 1); /* clear busy flag */ 157996feed4SSergio Andres Gomez Del Real x86_write_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel); 158996feed4SSergio Andres Gomez Del Real } 159996feed4SSergio Andres Gomez Del Real 160996feed4SSergio Andres Gomez Del Real if (reason == TSR_IRET) 161*967f4da2SRoman Bolshakov env->eflags &= ~RFLAGS_NT; 162996feed4SSergio Andres Gomez Del Real 163996feed4SSergio Andres Gomez Del Real if (reason != TSR_CALL && reason != TSR_IDT_GATE) 164996feed4SSergio Andres Gomez Del Real old_tss_sel.sel = 0xffff; 165996feed4SSergio Andres Gomez Del Real 166996feed4SSergio Andres Gomez Del Real if (reason != TSR_IRET) { 167996feed4SSergio Andres Gomez Del Real next_tss_desc.type |= (1 << 1); /* set busy flag */ 168996feed4SSergio Andres Gomez Del Real x86_write_segment_descriptor(cpu, &next_tss_desc, tss_sel); 169996feed4SSergio Andres Gomez Del Real } 170996feed4SSergio Andres Gomez Del Real 171996feed4SSergio Andres Gomez Del Real if (next_tss_desc.type & 8) 172996feed4SSergio Andres Gomez Del Real ret = task_switch_32(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); 173996feed4SSergio Andres Gomez Del Real else 174996feed4SSergio Andres Gomez Del Real //ret = task_switch_16(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); 175996feed4SSergio Andres Gomez Del Real VM_PANIC("task_switch_16"); 176996feed4SSergio Andres Gomez Del Real 177996feed4SSergio Andres Gomez Del Real macvm_set_cr0(cpu->hvf_fd, rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0) | CR0_TS); 178996feed4SSergio Andres Gomez Del Real x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); 1796701d81dSPaolo Bonzini vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); 180996feed4SSergio Andres Gomez Del Real 181996feed4SSergio Andres Gomez Del Real store_regs(cpu); 182996feed4SSergio Andres Gomez Del Real 183996feed4SSergio Andres Gomez Del Real hv_vcpu_invalidate_tlb(cpu->hvf_fd); 184996feed4SSergio Andres Gomez Del Real hv_vcpu_flush(cpu->hvf_fd); 185996feed4SSergio Andres Gomez Del Real } 186