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