xref: /qemu/pc-bios/optionrom/kvmvapic.S (revision 72ac97cd)
1#
2# Local APIC acceleration for Windows XP and related guests
3#
4# Copyright 2011 Red Hat, Inc. and/or its affiliates
5#
6# Author: Avi Kivity <avi@redhat.com>
7#
8# This work is licensed under the terms of the GNU GPL, version 2, or (at your
9# option) any later version.  See the COPYING file in the top-level directory.
10#
11
12#include "optionrom.h"
13
14OPTION_ROM_START
15
16	# clear vapic area: firmware load using rep insb may cause
17	# stale tpr/isr/irr data to corrupt the vapic area.
18	push %es
19	push %cs
20	pop %es
21	xor %ax, %ax
22	mov $vapic_size/2, %cx
23	lea vapic, %di
24	cld
25	rep stosw
26	pop %es
27
28	# announce presence to the hypervisor
29	mov $vapic_base, %ax
30	out %ax, $0x7e
31
32	lret
33
34	.code32
35vapic_size = 2*4096
36
37.macro fixup delta=-4
38777:
39	.text 1
40	.long 777b + \delta  - vapic_base
41	.text 0
42.endm
43
44.macro reenable_vtpr
45	out %al, $0x7e
46.endm
47
48.text 1
49	fixup_start = .
50.text 0
51
52.align 16
53
54vapic_base:
55	.ascii "kvm aPiC"
56
57	/* relocation data */
58	.long vapic_base	; fixup
59	.long fixup_start	; fixup
60	.long fixup_end		; fixup
61
62	.long vapic		; fixup
63	.long vapic_size
64vcpu_shift:
65	.long 0
66real_tpr:
67	.long 0
68	.long up_set_tpr	; fixup
69	.long up_set_tpr_eax	; fixup
70	.long up_get_tpr_eax	; fixup
71	.long up_get_tpr_ecx	; fixup
72	.long up_get_tpr_edx	; fixup
73	.long up_get_tpr_ebx	; fixup
74	.long 0 /* esp. won't work. */
75	.long up_get_tpr_ebp	; fixup
76	.long up_get_tpr_esi	; fixup
77	.long up_get_tpr_edi	; fixup
78	.long up_get_tpr_stack  ; fixup
79	.long mp_set_tpr	; fixup
80	.long mp_set_tpr_eax	; fixup
81	.long mp_get_tpr_eax	; fixup
82	.long mp_get_tpr_ecx	; fixup
83	.long mp_get_tpr_edx	; fixup
84	.long mp_get_tpr_ebx	; fixup
85	.long 0 /* esp. won't work. */
86	.long mp_get_tpr_ebp	; fixup
87	.long mp_get_tpr_esi	; fixup
88	.long mp_get_tpr_edi	; fixup
89	.long mp_get_tpr_stack  ; fixup
90
91.macro kvm_hypercall
92	.byte 0x0f, 0x01, 0xc1
93.endm
94
95kvm_hypercall_vapic_poll_irq = 1
96
97pcr_cpu = 0x51
98
99.align 64
100
101mp_get_tpr_eax:
102	pushf
103	cli
104	reenable_vtpr
105	push %ecx
106
107	fs/movzbl pcr_cpu, %eax
108
109	mov vcpu_shift, %ecx	; fixup
110	shl %cl, %eax
111	testb $1, vapic+4(%eax)	; fixup delta=-5
112	jz mp_get_tpr_bad
113	movzbl vapic(%eax), %eax ; fixup
114
115mp_get_tpr_out:
116	pop %ecx
117	popf
118	ret
119
120mp_get_tpr_bad:
121	mov real_tpr, %eax	; fixup
122	mov (%eax), %eax
123	jmp mp_get_tpr_out
124
125mp_get_tpr_ebx:
126	mov %eax, %ebx
127	call mp_get_tpr_eax
128	xchg %eax, %ebx
129	ret
130
131mp_get_tpr_ecx:
132	mov %eax, %ecx
133	call mp_get_tpr_eax
134	xchg %eax, %ecx
135	ret
136
137mp_get_tpr_edx:
138	mov %eax, %edx
139	call mp_get_tpr_eax
140	xchg %eax, %edx
141	ret
142
143mp_get_tpr_esi:
144	mov %eax, %esi
145	call mp_get_tpr_eax
146	xchg %eax, %esi
147	ret
148
149mp_get_tpr_edi:
150	mov %eax, %edi
151	call mp_get_tpr_edi
152	xchg %eax, %edi
153	ret
154
155mp_get_tpr_ebp:
156	mov %eax, %ebp
157	call mp_get_tpr_eax
158	xchg %eax, %ebp
159	ret
160
161mp_get_tpr_stack:
162	call mp_get_tpr_eax
163	xchg %eax, 4(%esp)
164	ret
165
166mp_set_tpr_eax:
167	push %eax
168	call mp_set_tpr
169	ret
170
171mp_set_tpr:
172	pushf
173	push %eax
174	push %ecx
175	push %edx
176	push %ebx
177	cli
178	reenable_vtpr
179
180mp_set_tpr_failed:
181	fs/movzbl pcr_cpu, %edx
182
183	mov vcpu_shift, %ecx	; fixup
184	shl %cl, %edx
185
186	testb $1, vapic+4(%edx)	; fixup delta=-5
187	jz mp_set_tpr_bad
188
189	mov vapic(%edx), %eax	; fixup
190
191	mov %eax, %ebx
192	mov 24(%esp), %bl
193
194	/* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
195
196	lock cmpxchg %ebx, vapic(%edx) ; fixup
197	jnz mp_set_tpr_failed
198
199	/* compute ppr */
200	cmp %bh, %bl
201	jae mp_tpr_is_bigger
202mp_isr_is_bigger:
203	mov %bh, %bl
204mp_tpr_is_bigger:
205	/* %bl = ppr */
206	rol $8, %ebx
207	/* now: %bl = irr, %bh = ppr */
208	cmp %bh, %bl
209	ja mp_set_tpr_poll_irq
210
211mp_set_tpr_out:
212	pop %ebx
213	pop %edx
214	pop %ecx
215	pop %eax
216	popf
217	ret $4
218
219mp_set_tpr_poll_irq:
220	mov $kvm_hypercall_vapic_poll_irq, %eax
221	kvm_hypercall
222	jmp mp_set_tpr_out
223
224mp_set_tpr_bad:
225	mov 24(%esp), %ecx
226	mov real_tpr, %eax	; fixup
227	mov %ecx, (%eax)
228	jmp mp_set_tpr_out
229
230up_get_tpr_eax:
231	reenable_vtpr
232	movzbl vapic, %eax ; fixup
233	ret
234
235up_get_tpr_ebx:
236	reenable_vtpr
237	movzbl vapic, %ebx ; fixup
238	ret
239
240up_get_tpr_ecx:
241	reenable_vtpr
242	movzbl vapic, %ecx ; fixup
243	ret
244
245up_get_tpr_edx:
246	reenable_vtpr
247	movzbl vapic, %edx ; fixup
248	ret
249
250up_get_tpr_esi:
251	reenable_vtpr
252	movzbl vapic, %esi ; fixup
253	ret
254
255up_get_tpr_edi:
256	reenable_vtpr
257	movzbl vapic, %edi ; fixup
258	ret
259
260up_get_tpr_ebp:
261	reenable_vtpr
262	movzbl vapic, %ebp ; fixup
263	ret
264
265up_get_tpr_stack:
266	reenable_vtpr
267	movzbl vapic, %eax ; fixup
268	xchg %eax, 4(%esp)
269	ret
270
271up_set_tpr_eax:
272	push %eax
273	call up_set_tpr
274	ret
275
276up_set_tpr:
277	pushf
278	push %eax
279	push %ebx
280	reenable_vtpr
281
282up_set_tpr_failed:
283	mov vapic, %eax	; fixup
284
285	mov %eax, %ebx
286	mov 16(%esp), %bl
287
288	/* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
289
290	lock cmpxchg %ebx, vapic ; fixup
291	jnz up_set_tpr_failed
292
293	/* compute ppr */
294	cmp %bh, %bl
295	jae up_tpr_is_bigger
296up_isr_is_bigger:
297	mov %bh, %bl
298up_tpr_is_bigger:
299	/* %bl = ppr */
300	rol $8, %ebx
301	/* now: %bl = irr, %bh = ppr */
302	cmp %bh, %bl
303	ja up_set_tpr_poll_irq
304
305up_set_tpr_out:
306	pop %ebx
307	pop %eax
308	popf
309	ret $4
310
311up_set_tpr_poll_irq:
312	mov $kvm_hypercall_vapic_poll_irq, %eax
313	kvm_hypercall
314	jmp up_set_tpr_out
315
316.text 1
317	fixup_end = .
318.text 0
319
320/*
321 * vapic format:
322 *  per-vcpu records of size 2^vcpu shift.
323 *     byte 0: tpr (r/w)
324 *     byte 1: highest in-service interrupt (isr) (r/o); bits 3:0 are zero
325 *     byte 2: zero (r/o)
326 *     byte 3: highest pending interrupt (irr) (r/o)
327 */
328.text 2
329
330.align 128
331
332vapic:
333. = . + vapic_size
334
335OPTION_ROM_END
336