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