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