xref: /qemu/pc-bios/optionrom/kvmvapic.S (revision 2a2af967b0bc601c9b450c72b95104e67222e5b2)
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	.text 0
13	.code16
14.global _start
15_start:
16	.short 0xaa55
17	.byte (_end - _start) / 512
18	# clear vapic area: firmware load using rep insb may cause
19	# stale tpr/isr/irr data to corrupt the vapic area.
20	push %es
21	push %cs
22	pop %es
23	xor %ax, %ax
24	mov $vapic_size/2, %cx
25	lea vapic, %di
26	cld
27	rep stosw
28	pop %es
29	mov $vapic_base, %ax
30	out %ax, $0x7e
31	lret
32
33	.code32
34vapic_size = 2*4096
35
36.macro fixup delta=-4
37777:
38	.text 1
39	.long 777b + \delta  - vapic_base
40	.text 0
41.endm
42
43.macro reenable_vtpr
44	out %al, $0x7e
45.endm
46
47.text 1
48	fixup_start = .
49.text 0
50
51.align 16
52
53vapic_base:
54	.ascii "kvm aPiC"
55
56	/* relocation data */
57	.long vapic_base	; fixup
58	.long fixup_start	; fixup
59	.long fixup_end		; fixup
60
61	.long vapic		; fixup
62	.long vapic_size
63vcpu_shift:
64	.long 0
65real_tpr:
66	.long 0
67	.long up_set_tpr	; fixup
68	.long up_set_tpr_eax	; fixup
69	.long up_get_tpr_eax	; fixup
70	.long up_get_tpr_ecx	; fixup
71	.long up_get_tpr_edx	; fixup
72	.long up_get_tpr_ebx	; fixup
73	.long 0 /* esp. won't work. */
74	.long up_get_tpr_ebp	; fixup
75	.long up_get_tpr_esi	; fixup
76	.long up_get_tpr_edi	; fixup
77	.long up_get_tpr_stack  ; fixup
78	.long mp_set_tpr	; fixup
79	.long mp_set_tpr_eax	; fixup
80	.long mp_get_tpr_eax	; fixup
81	.long mp_get_tpr_ecx	; fixup
82	.long mp_get_tpr_edx	; fixup
83	.long mp_get_tpr_ebx	; fixup
84	.long 0 /* esp. won't work. */
85	.long mp_get_tpr_ebp	; fixup
86	.long mp_get_tpr_esi	; fixup
87	.long mp_get_tpr_edi	; fixup
88	.long mp_get_tpr_stack  ; fixup
89
90.macro kvm_hypercall
91	.byte 0x0f, 0x01, 0xc1
92.endm
93
94kvm_hypercall_vapic_poll_irq = 1
95
96pcr_cpu = 0x51
97
98.align 64
99
100mp_get_tpr_eax:
101	pushf
102	cli
103	reenable_vtpr
104	push %ecx
105
106	fs/movzbl pcr_cpu, %eax
107
108	mov vcpu_shift, %ecx	; fixup
109	shl %cl, %eax
110	testb $1, vapic+4(%eax)	; fixup delta=-5
111	jz mp_get_tpr_bad
112	movzbl vapic(%eax), %eax ; fixup
113
114mp_get_tpr_out:
115	pop %ecx
116	popf
117	ret
118
119mp_get_tpr_bad:
120	mov real_tpr, %eax	; fixup
121	mov (%eax), %eax
122	jmp mp_get_tpr_out
123
124mp_get_tpr_ebx:
125	mov %eax, %ebx
126	call mp_get_tpr_eax
127	xchg %eax, %ebx
128	ret
129
130mp_get_tpr_ecx:
131	mov %eax, %ecx
132	call mp_get_tpr_eax
133	xchg %eax, %ecx
134	ret
135
136mp_get_tpr_edx:
137	mov %eax, %edx
138	call mp_get_tpr_eax
139	xchg %eax, %edx
140	ret
141
142mp_get_tpr_esi:
143	mov %eax, %esi
144	call mp_get_tpr_eax
145	xchg %eax, %esi
146	ret
147
148mp_get_tpr_edi:
149	mov %eax, %edi
150	call mp_get_tpr_edi
151	xchg %eax, %edi
152	ret
153
154mp_get_tpr_ebp:
155	mov %eax, %ebp
156	call mp_get_tpr_eax
157	xchg %eax, %ebp
158	ret
159
160mp_get_tpr_stack:
161	call mp_get_tpr_eax
162	xchg %eax, 4(%esp)
163	ret
164
165mp_set_tpr_eax:
166	push %eax
167	call mp_set_tpr
168	ret
169
170mp_set_tpr:
171	pushf
172	push %eax
173	push %ecx
174	push %edx
175	push %ebx
176	cli
177	reenable_vtpr
178
179mp_set_tpr_failed:
180	fs/movzbl pcr_cpu, %edx
181
182	mov vcpu_shift, %ecx	; fixup
183	shl %cl, %edx
184
185	testb $1, vapic+4(%edx)	; fixup delta=-5
186	jz mp_set_tpr_bad
187
188	mov vapic(%edx), %eax	; fixup
189
190	mov %eax, %ebx
191	mov 24(%esp), %bl
192
193	/* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
194
195	lock cmpxchg %ebx, vapic(%edx) ; fixup
196	jnz mp_set_tpr_failed
197
198	/* compute ppr */
199	cmp %bh, %bl
200	jae mp_tpr_is_bigger
201mp_isr_is_bigger:
202	mov %bh, %bl
203mp_tpr_is_bigger:
204	/* %bl = ppr */
205	mov %bl, %ch   /* ch = 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 %ecx
280	push %ebx
281	reenable_vtpr
282
283up_set_tpr_failed:
284	mov vapic, %eax	; fixup
285
286	mov %eax, %ebx
287	mov 20(%esp), %bl
288
289	/* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
290
291	lock cmpxchg %ebx, vapic ; fixup
292	jnz up_set_tpr_failed
293
294	/* compute ppr */
295	cmp %bh, %bl
296	jae up_tpr_is_bigger
297up_isr_is_bigger:
298	mov %bh, %bl
299up_tpr_is_bigger:
300	/* %bl = ppr */
301	mov %bl, %ch   /* ch = ppr */
302	rol $8, %ebx
303	/* now: %bl = irr, %bh = ppr */
304	cmp %bh, %bl
305	ja up_set_tpr_poll_irq
306
307up_set_tpr_out:
308	pop %ebx
309	pop %ecx
310	pop %eax
311	popf
312	ret $4
313
314up_set_tpr_poll_irq:
315	mov $kvm_hypercall_vapic_poll_irq, %eax
316	kvm_hypercall
317	jmp up_set_tpr_out
318
319.text 1
320	fixup_end = .
321.text 0
322
323/*
324 * vapic format:
325 *  per-vcpu records of size 2^vcpu shift.
326 *     byte 0: tpr (r/w)
327 *     byte 1: highest in-service interrupt (isr) (r/o); bits 3:0 are zero
328 *     byte 2: zero (r/o)
329 *     byte 3: highest pending interrupt (irr) (r/o)
330 */
331.text 2
332
333.align 128
334
335vapic:
336. = . + vapic_size
337
338.byte 0  # reserve space for signature
339.align 512, 0
340
341_end:
342