Add qemu 2.4.0
[kvmfornfv.git] / qemu / pc-bios / optionrom / kvmvapic.S
diff --git a/qemu/pc-bios/optionrom/kvmvapic.S b/qemu/pc-bios/optionrom/kvmvapic.S
new file mode 100644 (file)
index 0000000..aa17a40
--- /dev/null
@@ -0,0 +1,335 @@
+#
+# Local APIC acceleration for Windows XP and related guests
+#
+# Copyright 2011 Red Hat, Inc. and/or its affiliates
+#
+# Author: Avi Kivity <avi@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2, or (at your
+# option) any later version.  See the COPYING file in the top-level directory.
+#
+
+#include "optionrom.h"
+
+OPTION_ROM_START
+
+       # clear vapic area: firmware load using rep insb may cause
+       # stale tpr/isr/irr data to corrupt the vapic area.
+       push %es
+       push %cs
+       pop %es
+       xor %ax, %ax
+       mov $vapic_size/2, %cx
+       lea vapic, %di
+       cld
+       rep stosw
+       pop %es
+
+       # announce presence to the hypervisor
+       mov $vapic_base, %ax
+       out %ax, $0x7e
+
+       lret
+
+       .code32
+vapic_size = 2*4096
+
+.macro fixup delta=-4
+777:
+       .text 1
+       .long 777b + \delta  - vapic_base
+       .text 0
+.endm
+
+.macro reenable_vtpr
+       out %al, $0x7e
+.endm
+
+.text 1
+       fixup_start = .
+.text 0
+
+.align 16
+
+vapic_base:
+       .ascii "kvm aPiC"
+
+       /* relocation data */
+       .long vapic_base        ; fixup
+       .long fixup_start       ; fixup
+       .long fixup_end         ; fixup
+
+       .long vapic             ; fixup
+       .long vapic_size
+vcpu_shift:
+       .long 0
+real_tpr:
+       .long 0
+       .long up_set_tpr        ; fixup
+       .long up_set_tpr_eax    ; fixup
+       .long up_get_tpr_eax    ; fixup
+       .long up_get_tpr_ecx    ; fixup
+       .long up_get_tpr_edx    ; fixup
+       .long up_get_tpr_ebx    ; fixup
+       .long 0 /* esp. won't work. */
+       .long up_get_tpr_ebp    ; fixup
+       .long up_get_tpr_esi    ; fixup
+       .long up_get_tpr_edi    ; fixup
+       .long up_get_tpr_stack  ; fixup
+       .long mp_set_tpr        ; fixup
+       .long mp_set_tpr_eax    ; fixup
+       .long mp_get_tpr_eax    ; fixup
+       .long mp_get_tpr_ecx    ; fixup
+       .long mp_get_tpr_edx    ; fixup
+       .long mp_get_tpr_ebx    ; fixup
+       .long 0 /* esp. won't work. */
+       .long mp_get_tpr_ebp    ; fixup
+       .long mp_get_tpr_esi    ; fixup
+       .long mp_get_tpr_edi    ; fixup
+       .long mp_get_tpr_stack  ; fixup
+
+.macro kvm_hypercall
+       .byte 0x0f, 0x01, 0xc1
+.endm
+
+kvm_hypercall_vapic_poll_irq = 1
+
+pcr_cpu = 0x51
+
+.align 64
+
+mp_get_tpr_eax:
+       pushf
+       cli
+       reenable_vtpr
+       push %ecx
+
+       fs/movzbl pcr_cpu, %eax
+
+       mov vcpu_shift, %ecx    ; fixup
+       shl %cl, %eax
+       testb $1, vapic+4(%eax) ; fixup delta=-5
+       jz mp_get_tpr_bad
+       movzbl vapic(%eax), %eax ; fixup
+
+mp_get_tpr_out:
+       pop %ecx
+       popf
+       ret
+
+mp_get_tpr_bad:
+       mov real_tpr, %eax      ; fixup
+       mov (%eax), %eax
+       jmp mp_get_tpr_out
+
+mp_get_tpr_ebx:
+       mov %eax, %ebx
+       call mp_get_tpr_eax
+       xchg %eax, %ebx
+       ret
+
+mp_get_tpr_ecx:
+       mov %eax, %ecx
+       call mp_get_tpr_eax
+       xchg %eax, %ecx
+       ret
+
+mp_get_tpr_edx:
+       mov %eax, %edx
+       call mp_get_tpr_eax
+       xchg %eax, %edx
+       ret
+
+mp_get_tpr_esi:
+       mov %eax, %esi
+       call mp_get_tpr_eax
+       xchg %eax, %esi
+       ret
+
+mp_get_tpr_edi:
+       mov %eax, %edi
+       call mp_get_tpr_edi
+       xchg %eax, %edi
+       ret
+
+mp_get_tpr_ebp:
+       mov %eax, %ebp
+       call mp_get_tpr_eax
+       xchg %eax, %ebp
+       ret
+
+mp_get_tpr_stack:
+       call mp_get_tpr_eax
+       xchg %eax, 4(%esp)
+       ret
+
+mp_set_tpr_eax:
+       push %eax
+       call mp_set_tpr
+       ret
+
+mp_set_tpr:
+       pushf
+       push %eax
+       push %ecx
+       push %edx
+       push %ebx
+       cli
+       reenable_vtpr
+
+mp_set_tpr_failed:
+       fs/movzbl pcr_cpu, %edx
+
+       mov vcpu_shift, %ecx    ; fixup
+       shl %cl, %edx
+
+       testb $1, vapic+4(%edx) ; fixup delta=-5
+       jz mp_set_tpr_bad
+
+       mov vapic(%edx), %eax   ; fixup
+
+       mov %eax, %ebx
+       mov 24(%esp), %bl
+
+       /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
+
+       lock cmpxchg %ebx, vapic(%edx) ; fixup
+       jnz mp_set_tpr_failed
+
+       /* compute ppr */
+       cmp %bh, %bl
+       jae mp_tpr_is_bigger
+mp_isr_is_bigger:
+       mov %bh, %bl
+mp_tpr_is_bigger:
+       /* %bl = ppr */
+       rol $8, %ebx
+       /* now: %bl = irr, %bh = ppr */
+       cmp %bh, %bl
+       ja mp_set_tpr_poll_irq
+
+mp_set_tpr_out:
+       pop %ebx
+       pop %edx
+       pop %ecx
+       pop %eax
+       popf
+       ret $4
+
+mp_set_tpr_poll_irq:
+       mov $kvm_hypercall_vapic_poll_irq, %eax
+       kvm_hypercall
+       jmp mp_set_tpr_out
+
+mp_set_tpr_bad:
+       mov 24(%esp), %ecx
+       mov real_tpr, %eax      ; fixup
+       mov %ecx, (%eax)
+       jmp mp_set_tpr_out
+
+up_get_tpr_eax:
+       reenable_vtpr
+       movzbl vapic, %eax ; fixup
+       ret
+
+up_get_tpr_ebx:
+       reenable_vtpr
+       movzbl vapic, %ebx ; fixup
+       ret
+
+up_get_tpr_ecx:
+       reenable_vtpr
+       movzbl vapic, %ecx ; fixup
+       ret
+
+up_get_tpr_edx:
+       reenable_vtpr
+       movzbl vapic, %edx ; fixup
+       ret
+
+up_get_tpr_esi:
+       reenable_vtpr
+       movzbl vapic, %esi ; fixup
+       ret
+
+up_get_tpr_edi:
+       reenable_vtpr
+       movzbl vapic, %edi ; fixup
+       ret
+
+up_get_tpr_ebp:
+       reenable_vtpr
+       movzbl vapic, %ebp ; fixup
+       ret
+
+up_get_tpr_stack:
+       reenable_vtpr
+       movzbl vapic, %eax ; fixup
+       xchg %eax, 4(%esp)
+       ret
+
+up_set_tpr_eax:
+       push %eax
+       call up_set_tpr
+       ret
+
+up_set_tpr:
+       pushf
+       push %eax
+       push %ebx
+       reenable_vtpr
+
+up_set_tpr_failed:
+       mov vapic, %eax ; fixup
+
+       mov %eax, %ebx
+       mov 16(%esp), %bl
+
+       /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
+
+       lock cmpxchg %ebx, vapic ; fixup
+       jnz up_set_tpr_failed
+
+       /* compute ppr */
+       cmp %bh, %bl
+       jae up_tpr_is_bigger
+up_isr_is_bigger:
+       mov %bh, %bl
+up_tpr_is_bigger:
+       /* %bl = ppr */
+       rol $8, %ebx
+       /* now: %bl = irr, %bh = ppr */
+       cmp %bh, %bl
+       ja up_set_tpr_poll_irq
+
+up_set_tpr_out:
+       pop %ebx
+       pop %eax
+       popf
+       ret $4
+
+up_set_tpr_poll_irq:
+       mov $kvm_hypercall_vapic_poll_irq, %eax
+       kvm_hypercall
+       jmp up_set_tpr_out
+
+.text 1
+       fixup_end = .
+.text 0
+
+/*
+ * vapic format:
+ *  per-vcpu records of size 2^vcpu shift.
+ *     byte 0: tpr (r/w)
+ *     byte 1: highest in-service interrupt (isr) (r/o); bits 3:0 are zero
+ *     byte 2: zero (r/o)
+ *     byte 3: highest pending interrupt (irr) (r/o)
+ */
+.text 2
+
+.align 128
+
+vapic:
+. = . + vapic_size
+
+OPTION_ROM_END