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