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