]>
Commit | Line | Data |
---|---|---|
996feed4 SAGDR |
1 | // This software is licensed under the terms of the GNU General Public |
2 | // License version 2, as published by the Free Software Foundation, and | |
3 | // may be copied, distributed, and modified under those terms. | |
4 | // | |
5 | // This program is distributed in the hope that it will be useful, | |
6 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
7 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
8 | // GNU General Public License for more details. | |
9 | #include "qemu/osdep.h" | |
895f9fdf | 10 | #include "panic.h" |
996feed4 SAGDR |
11 | #include "qemu/error-report.h" |
12 | ||
13 | #include "sysemu/hvf.h" | |
14 | #include "hvf-i386.h" | |
69e0a03c PB |
15 | #include "vmcs.h" |
16 | #include "vmx.h" | |
17 | #include "x86.h" | |
18 | #include "x86_descr.h" | |
19 | #include "x86_mmu.h" | |
20 | #include "x86_decode.h" | |
21 | #include "x86_emu.h" | |
22 | #include "x86_task.h" | |
23 | #include "x86hvf.h" | |
996feed4 SAGDR |
24 | |
25 | #include <Hypervisor/hv.h> | |
26 | #include <Hypervisor/hv_vmx.h> | |
27 | ||
996feed4 | 28 | #include "hw/i386/apic_internal.h" |
996feed4 | 29 | #include "qemu/main-loop.h" |
940e43aa | 30 | #include "qemu/accel.h" |
996feed4 SAGDR |
31 | #include "target/i386/cpu.h" |
32 | ||
33 | // TODO: taskswitch handling | |
34 | static void save_state_to_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) | |
35 | { | |
36 | X86CPU *x86_cpu = X86_CPU(cpu); | |
37 | CPUX86State *env = &x86_cpu->env; | |
38 | ||
39 | /* CR3 and ldt selector are not saved intentionally */ | |
5d32173f | 40 | tss->eip = (uint32_t)env->eip; |
967f4da2 | 41 | tss->eflags = (uint32_t)env->eflags; |
996feed4 SAGDR |
42 | tss->eax = EAX(env); |
43 | tss->ecx = ECX(env); | |
44 | tss->edx = EDX(env); | |
45 | tss->ebx = EBX(env); | |
46 | tss->esp = ESP(env); | |
47 | tss->ebp = EBP(env); | |
48 | tss->esi = ESI(env); | |
49 | tss->edi = EDI(env); | |
50 | ||
6701d81d PB |
51 | tss->es = vmx_read_segment_selector(cpu, R_ES).sel; |
52 | tss->cs = vmx_read_segment_selector(cpu, R_CS).sel; | |
53 | tss->ss = vmx_read_segment_selector(cpu, R_SS).sel; | |
54 | tss->ds = vmx_read_segment_selector(cpu, R_DS).sel; | |
55 | tss->fs = vmx_read_segment_selector(cpu, R_FS).sel; | |
56 | tss->gs = vmx_read_segment_selector(cpu, R_GS).sel; | |
996feed4 SAGDR |
57 | } |
58 | ||
59 | static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) | |
60 | { | |
61 | X86CPU *x86_cpu = X86_CPU(cpu); | |
62 | CPUX86State *env = &x86_cpu->env; | |
63 | ||
b533450e | 64 | wvmcs(cpu->hvf->fd, VMCS_GUEST_CR3, tss->cr3); |
996feed4 | 65 | |
5d32173f | 66 | env->eip = tss->eip; |
967f4da2 | 67 | env->eflags = tss->eflags | 2; |
996feed4 SAGDR |
68 | |
69 | /* General purpose registers */ | |
70 | RAX(env) = tss->eax; | |
71 | RCX(env) = tss->ecx; | |
72 | RDX(env) = tss->edx; | |
73 | RBX(env) = tss->ebx; | |
74 | RSP(env) = tss->esp; | |
75 | RBP(env) = tss->ebp; | |
76 | RSI(env) = tss->esi; | |
77 | RDI(env) = tss->edi; | |
78 | ||
6701d81d PB |
79 | vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR); |
80 | vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES); | |
81 | vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS); | |
82 | vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS); | |
83 | vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS); | |
84 | vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS); | |
85 | vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS); | |
996feed4 SAGDR |
86 | } |
87 | ||
88 | static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel, | |
89 | uint64_t old_tss_base, struct x86_segment_descriptor *new_desc) | |
90 | { | |
91 | struct x86_tss_segment32 tss_seg; | |
92 | uint32_t new_tss_base = x86_segment_base(new_desc); | |
93 | uint32_t eip_offset = offsetof(struct x86_tss_segment32, eip); | |
94 | uint32_t ldt_sel_offset = offsetof(struct x86_tss_segment32, ldt); | |
95 | ||
96 | vmx_read_mem(cpu, &tss_seg, old_tss_base, sizeof(tss_seg)); | |
97 | save_state_to_tss32(cpu, &tss_seg); | |
98 | ||
99 | vmx_write_mem(cpu, old_tss_base + eip_offset, &tss_seg.eip, ldt_sel_offset - eip_offset); | |
100 | vmx_read_mem(cpu, &tss_seg, new_tss_base, sizeof(tss_seg)); | |
101 | ||
102 | if (old_tss_sel.sel != 0xffff) { | |
103 | tss_seg.prev_tss = old_tss_sel.sel; | |
104 | ||
105 | vmx_write_mem(cpu, new_tss_base, &tss_seg.prev_tss, sizeof(tss_seg.prev_tss)); | |
106 | } | |
107 | load_state_from_tss32(cpu, &tss_seg); | |
108 | return 0; | |
109 | } | |
110 | ||
111 | void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) | |
112 | { | |
b533450e | 113 | uint64_t rip = rreg(cpu->hvf->fd, HV_X86_RIP); |
996feed4 SAGDR |
114 | if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && |
115 | gate_type != VMCS_INTR_T_HWINTR && | |
116 | gate_type != VMCS_INTR_T_NMI)) { | |
b533450e | 117 | int ins_len = rvmcs(cpu->hvf->fd, VMCS_EXIT_INSTRUCTION_LENGTH); |
996feed4 SAGDR |
118 | macvm_set_rip(cpu, rip + ins_len); |
119 | return; | |
120 | } | |
121 | ||
122 | load_regs(cpu); | |
123 | ||
124 | struct x86_segment_descriptor curr_tss_desc, next_tss_desc; | |
125 | int ret; | |
6701d81d PB |
126 | x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); |
127 | uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR); | |
996feed4 SAGDR |
128 | uint32_t desc_limit; |
129 | struct x86_call_gate task_gate_desc; | |
130 | struct vmx_segment vmx_seg; | |
131 | ||
132 | X86CPU *x86_cpu = X86_CPU(cpu); | |
133 | CPUX86State *env = &x86_cpu->env; | |
134 | ||
135 | x86_read_segment_descriptor(cpu, &next_tss_desc, tss_sel); | |
136 | x86_read_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel); | |
137 | ||
138 | if (reason == TSR_IDT_GATE && gate_valid) { | |
139 | int dpl; | |
140 | ||
141 | ret = x86_read_call_gate(cpu, &task_gate_desc, gate); | |
142 | ||
143 | dpl = task_gate_desc.dpl; | |
6701d81d | 144 | x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); |
996feed4 SAGDR |
145 | if (tss_sel.rpl > dpl || cs.rpl > dpl) |
146 | ;//DPRINTF("emulate_gp"); | |
147 | } | |
148 | ||
149 | desc_limit = x86_segment_limit(&next_tss_desc); | |
150 | if (!next_tss_desc.p || ((desc_limit < 0x67 && (next_tss_desc.type & 8)) || desc_limit < 0x2b)) { | |
151 | VM_PANIC("emulate_ts"); | |
152 | } | |
153 | ||
154 | if (reason == TSR_IRET || reason == TSR_JMP) { | |
155 | curr_tss_desc.type &= ~(1 << 1); /* clear busy flag */ | |
156 | x86_write_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel); | |
157 | } | |
158 | ||
159 | if (reason == TSR_IRET) | |
ea48ae91 | 160 | env->eflags &= ~NT_MASK; |
996feed4 SAGDR |
161 | |
162 | if (reason != TSR_CALL && reason != TSR_IDT_GATE) | |
163 | old_tss_sel.sel = 0xffff; | |
164 | ||
165 | if (reason != TSR_IRET) { | |
166 | next_tss_desc.type |= (1 << 1); /* set busy flag */ | |
167 | x86_write_segment_descriptor(cpu, &next_tss_desc, tss_sel); | |
168 | } | |
169 | ||
170 | if (next_tss_desc.type & 8) | |
171 | ret = task_switch_32(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); | |
172 | else | |
173 | //ret = task_switch_16(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); | |
174 | VM_PANIC("task_switch_16"); | |
175 | ||
704afe34 CE |
176 | macvm_set_cr0(cpu->hvf->fd, rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0) | |
177 | CR0_TS_MASK); | |
996feed4 | 178 | x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); |
6701d81d | 179 | vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); |
996feed4 SAGDR |
180 | |
181 | store_regs(cpu); | |
182 | ||
b533450e | 183 | hv_vcpu_invalidate_tlb(cpu->hvf->fd); |
996feed4 | 184 | } |