root / hw / i386 / kvmvapic.c @ 0445259b
History | View | Annotate | Download (22.9 kB)
1 | e5ad936b | Jan Kiszka | /*
|
---|---|---|---|
2 | e5ad936b | Jan Kiszka | * TPR optimization for 32-bit Windows guests (XP and Server 2003)
|
3 | e5ad936b | Jan Kiszka | *
|
4 | e5ad936b | Jan Kiszka | * Copyright (C) 2007-2008 Qumranet Technologies
|
5 | e5ad936b | Jan Kiszka | * Copyright (C) 2012 Jan Kiszka, Siemens AG
|
6 | e5ad936b | Jan Kiszka | *
|
7 | e5ad936b | Jan Kiszka | * This work is licensed under the terms of the GNU GPL version 2, or
|
8 | e5ad936b | Jan Kiszka | * (at your option) any later version. See the COPYING file in the
|
9 | e5ad936b | Jan Kiszka | * top-level directory.
|
10 | e5ad936b | Jan Kiszka | */
|
11 | 9c17d615 | Paolo Bonzini | #include "sysemu/sysemu.h" |
12 | 9c17d615 | Paolo Bonzini | #include "sysemu/cpus.h" |
13 | 9c17d615 | Paolo Bonzini | #include "sysemu/kvm.h" |
14 | 0d09e41a | Paolo Bonzini | #include "hw/i386/apic_internal.h" |
15 | e5ad936b | Jan Kiszka | |
16 | e5ad936b | Jan Kiszka | #define VAPIC_IO_PORT 0x7e |
17 | e5ad936b | Jan Kiszka | |
18 | e5ad936b | Jan Kiszka | #define VAPIC_CPU_SHIFT 7 |
19 | e5ad936b | Jan Kiszka | |
20 | e5ad936b | Jan Kiszka | #define ROM_BLOCK_SIZE 512 |
21 | e5ad936b | Jan Kiszka | #define ROM_BLOCK_MASK (~(ROM_BLOCK_SIZE - 1)) |
22 | e5ad936b | Jan Kiszka | |
23 | e5ad936b | Jan Kiszka | typedef enum VAPICMode { |
24 | e5ad936b | Jan Kiszka | VAPIC_INACTIVE = 0,
|
25 | e5ad936b | Jan Kiszka | VAPIC_ACTIVE = 1,
|
26 | e5ad936b | Jan Kiszka | VAPIC_STANDBY = 2,
|
27 | e5ad936b | Jan Kiszka | } VAPICMode; |
28 | e5ad936b | Jan Kiszka | |
29 | e5ad936b | Jan Kiszka | typedef struct VAPICHandlers { |
30 | e5ad936b | Jan Kiszka | uint32_t set_tpr; |
31 | e5ad936b | Jan Kiszka | uint32_t set_tpr_eax; |
32 | e5ad936b | Jan Kiszka | uint32_t get_tpr[8];
|
33 | e5ad936b | Jan Kiszka | uint32_t get_tpr_stack; |
34 | e5ad936b | Jan Kiszka | } QEMU_PACKED VAPICHandlers; |
35 | e5ad936b | Jan Kiszka | |
36 | e5ad936b | Jan Kiszka | typedef struct GuestROMState { |
37 | e5ad936b | Jan Kiszka | char signature[8]; |
38 | e5ad936b | Jan Kiszka | uint32_t vaddr; |
39 | e5ad936b | Jan Kiszka | uint32_t fixup_start; |
40 | e5ad936b | Jan Kiszka | uint32_t fixup_end; |
41 | e5ad936b | Jan Kiszka | uint32_t vapic_vaddr; |
42 | e5ad936b | Jan Kiszka | uint32_t vapic_size; |
43 | e5ad936b | Jan Kiszka | uint32_t vcpu_shift; |
44 | e5ad936b | Jan Kiszka | uint32_t real_tpr_addr; |
45 | e5ad936b | Jan Kiszka | VAPICHandlers up; |
46 | e5ad936b | Jan Kiszka | VAPICHandlers mp; |
47 | e5ad936b | Jan Kiszka | } QEMU_PACKED GuestROMState; |
48 | e5ad936b | Jan Kiszka | |
49 | e5ad936b | Jan Kiszka | typedef struct VAPICROMState { |
50 | e5ad936b | Jan Kiszka | SysBusDevice busdev; |
51 | e5ad936b | Jan Kiszka | MemoryRegion io; |
52 | e5ad936b | Jan Kiszka | MemoryRegion rom; |
53 | e5ad936b | Jan Kiszka | uint32_t state; |
54 | e5ad936b | Jan Kiszka | uint32_t rom_state_paddr; |
55 | e5ad936b | Jan Kiszka | uint32_t rom_state_vaddr; |
56 | e5ad936b | Jan Kiszka | uint32_t vapic_paddr; |
57 | e5ad936b | Jan Kiszka | uint32_t real_tpr_addr; |
58 | e5ad936b | Jan Kiszka | GuestROMState rom_state; |
59 | e5ad936b | Jan Kiszka | size_t rom_size; |
60 | e5ad936b | Jan Kiszka | bool rom_mapped_writable;
|
61 | e5ad936b | Jan Kiszka | } VAPICROMState; |
62 | e5ad936b | Jan Kiszka | |
63 | e5ad936b | Jan Kiszka | #define TPR_INSTR_ABS_MODRM 0x1 |
64 | e5ad936b | Jan Kiszka | #define TPR_INSTR_MATCH_MODRM_REG 0x2 |
65 | e5ad936b | Jan Kiszka | |
66 | e5ad936b | Jan Kiszka | typedef struct TPRInstruction { |
67 | e5ad936b | Jan Kiszka | uint8_t opcode; |
68 | e5ad936b | Jan Kiszka | uint8_t modrm_reg; |
69 | e5ad936b | Jan Kiszka | unsigned int flags; |
70 | e5ad936b | Jan Kiszka | TPRAccess access; |
71 | e5ad936b | Jan Kiszka | size_t length; |
72 | e5ad936b | Jan Kiszka | off_t addr_offset; |
73 | e5ad936b | Jan Kiszka | } TPRInstruction; |
74 | e5ad936b | Jan Kiszka | |
75 | e5ad936b | Jan Kiszka | /* must be sorted by length, shortest first */
|
76 | e5ad936b | Jan Kiszka | static const TPRInstruction tpr_instr[] = { |
77 | e5ad936b | Jan Kiszka | { /* mov abs to eax */
|
78 | e5ad936b | Jan Kiszka | .opcode = 0xa1,
|
79 | e5ad936b | Jan Kiszka | .access = TPR_ACCESS_READ, |
80 | e5ad936b | Jan Kiszka | .length = 5,
|
81 | e5ad936b | Jan Kiszka | .addr_offset = 1,
|
82 | e5ad936b | Jan Kiszka | }, |
83 | e5ad936b | Jan Kiszka | { /* mov eax to abs */
|
84 | e5ad936b | Jan Kiszka | .opcode = 0xa3,
|
85 | e5ad936b | Jan Kiszka | .access = TPR_ACCESS_WRITE, |
86 | e5ad936b | Jan Kiszka | .length = 5,
|
87 | e5ad936b | Jan Kiszka | .addr_offset = 1,
|
88 | e5ad936b | Jan Kiszka | }, |
89 | e5ad936b | Jan Kiszka | { /* mov r32 to r/m32 */
|
90 | e5ad936b | Jan Kiszka | .opcode = 0x89,
|
91 | e5ad936b | Jan Kiszka | .flags = TPR_INSTR_ABS_MODRM, |
92 | e5ad936b | Jan Kiszka | .access = TPR_ACCESS_WRITE, |
93 | e5ad936b | Jan Kiszka | .length = 6,
|
94 | e5ad936b | Jan Kiszka | .addr_offset = 2,
|
95 | e5ad936b | Jan Kiszka | }, |
96 | e5ad936b | Jan Kiszka | { /* mov r/m32 to r32 */
|
97 | e5ad936b | Jan Kiszka | .opcode = 0x8b,
|
98 | e5ad936b | Jan Kiszka | .flags = TPR_INSTR_ABS_MODRM, |
99 | e5ad936b | Jan Kiszka | .access = TPR_ACCESS_READ, |
100 | e5ad936b | Jan Kiszka | .length = 6,
|
101 | e5ad936b | Jan Kiszka | .addr_offset = 2,
|
102 | e5ad936b | Jan Kiszka | }, |
103 | e5ad936b | Jan Kiszka | { /* push r/m32 */
|
104 | e5ad936b | Jan Kiszka | .opcode = 0xff,
|
105 | e5ad936b | Jan Kiszka | .modrm_reg = 6,
|
106 | e5ad936b | Jan Kiszka | .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, |
107 | e5ad936b | Jan Kiszka | .access = TPR_ACCESS_READ, |
108 | e5ad936b | Jan Kiszka | .length = 6,
|
109 | e5ad936b | Jan Kiszka | .addr_offset = 2,
|
110 | e5ad936b | Jan Kiszka | }, |
111 | e5ad936b | Jan Kiszka | { /* mov imm32, r/m32 (c7/0) */
|
112 | e5ad936b | Jan Kiszka | .opcode = 0xc7,
|
113 | e5ad936b | Jan Kiszka | .modrm_reg = 0,
|
114 | e5ad936b | Jan Kiszka | .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, |
115 | e5ad936b | Jan Kiszka | .access = TPR_ACCESS_WRITE, |
116 | e5ad936b | Jan Kiszka | .length = 10,
|
117 | e5ad936b | Jan Kiszka | .addr_offset = 2,
|
118 | e5ad936b | Jan Kiszka | }, |
119 | e5ad936b | Jan Kiszka | }; |
120 | e5ad936b | Jan Kiszka | |
121 | e5ad936b | Jan Kiszka | static void read_guest_rom_state(VAPICROMState *s) |
122 | e5ad936b | Jan Kiszka | { |
123 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state,
|
124 | e5ad936b | Jan Kiszka | sizeof(GuestROMState), 0); |
125 | e5ad936b | Jan Kiszka | } |
126 | e5ad936b | Jan Kiszka | |
127 | e5ad936b | Jan Kiszka | static void write_guest_rom_state(VAPICROMState *s) |
128 | e5ad936b | Jan Kiszka | { |
129 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state,
|
130 | e5ad936b | Jan Kiszka | sizeof(GuestROMState), 1); |
131 | e5ad936b | Jan Kiszka | } |
132 | e5ad936b | Jan Kiszka | |
133 | e5ad936b | Jan Kiszka | static void update_guest_rom_state(VAPICROMState *s) |
134 | e5ad936b | Jan Kiszka | { |
135 | e5ad936b | Jan Kiszka | read_guest_rom_state(s); |
136 | e5ad936b | Jan Kiszka | |
137 | e5ad936b | Jan Kiszka | s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr); |
138 | e5ad936b | Jan Kiszka | s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT); |
139 | e5ad936b | Jan Kiszka | |
140 | e5ad936b | Jan Kiszka | write_guest_rom_state(s); |
141 | e5ad936b | Jan Kiszka | } |
142 | e5ad936b | Jan Kiszka | |
143 | 4a8fa5dc | Andreas Färber | static int find_real_tpr_addr(VAPICROMState *s, CPUX86State *env) |
144 | e5ad936b | Jan Kiszka | { |
145 | a8170e5e | Avi Kivity | hwaddr paddr; |
146 | e5ad936b | Jan Kiszka | target_ulong addr; |
147 | e5ad936b | Jan Kiszka | |
148 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_ACTIVE) {
|
149 | e5ad936b | Jan Kiszka | return 0; |
150 | e5ad936b | Jan Kiszka | } |
151 | e5ad936b | Jan Kiszka | /*
|
152 | e5ad936b | Jan Kiszka | * If there is no prior TPR access instruction we could analyze (which is
|
153 | e5ad936b | Jan Kiszka | * the case after resume from hibernation), we need to scan the possible
|
154 | e5ad936b | Jan Kiszka | * virtual address space for the APIC mapping.
|
155 | e5ad936b | Jan Kiszka | */
|
156 | e5ad936b | Jan Kiszka | for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) { |
157 | e5ad936b | Jan Kiszka | paddr = cpu_get_phys_page_debug(env, addr); |
158 | e5ad936b | Jan Kiszka | if (paddr != APIC_DEFAULT_ADDRESS) {
|
159 | e5ad936b | Jan Kiszka | continue;
|
160 | e5ad936b | Jan Kiszka | } |
161 | e5ad936b | Jan Kiszka | s->real_tpr_addr = addr + 0x80;
|
162 | e5ad936b | Jan Kiszka | update_guest_rom_state(s); |
163 | e5ad936b | Jan Kiszka | return 0; |
164 | e5ad936b | Jan Kiszka | } |
165 | e5ad936b | Jan Kiszka | return -1; |
166 | e5ad936b | Jan Kiszka | } |
167 | e5ad936b | Jan Kiszka | |
168 | e5ad936b | Jan Kiszka | static uint8_t modrm_reg(uint8_t modrm)
|
169 | e5ad936b | Jan Kiszka | { |
170 | e5ad936b | Jan Kiszka | return (modrm >> 3) & 7; |
171 | e5ad936b | Jan Kiszka | } |
172 | e5ad936b | Jan Kiszka | |
173 | e5ad936b | Jan Kiszka | static bool is_abs_modrm(uint8_t modrm) |
174 | e5ad936b | Jan Kiszka | { |
175 | e5ad936b | Jan Kiszka | return (modrm & 0xc7) == 0x05; |
176 | e5ad936b | Jan Kiszka | } |
177 | e5ad936b | Jan Kiszka | |
178 | e5ad936b | Jan Kiszka | static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr) |
179 | e5ad936b | Jan Kiszka | { |
180 | e5ad936b | Jan Kiszka | return opcode[0] == instr->opcode && |
181 | e5ad936b | Jan Kiszka | (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) &&
|
182 | e5ad936b | Jan Kiszka | (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) || |
183 | e5ad936b | Jan Kiszka | modrm_reg(opcode[1]) == instr->modrm_reg);
|
184 | e5ad936b | Jan Kiszka | } |
185 | e5ad936b | Jan Kiszka | |
186 | 4a8fa5dc | Andreas Färber | static int evaluate_tpr_instruction(VAPICROMState *s, CPUX86State *env, |
187 | e5ad936b | Jan Kiszka | target_ulong *pip, TPRAccess access) |
188 | e5ad936b | Jan Kiszka | { |
189 | e5ad936b | Jan Kiszka | const TPRInstruction *instr;
|
190 | e5ad936b | Jan Kiszka | target_ulong ip = *pip; |
191 | e5ad936b | Jan Kiszka | uint8_t opcode[2];
|
192 | e5ad936b | Jan Kiszka | uint32_t real_tpr_addr; |
193 | e5ad936b | Jan Kiszka | int i;
|
194 | e5ad936b | Jan Kiszka | |
195 | e5ad936b | Jan Kiszka | if ((ip & 0xf0000000ULL) != 0x80000000ULL && |
196 | e5ad936b | Jan Kiszka | (ip & 0xf0000000ULL) != 0xe0000000ULL) { |
197 | e5ad936b | Jan Kiszka | return -1; |
198 | e5ad936b | Jan Kiszka | } |
199 | e5ad936b | Jan Kiszka | |
200 | e5ad936b | Jan Kiszka | /*
|
201 | e5ad936b | Jan Kiszka | * Early Windows 2003 SMP initialization contains a
|
202 | e5ad936b | Jan Kiszka | *
|
203 | e5ad936b | Jan Kiszka | * mov imm32, r/m32
|
204 | e5ad936b | Jan Kiszka | *
|
205 | e5ad936b | Jan Kiszka | * instruction that is patched by TPR optimization. The problem is that
|
206 | e5ad936b | Jan Kiszka | * RSP, used by the patched instruction, is zero, so the guest gets a
|
207 | e5ad936b | Jan Kiszka | * double fault and dies.
|
208 | e5ad936b | Jan Kiszka | */
|
209 | e5ad936b | Jan Kiszka | if (env->regs[R_ESP] == 0) { |
210 | e5ad936b | Jan Kiszka | return -1; |
211 | e5ad936b | Jan Kiszka | } |
212 | e5ad936b | Jan Kiszka | |
213 | e5ad936b | Jan Kiszka | if (kvm_enabled() && !kvm_irqchip_in_kernel()) {
|
214 | e5ad936b | Jan Kiszka | /*
|
215 | e5ad936b | Jan Kiszka | * KVM without kernel-based TPR access reporting will pass an IP that
|
216 | e5ad936b | Jan Kiszka | * points after the accessing instruction. So we need to look backward
|
217 | e5ad936b | Jan Kiszka | * to find the reason.
|
218 | e5ad936b | Jan Kiszka | */
|
219 | e5ad936b | Jan Kiszka | for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { |
220 | e5ad936b | Jan Kiszka | instr = &tpr_instr[i]; |
221 | e5ad936b | Jan Kiszka | if (instr->access != access) {
|
222 | e5ad936b | Jan Kiszka | continue;
|
223 | e5ad936b | Jan Kiszka | } |
224 | e5ad936b | Jan Kiszka | if (cpu_memory_rw_debug(env, ip - instr->length, opcode,
|
225 | e5ad936b | Jan Kiszka | sizeof(opcode), 0) < 0) { |
226 | e5ad936b | Jan Kiszka | return -1; |
227 | e5ad936b | Jan Kiszka | } |
228 | e5ad936b | Jan Kiszka | if (opcode_matches(opcode, instr)) {
|
229 | e5ad936b | Jan Kiszka | ip -= instr->length; |
230 | e5ad936b | Jan Kiszka | goto instruction_ok;
|
231 | e5ad936b | Jan Kiszka | } |
232 | e5ad936b | Jan Kiszka | } |
233 | e5ad936b | Jan Kiszka | return -1; |
234 | e5ad936b | Jan Kiszka | } else {
|
235 | e5ad936b | Jan Kiszka | if (cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0) < 0) { |
236 | e5ad936b | Jan Kiszka | return -1; |
237 | e5ad936b | Jan Kiszka | } |
238 | e5ad936b | Jan Kiszka | for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { |
239 | e5ad936b | Jan Kiszka | instr = &tpr_instr[i]; |
240 | e5ad936b | Jan Kiszka | if (opcode_matches(opcode, instr)) {
|
241 | e5ad936b | Jan Kiszka | goto instruction_ok;
|
242 | e5ad936b | Jan Kiszka | } |
243 | e5ad936b | Jan Kiszka | } |
244 | e5ad936b | Jan Kiszka | return -1; |
245 | e5ad936b | Jan Kiszka | } |
246 | e5ad936b | Jan Kiszka | |
247 | e5ad936b | Jan Kiszka | instruction_ok:
|
248 | e5ad936b | Jan Kiszka | /*
|
249 | e5ad936b | Jan Kiszka | * Grab the virtual TPR address from the instruction
|
250 | e5ad936b | Jan Kiszka | * and update the cached values.
|
251 | e5ad936b | Jan Kiszka | */
|
252 | e5ad936b | Jan Kiszka | if (cpu_memory_rw_debug(env, ip + instr->addr_offset,
|
253 | e5ad936b | Jan Kiszka | (void *)&real_tpr_addr,
|
254 | e5ad936b | Jan Kiszka | sizeof(real_tpr_addr), 0) < 0) { |
255 | e5ad936b | Jan Kiszka | return -1; |
256 | e5ad936b | Jan Kiszka | } |
257 | e5ad936b | Jan Kiszka | real_tpr_addr = le32_to_cpu(real_tpr_addr); |
258 | e5ad936b | Jan Kiszka | if ((real_tpr_addr & 0xfff) != 0x80) { |
259 | e5ad936b | Jan Kiszka | return -1; |
260 | e5ad936b | Jan Kiszka | } |
261 | e5ad936b | Jan Kiszka | s->real_tpr_addr = real_tpr_addr; |
262 | e5ad936b | Jan Kiszka | update_guest_rom_state(s); |
263 | e5ad936b | Jan Kiszka | |
264 | e5ad936b | Jan Kiszka | *pip = ip; |
265 | e5ad936b | Jan Kiszka | return 0; |
266 | e5ad936b | Jan Kiszka | } |
267 | e5ad936b | Jan Kiszka | |
268 | 4a8fa5dc | Andreas Färber | static int update_rom_mapping(VAPICROMState *s, CPUX86State *env, target_ulong ip) |
269 | e5ad936b | Jan Kiszka | { |
270 | a8170e5e | Avi Kivity | hwaddr paddr; |
271 | e5ad936b | Jan Kiszka | uint32_t rom_state_vaddr; |
272 | e5ad936b | Jan Kiszka | uint32_t pos, patch, offset; |
273 | e5ad936b | Jan Kiszka | |
274 | e5ad936b | Jan Kiszka | /* nothing to do if already activated */
|
275 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_ACTIVE) {
|
276 | e5ad936b | Jan Kiszka | return 0; |
277 | e5ad936b | Jan Kiszka | } |
278 | e5ad936b | Jan Kiszka | |
279 | e5ad936b | Jan Kiszka | /* bail out if ROM init code was not executed (missing ROM?) */
|
280 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_INACTIVE) {
|
281 | e5ad936b | Jan Kiszka | return -1; |
282 | e5ad936b | Jan Kiszka | } |
283 | e5ad936b | Jan Kiszka | |
284 | e5ad936b | Jan Kiszka | /* find out virtual address of the ROM */
|
285 | e5ad936b | Jan Kiszka | rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000);
|
286 | e5ad936b | Jan Kiszka | paddr = cpu_get_phys_page_debug(env, rom_state_vaddr); |
287 | e5ad936b | Jan Kiszka | if (paddr == -1) { |
288 | e5ad936b | Jan Kiszka | return -1; |
289 | e5ad936b | Jan Kiszka | } |
290 | e5ad936b | Jan Kiszka | paddr += rom_state_vaddr & ~TARGET_PAGE_MASK; |
291 | e5ad936b | Jan Kiszka | if (paddr != s->rom_state_paddr) {
|
292 | e5ad936b | Jan Kiszka | return -1; |
293 | e5ad936b | Jan Kiszka | } |
294 | e5ad936b | Jan Kiszka | read_guest_rom_state(s); |
295 | e5ad936b | Jan Kiszka | if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) { |
296 | e5ad936b | Jan Kiszka | return -1; |
297 | e5ad936b | Jan Kiszka | } |
298 | e5ad936b | Jan Kiszka | s->rom_state_vaddr = rom_state_vaddr; |
299 | e5ad936b | Jan Kiszka | |
300 | e5ad936b | Jan Kiszka | /* fixup addresses in ROM if needed */
|
301 | e5ad936b | Jan Kiszka | if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) {
|
302 | e5ad936b | Jan Kiszka | return 0; |
303 | e5ad936b | Jan Kiszka | } |
304 | e5ad936b | Jan Kiszka | for (pos = le32_to_cpu(s->rom_state.fixup_start);
|
305 | e5ad936b | Jan Kiszka | pos < le32_to_cpu(s->rom_state.fixup_end); |
306 | e5ad936b | Jan Kiszka | pos += 4) {
|
307 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(paddr + pos - s->rom_state.vaddr, |
308 | e5ad936b | Jan Kiszka | (void *)&offset, sizeof(offset), 0); |
309 | e5ad936b | Jan Kiszka | offset = le32_to_cpu(offset); |
310 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(paddr + offset, (void *)&patch,
|
311 | e5ad936b | Jan Kiszka | sizeof(patch), 0); |
312 | e5ad936b | Jan Kiszka | patch = le32_to_cpu(patch); |
313 | e5ad936b | Jan Kiszka | patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr); |
314 | e5ad936b | Jan Kiszka | patch = cpu_to_le32(patch); |
315 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(paddr + offset, (void *)&patch,
|
316 | e5ad936b | Jan Kiszka | sizeof(patch), 1); |
317 | e5ad936b | Jan Kiszka | } |
318 | e5ad936b | Jan Kiszka | read_guest_rom_state(s); |
319 | e5ad936b | Jan Kiszka | s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) - |
320 | e5ad936b | Jan Kiszka | le32_to_cpu(s->rom_state.vaddr); |
321 | e5ad936b | Jan Kiszka | |
322 | e5ad936b | Jan Kiszka | return 0; |
323 | e5ad936b | Jan Kiszka | } |
324 | e5ad936b | Jan Kiszka | |
325 | e5ad936b | Jan Kiszka | /*
|
326 | e5ad936b | Jan Kiszka | * Tries to read the unique processor number from the Kernel Processor Control
|
327 | e5ad936b | Jan Kiszka | * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR
|
328 | e5ad936b | Jan Kiszka | * cannot be accessed or is considered invalid. This also ensures that we are
|
329 | e5ad936b | Jan Kiszka | * not patching the wrong guest.
|
330 | e5ad936b | Jan Kiszka | */
|
331 | 4a8fa5dc | Andreas Färber | static int get_kpcr_number(CPUX86State *env) |
332 | e5ad936b | Jan Kiszka | { |
333 | e5ad936b | Jan Kiszka | struct kpcr {
|
334 | e5ad936b | Jan Kiszka | uint8_t fill1[0x1c];
|
335 | e5ad936b | Jan Kiszka | uint32_t self; |
336 | e5ad936b | Jan Kiszka | uint8_t fill2[0x31];
|
337 | e5ad936b | Jan Kiszka | uint8_t number; |
338 | e5ad936b | Jan Kiszka | } QEMU_PACKED kpcr; |
339 | e5ad936b | Jan Kiszka | |
340 | e5ad936b | Jan Kiszka | if (cpu_memory_rw_debug(env, env->segs[R_FS].base,
|
341 | e5ad936b | Jan Kiszka | (void *)&kpcr, sizeof(kpcr), 0) < 0 || |
342 | e5ad936b | Jan Kiszka | kpcr.self != env->segs[R_FS].base) { |
343 | e5ad936b | Jan Kiszka | return -1; |
344 | e5ad936b | Jan Kiszka | } |
345 | e5ad936b | Jan Kiszka | return kpcr.number;
|
346 | e5ad936b | Jan Kiszka | } |
347 | e5ad936b | Jan Kiszka | |
348 | 4a8fa5dc | Andreas Färber | static int vapic_enable(VAPICROMState *s, CPUX86State *env) |
349 | e5ad936b | Jan Kiszka | { |
350 | e5ad936b | Jan Kiszka | int cpu_number = get_kpcr_number(env);
|
351 | a8170e5e | Avi Kivity | hwaddr vapic_paddr; |
352 | e5ad936b | Jan Kiszka | static const uint8_t enabled = 1; |
353 | e5ad936b | Jan Kiszka | |
354 | e5ad936b | Jan Kiszka | if (cpu_number < 0) { |
355 | e5ad936b | Jan Kiszka | return -1; |
356 | e5ad936b | Jan Kiszka | } |
357 | e5ad936b | Jan Kiszka | vapic_paddr = s->vapic_paddr + |
358 | a8170e5e | Avi Kivity | (((hwaddr)cpu_number) << VAPIC_CPU_SHIFT); |
359 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(vapic_paddr + offsetof(VAPICState, enabled), |
360 | e5ad936b | Jan Kiszka | (void *)&enabled, sizeof(enabled), 1); |
361 | e5ad936b | Jan Kiszka | apic_enable_vapic(env->apic_state, vapic_paddr); |
362 | e5ad936b | Jan Kiszka | |
363 | e5ad936b | Jan Kiszka | s->state = VAPIC_ACTIVE; |
364 | e5ad936b | Jan Kiszka | |
365 | e5ad936b | Jan Kiszka | return 0; |
366 | e5ad936b | Jan Kiszka | } |
367 | e5ad936b | Jan Kiszka | |
368 | 4a8fa5dc | Andreas Färber | static void patch_byte(CPUX86State *env, target_ulong addr, uint8_t byte) |
369 | e5ad936b | Jan Kiszka | { |
370 | e5ad936b | Jan Kiszka | cpu_memory_rw_debug(env, addr, &byte, 1, 1); |
371 | e5ad936b | Jan Kiszka | } |
372 | e5ad936b | Jan Kiszka | |
373 | 4a8fa5dc | Andreas Färber | static void patch_call(VAPICROMState *s, CPUX86State *env, target_ulong ip, |
374 | e5ad936b | Jan Kiszka | uint32_t target) |
375 | e5ad936b | Jan Kiszka | { |
376 | e5ad936b | Jan Kiszka | uint32_t offset; |
377 | e5ad936b | Jan Kiszka | |
378 | e5ad936b | Jan Kiszka | offset = cpu_to_le32(target - ip - 5);
|
379 | e5ad936b | Jan Kiszka | patch_byte(env, ip, 0xe8); /* call near */ |
380 | e5ad936b | Jan Kiszka | cpu_memory_rw_debug(env, ip + 1, (void *)&offset, sizeof(offset), 1); |
381 | e5ad936b | Jan Kiszka | } |
382 | e5ad936b | Jan Kiszka | |
383 | d77953b9 | Andreas Färber | static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) |
384 | e5ad936b | Jan Kiszka | { |
385 | d77953b9 | Andreas Färber | CPUState *cs = CPU(cpu); |
386 | d77953b9 | Andreas Färber | CPUX86State *env = &cpu->env; |
387 | e5ad936b | Jan Kiszka | VAPICHandlers *handlers; |
388 | e5ad936b | Jan Kiszka | uint8_t opcode[2];
|
389 | e5ad936b | Jan Kiszka | uint32_t imm32; |
390 | 5c61afec | Jan Kiszka | target_ulong current_pc = 0;
|
391 | 5c61afec | Jan Kiszka | target_ulong current_cs_base = 0;
|
392 | 5c61afec | Jan Kiszka | int current_flags = 0; |
393 | e5ad936b | Jan Kiszka | |
394 | e5ad936b | Jan Kiszka | if (smp_cpus == 1) { |
395 | e5ad936b | Jan Kiszka | handlers = &s->rom_state.up; |
396 | e5ad936b | Jan Kiszka | } else {
|
397 | e5ad936b | Jan Kiszka | handlers = &s->rom_state.mp; |
398 | e5ad936b | Jan Kiszka | } |
399 | e5ad936b | Jan Kiszka | |
400 | 5c61afec | Jan Kiszka | if (!kvm_enabled()) {
|
401 | a8a826a3 | Blue Swirl | cpu_restore_state(env, env->mem_io_pc); |
402 | 5c61afec | Jan Kiszka | cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, |
403 | 5c61afec | Jan Kiszka | ¤t_flags); |
404 | 5c61afec | Jan Kiszka | } |
405 | 5c61afec | Jan Kiszka | |
406 | e5ad936b | Jan Kiszka | pause_all_vcpus(); |
407 | e5ad936b | Jan Kiszka | |
408 | e5ad936b | Jan Kiszka | cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0); |
409 | e5ad936b | Jan Kiszka | |
410 | e5ad936b | Jan Kiszka | switch (opcode[0]) { |
411 | e5ad936b | Jan Kiszka | case 0x89: /* mov r32 to r/m32 */ |
412 | e5ad936b | Jan Kiszka | patch_byte(env, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ |
413 | e5ad936b | Jan Kiszka | patch_call(s, env, ip + 1, handlers->set_tpr);
|
414 | e5ad936b | Jan Kiszka | break;
|
415 | e5ad936b | Jan Kiszka | case 0x8b: /* mov r/m32 to r32 */ |
416 | e5ad936b | Jan Kiszka | patch_byte(env, ip, 0x90);
|
417 | e5ad936b | Jan Kiszka | patch_call(s, env, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); |
418 | e5ad936b | Jan Kiszka | break;
|
419 | e5ad936b | Jan Kiszka | case 0xa1: /* mov abs to eax */ |
420 | e5ad936b | Jan Kiszka | patch_call(s, env, ip, handlers->get_tpr[0]);
|
421 | e5ad936b | Jan Kiszka | break;
|
422 | e5ad936b | Jan Kiszka | case 0xa3: /* mov eax to abs */ |
423 | e5ad936b | Jan Kiszka | patch_call(s, env, ip, handlers->set_tpr_eax); |
424 | e5ad936b | Jan Kiszka | break;
|
425 | e5ad936b | Jan Kiszka | case 0xc7: /* mov imm32, r/m32 (c7/0) */ |
426 | e5ad936b | Jan Kiszka | patch_byte(env, ip, 0x68); /* push imm32 */ |
427 | e5ad936b | Jan Kiszka | cpu_memory_rw_debug(env, ip + 6, (void *)&imm32, sizeof(imm32), 0); |
428 | e5ad936b | Jan Kiszka | cpu_memory_rw_debug(env, ip + 1, (void *)&imm32, sizeof(imm32), 1); |
429 | e5ad936b | Jan Kiszka | patch_call(s, env, ip + 5, handlers->set_tpr);
|
430 | e5ad936b | Jan Kiszka | break;
|
431 | e5ad936b | Jan Kiszka | case 0xff: /* push r/m32 */ |
432 | e5ad936b | Jan Kiszka | patch_byte(env, ip, 0x50); /* push eax */ |
433 | e5ad936b | Jan Kiszka | patch_call(s, env, ip + 1, handlers->get_tpr_stack);
|
434 | e5ad936b | Jan Kiszka | break;
|
435 | e5ad936b | Jan Kiszka | default:
|
436 | e5ad936b | Jan Kiszka | abort(); |
437 | e5ad936b | Jan Kiszka | } |
438 | e5ad936b | Jan Kiszka | |
439 | e5ad936b | Jan Kiszka | resume_all_vcpus(); |
440 | e5ad936b | Jan Kiszka | |
441 | 5c61afec | Jan Kiszka | if (!kvm_enabled()) {
|
442 | d77953b9 | Andreas Färber | cs->current_tb = NULL;
|
443 | 5c61afec | Jan Kiszka | tb_gen_code(env, current_pc, current_cs_base, current_flags, 1);
|
444 | 5c61afec | Jan Kiszka | cpu_resume_from_signal(env, NULL);
|
445 | 5c61afec | Jan Kiszka | } |
446 | e5ad936b | Jan Kiszka | } |
447 | e5ad936b | Jan Kiszka | |
448 | d77953b9 | Andreas Färber | void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip,
|
449 | e5ad936b | Jan Kiszka | TPRAccess access) |
450 | e5ad936b | Jan Kiszka | { |
451 | e5ad936b | Jan Kiszka | VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev); |
452 | d77953b9 | Andreas Färber | X86CPU *cpu = X86_CPU(cs); |
453 | d77953b9 | Andreas Färber | CPUX86State *env = &cpu->env; |
454 | e5ad936b | Jan Kiszka | |
455 | e5ad936b | Jan Kiszka | cpu_synchronize_state(env); |
456 | e5ad936b | Jan Kiszka | |
457 | e5ad936b | Jan Kiszka | if (evaluate_tpr_instruction(s, env, &ip, access) < 0) { |
458 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_ACTIVE) {
|
459 | e5ad936b | Jan Kiszka | vapic_enable(s, env); |
460 | e5ad936b | Jan Kiszka | } |
461 | e5ad936b | Jan Kiszka | return;
|
462 | e5ad936b | Jan Kiszka | } |
463 | e5ad936b | Jan Kiszka | if (update_rom_mapping(s, env, ip) < 0) { |
464 | e5ad936b | Jan Kiszka | return;
|
465 | e5ad936b | Jan Kiszka | } |
466 | e5ad936b | Jan Kiszka | if (vapic_enable(s, env) < 0) { |
467 | e5ad936b | Jan Kiszka | return;
|
468 | e5ad936b | Jan Kiszka | } |
469 | d77953b9 | Andreas Färber | patch_instruction(s, cpu, ip); |
470 | e5ad936b | Jan Kiszka | } |
471 | e5ad936b | Jan Kiszka | |
472 | e5ad936b | Jan Kiszka | typedef struct VAPICEnableTPRReporting { |
473 | e5ad936b | Jan Kiszka | DeviceState *apic; |
474 | e5ad936b | Jan Kiszka | bool enable;
|
475 | e5ad936b | Jan Kiszka | } VAPICEnableTPRReporting; |
476 | e5ad936b | Jan Kiszka | |
477 | e5ad936b | Jan Kiszka | static void vapic_do_enable_tpr_reporting(void *data) |
478 | e5ad936b | Jan Kiszka | { |
479 | e5ad936b | Jan Kiszka | VAPICEnableTPRReporting *info = data; |
480 | e5ad936b | Jan Kiszka | |
481 | e5ad936b | Jan Kiszka | apic_enable_tpr_access_reporting(info->apic, info->enable); |
482 | e5ad936b | Jan Kiszka | } |
483 | e5ad936b | Jan Kiszka | |
484 | e5ad936b | Jan Kiszka | static void vapic_enable_tpr_reporting(bool enable) |
485 | e5ad936b | Jan Kiszka | { |
486 | e5ad936b | Jan Kiszka | VAPICEnableTPRReporting info = { |
487 | e5ad936b | Jan Kiszka | .enable = enable, |
488 | e5ad936b | Jan Kiszka | }; |
489 | f100f0b3 | Andreas Färber | X86CPU *cpu; |
490 | 4a8fa5dc | Andreas Färber | CPUX86State *env; |
491 | e5ad936b | Jan Kiszka | |
492 | e5ad936b | Jan Kiszka | for (env = first_cpu; env != NULL; env = env->next_cpu) { |
493 | f100f0b3 | Andreas Färber | cpu = x86_env_get_cpu(env); |
494 | e5ad936b | Jan Kiszka | info.apic = env->apic_state; |
495 | f100f0b3 | Andreas Färber | run_on_cpu(CPU(cpu), vapic_do_enable_tpr_reporting, &info); |
496 | e5ad936b | Jan Kiszka | } |
497 | e5ad936b | Jan Kiszka | } |
498 | e5ad936b | Jan Kiszka | |
499 | e5ad936b | Jan Kiszka | static void vapic_reset(DeviceState *dev) |
500 | e5ad936b | Jan Kiszka | { |
501 | e5ad936b | Jan Kiszka | VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev); |
502 | e5ad936b | Jan Kiszka | |
503 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_ACTIVE) {
|
504 | e5ad936b | Jan Kiszka | s->state = VAPIC_STANDBY; |
505 | e5ad936b | Jan Kiszka | } |
506 | e5ad936b | Jan Kiszka | vapic_enable_tpr_reporting(false);
|
507 | e5ad936b | Jan Kiszka | } |
508 | e5ad936b | Jan Kiszka | |
509 | e5ad936b | Jan Kiszka | /*
|
510 | e5ad936b | Jan Kiszka | * Set the IRQ polling hypercalls to the supported variant:
|
511 | e5ad936b | Jan Kiszka | * - vmcall if using KVM in-kernel irqchip
|
512 | e5ad936b | Jan Kiszka | * - 32-bit VAPIC port write otherwise
|
513 | e5ad936b | Jan Kiszka | */
|
514 | e5ad936b | Jan Kiszka | static int patch_hypercalls(VAPICROMState *s) |
515 | e5ad936b | Jan Kiszka | { |
516 | a8170e5e | Avi Kivity | hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; |
517 | e5ad936b | Jan Kiszka | static const uint8_t vmcall_pattern[] = { /* vmcall */ |
518 | e5ad936b | Jan Kiszka | 0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1 |
519 | e5ad936b | Jan Kiszka | }; |
520 | e5ad936b | Jan Kiszka | static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */ |
521 | e5ad936b | Jan Kiszka | 0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e |
522 | e5ad936b | Jan Kiszka | }; |
523 | e5ad936b | Jan Kiszka | uint8_t alternates[2];
|
524 | e5ad936b | Jan Kiszka | const uint8_t *pattern;
|
525 | e5ad936b | Jan Kiszka | const uint8_t *patch;
|
526 | e5ad936b | Jan Kiszka | int patches = 0; |
527 | e5ad936b | Jan Kiszka | off_t pos; |
528 | e5ad936b | Jan Kiszka | uint8_t *rom; |
529 | e5ad936b | Jan Kiszka | |
530 | e5ad936b | Jan Kiszka | rom = g_malloc(s->rom_size); |
531 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(rom_paddr, rom, s->rom_size, 0);
|
532 | e5ad936b | Jan Kiszka | |
533 | e5ad936b | Jan Kiszka | for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) { |
534 | e5ad936b | Jan Kiszka | if (kvm_irqchip_in_kernel()) {
|
535 | e5ad936b | Jan Kiszka | pattern = outl_pattern; |
536 | e5ad936b | Jan Kiszka | alternates[0] = outl_pattern[7]; |
537 | e5ad936b | Jan Kiszka | alternates[1] = outl_pattern[7]; |
538 | e5ad936b | Jan Kiszka | patch = &vmcall_pattern[5];
|
539 | e5ad936b | Jan Kiszka | } else {
|
540 | e5ad936b | Jan Kiszka | pattern = vmcall_pattern; |
541 | e5ad936b | Jan Kiszka | alternates[0] = vmcall_pattern[7]; |
542 | e5ad936b | Jan Kiszka | alternates[1] = 0xd9; /* AMD's VMMCALL */ |
543 | e5ad936b | Jan Kiszka | patch = &outl_pattern[5];
|
544 | e5ad936b | Jan Kiszka | } |
545 | e5ad936b | Jan Kiszka | if (memcmp(rom + pos, pattern, 7) == 0 && |
546 | e5ad936b | Jan Kiszka | (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) { |
547 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(rom_paddr + pos + 5, (uint8_t *)patch,
|
548 | e5ad936b | Jan Kiszka | 3, 1); |
549 | e5ad936b | Jan Kiszka | /*
|
550 | e5ad936b | Jan Kiszka | * Don't flush the tb here. Under ordinary conditions, the patched
|
551 | e5ad936b | Jan Kiszka | * calls are miles away from the current IP. Under malicious
|
552 | e5ad936b | Jan Kiszka | * conditions, the guest could trick us to crash.
|
553 | e5ad936b | Jan Kiszka | */
|
554 | e5ad936b | Jan Kiszka | } |
555 | e5ad936b | Jan Kiszka | } |
556 | e5ad936b | Jan Kiszka | |
557 | e5ad936b | Jan Kiszka | g_free(rom); |
558 | e5ad936b | Jan Kiszka | |
559 | e5ad936b | Jan Kiszka | if (patches != 0 && patches != 2) { |
560 | e5ad936b | Jan Kiszka | return -1; |
561 | e5ad936b | Jan Kiszka | } |
562 | e5ad936b | Jan Kiszka | |
563 | e5ad936b | Jan Kiszka | return 0; |
564 | e5ad936b | Jan Kiszka | } |
565 | e5ad936b | Jan Kiszka | |
566 | e5ad936b | Jan Kiszka | /*
|
567 | e5ad936b | Jan Kiszka | * For TCG mode or the time KVM honors read-only memory regions, we need to
|
568 | e5ad936b | Jan Kiszka | * enable write access to the option ROM so that variables can be updated by
|
569 | e5ad936b | Jan Kiszka | * the guest.
|
570 | e5ad936b | Jan Kiszka | */
|
571 | e5ad936b | Jan Kiszka | static void vapic_map_rom_writable(VAPICROMState *s) |
572 | e5ad936b | Jan Kiszka | { |
573 | a8170e5e | Avi Kivity | hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; |
574 | e5ad936b | Jan Kiszka | MemoryRegionSection section; |
575 | e5ad936b | Jan Kiszka | MemoryRegion *as; |
576 | e5ad936b | Jan Kiszka | size_t rom_size; |
577 | e5ad936b | Jan Kiszka | uint8_t *ram; |
578 | e5ad936b | Jan Kiszka | |
579 | e5ad936b | Jan Kiszka | as = sysbus_address_space(&s->busdev); |
580 | e5ad936b | Jan Kiszka | |
581 | e5ad936b | Jan Kiszka | if (s->rom_mapped_writable) {
|
582 | e5ad936b | Jan Kiszka | memory_region_del_subregion(as, &s->rom); |
583 | e5ad936b | Jan Kiszka | memory_region_destroy(&s->rom); |
584 | e5ad936b | Jan Kiszka | } |
585 | e5ad936b | Jan Kiszka | |
586 | e5ad936b | Jan Kiszka | /* grab RAM memory region (region @rom_paddr may still be pc.rom) */
|
587 | e5ad936b | Jan Kiszka | section = memory_region_find(as, 0, 1); |
588 | e5ad936b | Jan Kiszka | |
589 | e5ad936b | Jan Kiszka | /* read ROM size from RAM region */
|
590 | e5ad936b | Jan Kiszka | ram = memory_region_get_ram_ptr(section.mr); |
591 | e5ad936b | Jan Kiszka | rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE;
|
592 | e5ad936b | Jan Kiszka | s->rom_size = rom_size; |
593 | e5ad936b | Jan Kiszka | |
594 | 9512e4a9 | Avi Kivity | /* We need to round to avoid creating subpages
|
595 | e5ad936b | Jan Kiszka | * from which we cannot run code. */
|
596 | 9512e4a9 | Avi Kivity | rom_size += rom_paddr & ~TARGET_PAGE_MASK; |
597 | 9512e4a9 | Avi Kivity | rom_paddr &= TARGET_PAGE_MASK; |
598 | e5ad936b | Jan Kiszka | rom_size = TARGET_PAGE_ALIGN(rom_size); |
599 | e5ad936b | Jan Kiszka | |
600 | e5ad936b | Jan Kiszka | memory_region_init_alias(&s->rom, "kvmvapic-rom", section.mr, rom_paddr,
|
601 | e5ad936b | Jan Kiszka | rom_size); |
602 | e5ad936b | Jan Kiszka | memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000);
|
603 | e5ad936b | Jan Kiszka | s->rom_mapped_writable = true;
|
604 | e5ad936b | Jan Kiszka | } |
605 | e5ad936b | Jan Kiszka | |
606 | e5ad936b | Jan Kiszka | static int vapic_prepare(VAPICROMState *s) |
607 | e5ad936b | Jan Kiszka | { |
608 | e5ad936b | Jan Kiszka | vapic_map_rom_writable(s); |
609 | e5ad936b | Jan Kiszka | |
610 | e5ad936b | Jan Kiszka | if (patch_hypercalls(s) < 0) { |
611 | e5ad936b | Jan Kiszka | return -1; |
612 | e5ad936b | Jan Kiszka | } |
613 | e5ad936b | Jan Kiszka | |
614 | e5ad936b | Jan Kiszka | vapic_enable_tpr_reporting(true);
|
615 | e5ad936b | Jan Kiszka | |
616 | e5ad936b | Jan Kiszka | return 0; |
617 | e5ad936b | Jan Kiszka | } |
618 | e5ad936b | Jan Kiszka | |
619 | a8170e5e | Avi Kivity | static void vapic_write(void *opaque, hwaddr addr, uint64_t data, |
620 | e5ad936b | Jan Kiszka | unsigned int size) |
621 | e5ad936b | Jan Kiszka | { |
622 | 4a8fa5dc | Andreas Färber | CPUX86State *env = cpu_single_env; |
623 | a8170e5e | Avi Kivity | hwaddr rom_paddr; |
624 | e5ad936b | Jan Kiszka | VAPICROMState *s = opaque; |
625 | e5ad936b | Jan Kiszka | |
626 | e5ad936b | Jan Kiszka | cpu_synchronize_state(env); |
627 | e5ad936b | Jan Kiszka | |
628 | e5ad936b | Jan Kiszka | /*
|
629 | e5ad936b | Jan Kiszka | * The VAPIC supports two PIO-based hypercalls, both via port 0x7E.
|
630 | e5ad936b | Jan Kiszka | * o 16-bit write access:
|
631 | e5ad936b | Jan Kiszka | * Reports the option ROM initialization to the hypervisor. Written
|
632 | e5ad936b | Jan Kiszka | * value is the offset of the state structure in the ROM.
|
633 | e5ad936b | Jan Kiszka | * o 8-bit write access:
|
634 | e5ad936b | Jan Kiszka | * Reactivates the VAPIC after a guest hibernation, i.e. after the
|
635 | e5ad936b | Jan Kiszka | * option ROM content has been re-initialized by a guest power cycle.
|
636 | e5ad936b | Jan Kiszka | * o 32-bit write access:
|
637 | e5ad936b | Jan Kiszka | * Poll for pending IRQs, considering the current VAPIC state.
|
638 | e5ad936b | Jan Kiszka | */
|
639 | e5ad936b | Jan Kiszka | switch (size) {
|
640 | e5ad936b | Jan Kiszka | case 2: |
641 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_INACTIVE) {
|
642 | e5ad936b | Jan Kiszka | rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK; |
643 | e5ad936b | Jan Kiszka | s->rom_state_paddr = rom_paddr + data; |
644 | e5ad936b | Jan Kiszka | |
645 | e5ad936b | Jan Kiszka | s->state = VAPIC_STANDBY; |
646 | e5ad936b | Jan Kiszka | } |
647 | e5ad936b | Jan Kiszka | if (vapic_prepare(s) < 0) { |
648 | e5ad936b | Jan Kiszka | s->state = VAPIC_INACTIVE; |
649 | e5ad936b | Jan Kiszka | break;
|
650 | e5ad936b | Jan Kiszka | } |
651 | e5ad936b | Jan Kiszka | break;
|
652 | e5ad936b | Jan Kiszka | case 1: |
653 | e5ad936b | Jan Kiszka | if (kvm_enabled()) {
|
654 | e5ad936b | Jan Kiszka | /*
|
655 | e5ad936b | Jan Kiszka | * Disable triggering instruction in ROM by writing a NOP.
|
656 | e5ad936b | Jan Kiszka | *
|
657 | e5ad936b | Jan Kiszka | * We cannot do this in TCG mode as the reported IP is not
|
658 | e5ad936b | Jan Kiszka | * accurate.
|
659 | e5ad936b | Jan Kiszka | */
|
660 | e5ad936b | Jan Kiszka | pause_all_vcpus(); |
661 | e5ad936b | Jan Kiszka | patch_byte(env, env->eip - 2, 0x66); |
662 | e5ad936b | Jan Kiszka | patch_byte(env, env->eip - 1, 0x90); |
663 | e5ad936b | Jan Kiszka | resume_all_vcpus(); |
664 | e5ad936b | Jan Kiszka | } |
665 | e5ad936b | Jan Kiszka | |
666 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_ACTIVE) {
|
667 | e5ad936b | Jan Kiszka | break;
|
668 | e5ad936b | Jan Kiszka | } |
669 | e5ad936b | Jan Kiszka | if (update_rom_mapping(s, env, env->eip) < 0) { |
670 | e5ad936b | Jan Kiszka | break;
|
671 | e5ad936b | Jan Kiszka | } |
672 | e5ad936b | Jan Kiszka | if (find_real_tpr_addr(s, env) < 0) { |
673 | e5ad936b | Jan Kiszka | break;
|
674 | e5ad936b | Jan Kiszka | } |
675 | e5ad936b | Jan Kiszka | vapic_enable(s, env); |
676 | e5ad936b | Jan Kiszka | break;
|
677 | e5ad936b | Jan Kiszka | default:
|
678 | e5ad936b | Jan Kiszka | case 4: |
679 | e5ad936b | Jan Kiszka | if (!kvm_irqchip_in_kernel()) {
|
680 | e5ad936b | Jan Kiszka | apic_poll_irq(env->apic_state); |
681 | e5ad936b | Jan Kiszka | } |
682 | e5ad936b | Jan Kiszka | break;
|
683 | e5ad936b | Jan Kiszka | } |
684 | e5ad936b | Jan Kiszka | } |
685 | e5ad936b | Jan Kiszka | |
686 | e5ad936b | Jan Kiszka | static const MemoryRegionOps vapic_ops = { |
687 | e5ad936b | Jan Kiszka | .write = vapic_write, |
688 | e5ad936b | Jan Kiszka | .endianness = DEVICE_NATIVE_ENDIAN, |
689 | e5ad936b | Jan Kiszka | }; |
690 | e5ad936b | Jan Kiszka | |
691 | e5ad936b | Jan Kiszka | static int vapic_init(SysBusDevice *dev) |
692 | e5ad936b | Jan Kiszka | { |
693 | e5ad936b | Jan Kiszka | VAPICROMState *s = FROM_SYSBUS(VAPICROMState, dev); |
694 | e5ad936b | Jan Kiszka | |
695 | e5ad936b | Jan Kiszka | memory_region_init_io(&s->io, &vapic_ops, s, "kvmvapic", 2); |
696 | e5ad936b | Jan Kiszka | sysbus_add_io(dev, VAPIC_IO_PORT, &s->io); |
697 | e5ad936b | Jan Kiszka | sysbus_init_ioports(dev, VAPIC_IO_PORT, 2);
|
698 | e5ad936b | Jan Kiszka | |
699 | e5ad936b | Jan Kiszka | option_rom[nb_option_roms].name = "kvmvapic.bin";
|
700 | e5ad936b | Jan Kiszka | option_rom[nb_option_roms].bootindex = -1;
|
701 | e5ad936b | Jan Kiszka | nb_option_roms++; |
702 | e5ad936b | Jan Kiszka | |
703 | e5ad936b | Jan Kiszka | return 0; |
704 | e5ad936b | Jan Kiszka | } |
705 | e5ad936b | Jan Kiszka | |
706 | e5ad936b | Jan Kiszka | static void do_vapic_enable(void *data) |
707 | e5ad936b | Jan Kiszka | { |
708 | e5ad936b | Jan Kiszka | VAPICROMState *s = data; |
709 | e5ad936b | Jan Kiszka | |
710 | e5ad936b | Jan Kiszka | vapic_enable(s, first_cpu); |
711 | e5ad936b | Jan Kiszka | } |
712 | e5ad936b | Jan Kiszka | |
713 | e5ad936b | Jan Kiszka | static int vapic_post_load(void *opaque, int version_id) |
714 | e5ad936b | Jan Kiszka | { |
715 | e5ad936b | Jan Kiszka | VAPICROMState *s = opaque; |
716 | e5ad936b | Jan Kiszka | uint8_t *zero; |
717 | e5ad936b | Jan Kiszka | |
718 | e5ad936b | Jan Kiszka | /*
|
719 | e5ad936b | Jan Kiszka | * The old implementation of qemu-kvm did not provide the state
|
720 | e5ad936b | Jan Kiszka | * VAPIC_STANDBY. Reconstruct it.
|
721 | e5ad936b | Jan Kiszka | */
|
722 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) { |
723 | e5ad936b | Jan Kiszka | s->state = VAPIC_STANDBY; |
724 | e5ad936b | Jan Kiszka | } |
725 | e5ad936b | Jan Kiszka | |
726 | e5ad936b | Jan Kiszka | if (s->state != VAPIC_INACTIVE) {
|
727 | e5ad936b | Jan Kiszka | if (vapic_prepare(s) < 0) { |
728 | e5ad936b | Jan Kiszka | return -1; |
729 | e5ad936b | Jan Kiszka | } |
730 | e5ad936b | Jan Kiszka | } |
731 | e5ad936b | Jan Kiszka | if (s->state == VAPIC_ACTIVE) {
|
732 | e5ad936b | Jan Kiszka | if (smp_cpus == 1) { |
733 | f100f0b3 | Andreas Färber | run_on_cpu(ENV_GET_CPU(first_cpu), do_vapic_enable, s); |
734 | e5ad936b | Jan Kiszka | } else {
|
735 | e5ad936b | Jan Kiszka | zero = g_malloc0(s->rom_state.vapic_size); |
736 | e5ad936b | Jan Kiszka | cpu_physical_memory_rw(s->vapic_paddr, zero, |
737 | e5ad936b | Jan Kiszka | s->rom_state.vapic_size, 1);
|
738 | e5ad936b | Jan Kiszka | g_free(zero); |
739 | e5ad936b | Jan Kiszka | } |
740 | e5ad936b | Jan Kiszka | } |
741 | e5ad936b | Jan Kiszka | |
742 | e5ad936b | Jan Kiszka | return 0; |
743 | e5ad936b | Jan Kiszka | } |
744 | e5ad936b | Jan Kiszka | |
745 | e5ad936b | Jan Kiszka | static const VMStateDescription vmstate_handlers = { |
746 | e5ad936b | Jan Kiszka | .name = "kvmvapic-handlers",
|
747 | e5ad936b | Jan Kiszka | .version_id = 1,
|
748 | e5ad936b | Jan Kiszka | .minimum_version_id = 1,
|
749 | e5ad936b | Jan Kiszka | .minimum_version_id_old = 1,
|
750 | e5ad936b | Jan Kiszka | .fields = (VMStateField[]) { |
751 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(set_tpr, VAPICHandlers), |
752 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(set_tpr_eax, VAPICHandlers), |
753 | e5ad936b | Jan Kiszka | VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8),
|
754 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(get_tpr_stack, VAPICHandlers), |
755 | e5ad936b | Jan Kiszka | VMSTATE_END_OF_LIST() |
756 | e5ad936b | Jan Kiszka | } |
757 | e5ad936b | Jan Kiszka | }; |
758 | e5ad936b | Jan Kiszka | |
759 | e5ad936b | Jan Kiszka | static const VMStateDescription vmstate_guest_rom = { |
760 | e5ad936b | Jan Kiszka | .name = "kvmvapic-guest-rom",
|
761 | e5ad936b | Jan Kiszka | .version_id = 1,
|
762 | e5ad936b | Jan Kiszka | .minimum_version_id = 1,
|
763 | e5ad936b | Jan Kiszka | .minimum_version_id_old = 1,
|
764 | e5ad936b | Jan Kiszka | .fields = (VMStateField[]) { |
765 | e5ad936b | Jan Kiszka | VMSTATE_UNUSED(8), /* signature */ |
766 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(vaddr, GuestROMState), |
767 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(fixup_start, GuestROMState), |
768 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(fixup_end, GuestROMState), |
769 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(vapic_vaddr, GuestROMState), |
770 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(vapic_size, GuestROMState), |
771 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(vcpu_shift, GuestROMState), |
772 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(real_tpr_addr, GuestROMState), |
773 | e5ad936b | Jan Kiszka | VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
|
774 | e5ad936b | Jan Kiszka | VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
|
775 | e5ad936b | Jan Kiszka | VMSTATE_END_OF_LIST() |
776 | e5ad936b | Jan Kiszka | } |
777 | e5ad936b | Jan Kiszka | }; |
778 | e5ad936b | Jan Kiszka | |
779 | e5ad936b | Jan Kiszka | static const VMStateDescription vmstate_vapic = { |
780 | e5ad936b | Jan Kiszka | .name = "kvm-tpr-opt", /* compatible with qemu-kvm VAPIC */ |
781 | e5ad936b | Jan Kiszka | .version_id = 1,
|
782 | e5ad936b | Jan Kiszka | .minimum_version_id = 1,
|
783 | e5ad936b | Jan Kiszka | .minimum_version_id_old = 1,
|
784 | e5ad936b | Jan Kiszka | .post_load = vapic_post_load, |
785 | e5ad936b | Jan Kiszka | .fields = (VMStateField[]) { |
786 | e5ad936b | Jan Kiszka | VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom,
|
787 | e5ad936b | Jan Kiszka | GuestROMState), |
788 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(state, VAPICROMState), |
789 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(real_tpr_addr, VAPICROMState), |
790 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(rom_state_vaddr, VAPICROMState), |
791 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(vapic_paddr, VAPICROMState), |
792 | e5ad936b | Jan Kiszka | VMSTATE_UINT32(rom_state_paddr, VAPICROMState), |
793 | e5ad936b | Jan Kiszka | VMSTATE_END_OF_LIST() |
794 | e5ad936b | Jan Kiszka | } |
795 | e5ad936b | Jan Kiszka | }; |
796 | e5ad936b | Jan Kiszka | |
797 | e5ad936b | Jan Kiszka | static void vapic_class_init(ObjectClass *klass, void *data) |
798 | e5ad936b | Jan Kiszka | { |
799 | e5ad936b | Jan Kiszka | SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); |
800 | e5ad936b | Jan Kiszka | DeviceClass *dc = DEVICE_CLASS(klass); |
801 | e5ad936b | Jan Kiszka | |
802 | e5ad936b | Jan Kiszka | dc->no_user = 1;
|
803 | e5ad936b | Jan Kiszka | dc->reset = vapic_reset; |
804 | e5ad936b | Jan Kiszka | dc->vmsd = &vmstate_vapic; |
805 | e5ad936b | Jan Kiszka | sc->init = vapic_init; |
806 | e5ad936b | Jan Kiszka | } |
807 | e5ad936b | Jan Kiszka | |
808 | 8c43a6f0 | Andreas Färber | static const TypeInfo vapic_type = { |
809 | e5ad936b | Jan Kiszka | .name = "kvmvapic",
|
810 | e5ad936b | Jan Kiszka | .parent = TYPE_SYS_BUS_DEVICE, |
811 | e5ad936b | Jan Kiszka | .instance_size = sizeof(VAPICROMState),
|
812 | e5ad936b | Jan Kiszka | .class_init = vapic_class_init, |
813 | e5ad936b | Jan Kiszka | }; |
814 | e5ad936b | Jan Kiszka | |
815 | e5ad936b | Jan Kiszka | static void vapic_register(void) |
816 | e5ad936b | Jan Kiszka | { |
817 | e5ad936b | Jan Kiszka | type_register_static(&vapic_type); |
818 | e5ad936b | Jan Kiszka | } |
819 | e5ad936b | Jan Kiszka | |
820 | e5ad936b | Jan Kiszka | type_init(vapic_register); |