Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 381 lines (333 sloc) 9.592 kB
68396ea @rmustacc Initial commit of d32e8d0b8d9e0ef7cf7ab2e74548982972789dfc from qemu-kvm
rmustacc authored
1 /*
2 * tpr optimization for qemu/kvm
3 *
4 * Copyright (C) 2007-2008 Qumranet Technologies
5 *
6 * Licensed under the terms of the GNU GPL version 2 or higher.
7 */
8
9 #include "config.h"
10 #include "config-host.h"
11
12 #include <string.h>
13
14 #include "hw/hw.h"
15 #include "hw/isa.h"
16 #include "sysemu.h"
17 #include "kvm.h"
18 #include "cpu.h"
19
20 #include <stdio.h>
21
22 static uint64_t map_addr(CPUState *env, target_ulong virt, unsigned *perms)
23 {
24 uint64_t mask = ((1ull << 48) - 1) & ~4095ull;
25 uint64_t p, pp = 7;
26
27 p = env->cr[3];
28 if (env->cr[4] & 0x20) {
29 p &= ~31ull;
30 p = ldq_phys(p + 8 * (virt >> 30));
31 if (!(p & 1))
32 return -1ull;
33 p &= mask;
34 p = ldq_phys(p + 8 * ((virt >> 21) & 511));
35 if (!(p & 1))
36 return -1ull;
37 pp &= p;
38 if (p & 128) {
39 p += ((virt >> 12) & 511) << 12;
40 } else {
41 p &= mask;
42 p = ldq_phys(p + 8 * ((virt >> 12) & 511));
43 if (!(p & 1))
44 return -1ull;
45 pp &= p;
46 }
47 } else {
48 p &= mask;
49 p = ldl_phys(p + 4 * ((virt >> 22) & 1023));
50 if (!(p & 1))
51 return -1ull;
52 pp &= p;
53 if (p & 128) {
54 p += ((virt >> 12) & 1023) << 12;
55 } else {
56 p &= mask;
57 p = ldl_phys(p + 4 * ((virt >> 12) & 1023));
58 pp &= p;
59 if (!(p & 1))
60 return -1ull;
61 }
62 }
63 if (perms)
64 *perms = pp >> 1;
65 p &= mask;
66 return p + (virt & 4095);
67 }
68
69 static uint8_t read_byte_virt(CPUState *env, target_ulong virt)
70 {
71 return ldub_phys(map_addr(env, virt, NULL));
72 }
73
74 static void write_byte_virt(CPUState *env, target_ulong virt, uint8_t b)
75 {
76 cpu_physical_memory_write_rom(map_addr(env, virt, NULL), &b, 1);
77 }
78
79 struct vapic_bios {
80 char signature[8];
81 uint32_t virt_base;
82 uint32_t fixup_start;
83 uint32_t fixup_end;
84 uint32_t vapic;
85 uint32_t vapic_size;
86 uint32_t vcpu_shift;
87 uint32_t real_tpr;
88 struct vapic_patches {
89 uint32_t set_tpr;
90 uint32_t set_tpr_eax;
91 uint32_t get_tpr[8];
92 uint32_t get_tpr_stack;
93 } __attribute__((packed)) up, mp;
94 } __attribute__((packed));
95
96 static struct vapic_bios vapic_bios;
97
98 static uint32_t real_tpr;
99 static uint32_t bios_addr;
100 static uint32_t vapic_phys;
101 static uint32_t bios_enabled;
102 static uint32_t vbios_desc_phys;
103 static uint32_t vapic_bios_addr;
104
105 static void update_vbios_real_tpr(void)
106 {
107 cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 0);
108 vapic_bios.real_tpr = real_tpr;
109 vapic_bios.vcpu_shift = 7;
110 cpu_physical_memory_write_rom(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios);
111 }
112
113 static unsigned modrm_reg(uint8_t modrm)
114 {
115 return (modrm >> 3) & 7;
116 }
117
118 static int is_abs_modrm(uint8_t modrm)
119 {
120 return (modrm & 0xc7) == 0x05;
121 }
122
123 static int instruction_is_ok(CPUState *env, uint64_t rip, int is_write)
124 {
125 uint8_t b1, b2;
126 unsigned addr_offset;
127 uint32_t addr;
128 uint64_t p;
129
130 if ((rip & 0xf0000000) != 0x80000000 && (rip & 0xf0000000) != 0xe0000000)
131 return 0;
132 if (env->regs[R_ESP] == 0)
133 return 0;
134 b1 = read_byte_virt(env, rip);
135 b2 = read_byte_virt(env, rip + 1);
136 switch (b1) {
137 case 0xc7: /* mov imm32, r/m32 (c7/0) */
138 if (modrm_reg(b2) != 0)
139 return 0;
140 /* fall through */
141 case 0x89: /* mov r32 to r/m32 */
142 case 0x8b: /* mov r/m32 to r32 */
143 if (!is_abs_modrm(b2))
144 return 0;
145 addr_offset = 2;
146 break;
147 case 0xa1: /* mov abs to eax */
148 case 0xa3: /* mov eax to abs */
149 addr_offset = 1;
150 break;
151 case 0xff: /* push r/m32 */
152 if (modrm_reg(b2) != 6 || !is_abs_modrm(b2))
153 return 0;
154 addr_offset = 2;
155 default:
156 return 0;
157 }
158 p = rip + addr_offset;
159 addr = read_byte_virt(env, p++);
160 addr |= read_byte_virt(env, p++) << 8;
161 addr |= read_byte_virt(env, p++) << 16;
162 addr |= read_byte_virt(env, p++) << 24;
163 if ((addr & 0xfff) != 0x80)
164 return 0;
165 real_tpr = addr;
166 update_vbios_real_tpr();
167 return 1;
168 }
169
170 static int bios_is_mapped(CPUState *env, uint64_t rip)
171 {
172 uint32_t probe;
173 uint64_t phys;
174 unsigned perms;
175 uint32_t i;
176 uint32_t offset, fixup, start = vapic_bios_addr ? : 0xe0000;
177 uint32_t patch;
178
179 if (bios_enabled)
180 return 1;
181
182 probe = (rip & 0xf0000000) + start;
183 phys = map_addr(env, probe, &perms);
184 if (phys != start)
185 return 0;
186 bios_addr = probe;
187 for (i = 0; i < 64; ++i) {
188 cpu_physical_memory_read(phys, (void *)&vapic_bios, sizeof(vapic_bios));
189 if (memcmp(vapic_bios.signature, "kvm aPiC", 8) == 0)
190 break;
191 phys += 1024;
192 bios_addr += 1024;
193 }
194 if (i == 64)
195 return 0;
196 if (bios_addr == vapic_bios.virt_base)
197 return 1;
198 vbios_desc_phys = phys;
199 for (i = vapic_bios.fixup_start; i < vapic_bios.fixup_end; i += 4) {
200 offset = ldl_phys(phys + i - vapic_bios.virt_base);
201 fixup = phys + offset;
202 patch = ldl_phys(fixup) + bios_addr - vapic_bios.virt_base;
203 cpu_physical_memory_write_rom(fixup, (uint8_t *)&patch, 4);
204 }
205 vapic_phys = vapic_bios.vapic - vapic_bios.virt_base + phys;
206 return 1;
207 }
208
209 static int get_pcr_cpu(CPUState *env)
210 {
211 uint8_t b;
212
213 cpu_synchronize_state(env);
214
215 if (cpu_memory_rw_debug(env, env->segs[R_FS].base + 0x51, &b, 1, 0) < 0)
216 return -1;
217
218 return (int)b;
219 }
220
221 int kvm_tpr_enable_vapic(CPUState *env)
222 {
223 static uint8_t one = 1;
224 int pcr_cpu = get_pcr_cpu(env);
225
226 if (pcr_cpu < 0)
227 return 0;
228
229 kvm_enable_vapic(env, vapic_phys + (pcr_cpu << 7));
230 cpu_physical_memory_write_rom(vapic_phys + (pcr_cpu << 7) + 4, &one, 1);
231 env->kvm_vcpu_update_vapic = 0;
232 bios_enabled = 1;
233 return 1;
234 }
235
236 static void patch_call(CPUState *env, uint64_t rip, uint32_t target)
237 {
238 uint32_t offset;
239
240 offset = target - vapic_bios.virt_base + bios_addr - rip - 5;
241 write_byte_virt(env, rip, 0xe8); /* call near */
242 write_byte_virt(env, rip + 1, offset);
243 write_byte_virt(env, rip + 2, offset >> 8);
244 write_byte_virt(env, rip + 3, offset >> 16);
245 write_byte_virt(env, rip + 4, offset >> 24);
246 }
247
248 static void patch_instruction(CPUState *env, uint64_t rip)
249 {
250 uint8_t b1, b2;
251 struct vapic_patches *vp;
252
253 vp = smp_cpus == 1 ? &vapic_bios.up : &vapic_bios.mp;
254 b1 = read_byte_virt(env, rip);
255 b2 = read_byte_virt(env, rip + 1);
256 switch (b1) {
257 case 0x89: /* mov r32 to r/m32 */
258 write_byte_virt(env, rip, 0x50 + modrm_reg(b2)); /* push reg */
259 patch_call(env, rip + 1, vp->set_tpr);
260 break;
261 case 0x8b: /* mov r/m32 to r32 */
262 write_byte_virt(env, rip, 0x90);
263 patch_call(env, rip + 1, vp->get_tpr[modrm_reg(b2)]);
264 break;
265 case 0xa1: /* mov abs to eax */
266 patch_call(env, rip, vp->get_tpr[0]);
267 break;
268 case 0xa3: /* mov eax to abs */
269 patch_call(env, rip, vp->set_tpr_eax);
270 break;
271 case 0xc7: /* mov imm32, r/m32 (c7/0) */
272 write_byte_virt(env, rip, 0x68); /* push imm32 */
273 write_byte_virt(env, rip + 1, read_byte_virt(env, rip+6));
274 write_byte_virt(env, rip + 2, read_byte_virt(env, rip+7));
275 write_byte_virt(env, rip + 3, read_byte_virt(env, rip+8));
276 write_byte_virt(env, rip + 4, read_byte_virt(env, rip+9));
277 patch_call(env, rip + 5, vp->set_tpr);
278 break;
279 case 0xff: /* push r/m32 */
280 printf("patching push\n");
281 write_byte_virt(env, rip, 0x50); /* push eax */
282 patch_call(env, rip + 1, vp->get_tpr_stack);
283 break;
284 default:
285 printf("funny insn %02x %02x\n", b1, b2);
286 }
287 }
288
289 void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write)
290 {
291 cpu_synchronize_state(env);
292 if (!instruction_is_ok(env, rip, is_write))
293 return;
294 if (!bios_is_mapped(env, rip))
295 return;
296 if (!kvm_tpr_enable_vapic(env))
297 return;
298 patch_instruction(env, rip);
299 }
300
301 static void tpr_save(QEMUFile *f, void *s)
302 {
303 int i;
304
305 for (i = 0; i < (sizeof vapic_bios) / 4; ++i)
306 qemu_put_be32s(f, &((uint32_t *)&vapic_bios)[i]);
307 qemu_put_be32s(f, &bios_enabled);
308 qemu_put_be32s(f, &real_tpr);
309 qemu_put_be32s(f, &bios_addr);
310 qemu_put_be32s(f, &vapic_phys);
311 qemu_put_be32s(f, &vbios_desc_phys);
312 }
313
314 static int tpr_load(QEMUFile *f, void *s, int version_id)
315 {
316 int i;
317
318 if (version_id != 1)
319 return -EINVAL;
320
321 for (i = 0; i < (sizeof vapic_bios) / 4; ++i)
322 qemu_get_be32s(f, &((uint32_t *)&vapic_bios)[i]);
323 qemu_get_be32s(f, &bios_enabled);
324 qemu_get_be32s(f, &real_tpr);
325 qemu_get_be32s(f, &bios_addr);
326 qemu_get_be32s(f, &vapic_phys);
327 qemu_get_be32s(f, &vbios_desc_phys);
328
329 if (bios_enabled) {
330 CPUState *env = first_cpu->next_cpu;
331
332 for (env = first_cpu; env != NULL; env = env->next_cpu)
333 env->kvm_vcpu_update_vapic = 1;
334 }
335
336 return 0;
337 }
338
339 static void vtpr_ioport_write16(void *opaque, uint32_t addr, uint32_t val)
340 {
341 CPUState *env = cpu_single_env;
342
343 cpu_synchronize_state(env);
344
345 vapic_bios_addr = ((env->segs[R_CS].base + env->eip) & ~(512 - 1)) + val;
346 bios_enabled = 0;
347 }
348
349 static void vtpr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
350 {
351 CPUState *env = cpu_single_env;
352 uint32_t rip;
353
354 cpu_synchronize_state(env);
355
356 rip = env->eip - 2;
357 write_byte_virt(env, rip, 0x66);
358 write_byte_virt(env, rip + 1, 0x90);
359 if (bios_enabled)
360 return;
361 if (!bios_is_mapped(env, rip))
362 printf("bios not mapped?\n");
363 for (addr = 0xfffff000u; addr >= 0x80000000u; addr -= 4096)
364 if (map_addr(env, addr, NULL) == 0xfee00000u) {
365 real_tpr = addr + 0x80;
366 break;
367 }
368 bios_enabled = 1;
369 update_vbios_real_tpr();
370 kvm_tpr_enable_vapic(env);
371 }
372
373 static void kvm_tpr_opt_setup(void)
374 {
375 register_savevm(NULL, "kvm-tpr-opt", 0, 1, tpr_save, tpr_load, NULL);
376 register_ioport_write(0x7e, 1, 1, vtpr_ioport_write, NULL);
377 register_ioport_write(0x7e, 2, 2, vtpr_ioport_write16, NULL);
378 }
379
380 device_init(kvm_tpr_opt_setup);
Something went wrong with that request. Please try again.