Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: PPC: Book3S PR: Fix VSX handling

This fixes various issues in how we were handling the VSX registers
that exist on POWER7 machines. First, we were running off the end
of the current->thread.fpr[] array. Ultimately this was because the
vcpu->arch.vsr[] array is sized to be able to store both the FP
registers and the extra VSX registers (i.e. 64 entries), but PR KVM
only uses it for the extra VSX registers (i.e. 32 entries).

Secondly, calling load_up_vsx() from C code is a really bad idea,
because it jumps to fast_exception_return at the end, rather than
returning with a blr instruction. This was causing it to jump off
to a random location with random register contents, since it was using
the largely uninitialized stack frame created by kvmppc_load_up_vsx.

In fact, it isn't necessary to call either __giveup_vsx or load_up_vsx,
since giveup_fpu and load_up_fpu handle the extra VSX registers as well
as the standard FP registers on machines with VSX. Also, since VSX
instructions can access the VMX registers and the FP registers as well
as the extra VSX registers, we have to load up the FP and VMX registers
before we can turn on the MSR_VSX bit for the guest. Conversely, if
we save away any of the VSX or FP registers, we have to turn off MSR_VSX
for the guest.

To handle all this, it is more convenient for a single call to
kvmppc_giveup_ext() to handle all the state saving that needs to be done,
so we make it take a set of MSR bits rather than just one, and the switch
statement becomes a series of if statements. Similarly kvmppc_handle_ext
needs to be able to load up more than one set of registers.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>

authored by

Paul Mackerras and committed by
Alexander Graf
28c483b6 b0a94d4e

+62 -57
+1
arch/powerpc/include/asm/reg.h
··· 518 518 #define SRR1_WS_DEEPER 0x00020000 /* Some resources not maintained */ 519 519 #define SRR1_WS_DEEP 0x00010000 /* All resources maintained */ 520 520 #define SRR1_PROGFPE 0x00100000 /* Floating Point Enabled */ 521 + #define SRR1_PROGILL 0x00080000 /* Illegal instruction */ 521 522 #define SRR1_PROGPRIV 0x00040000 /* Privileged instruction */ 522 523 #define SRR1_PROGTRAP 0x00020000 /* Trap */ 523 524 #define SRR1_PROGADDR 0x00010000 /* SRR0 contains subsequent addr */
-3
arch/powerpc/kvm/book3s_exports.c
··· 28 28 #ifdef CONFIG_ALTIVEC 29 29 EXPORT_SYMBOL_GPL(kvmppc_load_up_altivec); 30 30 #endif 31 - #ifdef CONFIG_VSX 32 - EXPORT_SYMBOL_GPL(kvmppc_load_up_vsx); 33 - #endif 34 31 #endif 35 32
+61 -51
arch/powerpc/kvm/book3s_pr.c
··· 81 81 svcpu_put(svcpu); 82 82 #endif 83 83 84 - kvmppc_giveup_ext(vcpu, MSR_FP); 85 - kvmppc_giveup_ext(vcpu, MSR_VEC); 86 - kvmppc_giveup_ext(vcpu, MSR_VSX); 84 + kvmppc_giveup_ext(vcpu, MSR_FP | MSR_VEC | MSR_VSX); 87 85 vcpu->cpu = -1; 88 86 } 89 87 ··· 431 433 432 434 static inline int get_fpr_index(int i) 433 435 { 434 - #ifdef CONFIG_VSX 435 - i *= 2; 436 - #endif 437 - return i; 436 + return i * TS_FPRWIDTH; 438 437 } 439 438 440 439 /* Give up external provider (FPU, Altivec, VSX) */ ··· 445 450 u64 *thread_fpr = (u64*)t->fpr; 446 451 int i; 447 452 448 - if (!(vcpu->arch.guest_owned_ext & msr)) 453 + /* 454 + * VSX instructions can access FP and vector registers, so if 455 + * we are giving up VSX, make sure we give up FP and VMX as well. 456 + */ 457 + if (msr & MSR_VSX) 458 + msr |= MSR_FP | MSR_VEC; 459 + 460 + msr &= vcpu->arch.guest_owned_ext; 461 + if (!msr) 449 462 return; 450 463 451 464 #ifdef DEBUG_EXT 452 465 printk(KERN_INFO "Giving up ext 0x%lx\n", msr); 453 466 #endif 454 467 455 - switch (msr) { 456 - case MSR_FP: 468 + if (msr & MSR_FP) { 469 + /* 470 + * Note that on CPUs with VSX, giveup_fpu stores 471 + * both the traditional FP registers and the added VSX 472 + * registers into thread.fpr[]. 473 + */ 457 474 giveup_fpu(current); 458 475 for (i = 0; i < ARRAY_SIZE(vcpu->arch.fpr); i++) 459 476 vcpu_fpr[i] = thread_fpr[get_fpr_index(i)]; 460 477 461 478 vcpu->arch.fpscr = t->fpscr.val; 462 - break; 463 - case MSR_VEC: 479 + 480 + #ifdef CONFIG_VSX 481 + if (cpu_has_feature(CPU_FTR_VSX)) 482 + for (i = 0; i < ARRAY_SIZE(vcpu->arch.vsr) / 2; i++) 483 + vcpu_vsx[i] = thread_fpr[get_fpr_index(i) + 1]; 484 + #endif 485 + } 486 + 464 487 #ifdef CONFIG_ALTIVEC 488 + if (msr & MSR_VEC) { 465 489 giveup_altivec(current); 466 490 memcpy(vcpu->arch.vr, t->vr, sizeof(vcpu->arch.vr)); 467 491 vcpu->arch.vscr = t->vscr; 468 - #endif 469 - break; 470 - case MSR_VSX: 471 - #ifdef CONFIG_VSX 472 - __giveup_vsx(current); 473 - for (i = 0; i < ARRAY_SIZE(vcpu->arch.vsr); i++) 474 - vcpu_vsx[i] = thread_fpr[get_fpr_index(i) + 1]; 475 - #endif 476 - break; 477 - default: 478 - BUG(); 479 492 } 493 + #endif 480 494 481 - vcpu->arch.guest_owned_ext &= ~msr; 482 - current->thread.regs->msr &= ~msr; 495 + vcpu->arch.guest_owned_ext &= ~(msr | MSR_VSX); 483 496 kvmppc_recalc_shadow_msr(vcpu); 484 497 } 485 498 ··· 547 544 return RESUME_GUEST; 548 545 } 549 546 550 - /* We already own the ext */ 551 - if (vcpu->arch.guest_owned_ext & msr) { 552 - return RESUME_GUEST; 547 + if (msr == MSR_VSX) { 548 + /* No VSX? Give an illegal instruction interrupt */ 549 + #ifdef CONFIG_VSX 550 + if (!cpu_has_feature(CPU_FTR_VSX)) 551 + #endif 552 + { 553 + kvmppc_core_queue_program(vcpu, SRR1_PROGILL); 554 + return RESUME_GUEST; 555 + } 556 + 557 + /* 558 + * We have to load up all the FP and VMX registers before 559 + * we can let the guest use VSX instructions. 560 + */ 561 + msr = MSR_FP | MSR_VEC | MSR_VSX; 553 562 } 563 + 564 + /* See if we already own all the ext(s) needed */ 565 + msr &= ~vcpu->arch.guest_owned_ext; 566 + if (!msr) 567 + return RESUME_GUEST; 554 568 555 569 #ifdef DEBUG_EXT 556 570 printk(KERN_INFO "Loading up ext 0x%lx\n", msr); ··· 575 555 576 556 current->thread.regs->msr |= msr; 577 557 578 - switch (msr) { 579 - case MSR_FP: 558 + if (msr & MSR_FP) { 580 559 for (i = 0; i < ARRAY_SIZE(vcpu->arch.fpr); i++) 581 560 thread_fpr[get_fpr_index(i)] = vcpu_fpr[i]; 582 - 561 + #ifdef CONFIG_VSX 562 + for (i = 0; i < ARRAY_SIZE(vcpu->arch.vsr) / 2; i++) 563 + thread_fpr[get_fpr_index(i) + 1] = vcpu_vsx[i]; 564 + #endif 583 565 t->fpscr.val = vcpu->arch.fpscr; 584 566 t->fpexc_mode = 0; 585 567 kvmppc_load_up_fpu(); 586 - break; 587 - case MSR_VEC: 568 + } 569 + 570 + if (msr & MSR_VEC) { 588 571 #ifdef CONFIG_ALTIVEC 589 572 memcpy(t->vr, vcpu->arch.vr, sizeof(vcpu->arch.vr)); 590 573 t->vscr = vcpu->arch.vscr; 591 574 t->vrsave = -1; 592 575 kvmppc_load_up_altivec(); 593 576 #endif 594 - break; 595 - case MSR_VSX: 596 - #ifdef CONFIG_VSX 597 - for (i = 0; i < ARRAY_SIZE(vcpu->arch.vsr); i++) 598 - thread_fpr[get_fpr_index(i) + 1] = vcpu_vsx[i]; 599 - kvmppc_load_up_vsx(); 600 - #endif 601 - break; 602 - default: 603 - BUG(); 604 577 } 605 578 606 579 vcpu->arch.guest_owned_ext |= msr; 607 - 608 580 kvmppc_recalc_shadow_msr(vcpu); 609 581 610 582 return RESUME_GUEST; ··· 1146 1134 /* Save VSX state in stack */ 1147 1135 used_vsr = current->thread.used_vsr; 1148 1136 if (used_vsr && (current->thread.regs->msr & MSR_VSX)) 1149 - __giveup_vsx(current); 1137 + __giveup_vsx(current); 1150 1138 #endif 1151 1139 1152 1140 /* Remember the MSR with disabled extensions */ ··· 1163 1151 /* No need for kvm_guest_exit. It's done in handle_exit. 1164 1152 We also get here with interrupts enabled. */ 1165 1153 1154 + /* Make sure we save the guest FPU/Altivec/VSX state */ 1155 + kvmppc_giveup_ext(vcpu, MSR_FP | MSR_VEC | MSR_VSX); 1156 + 1166 1157 current->thread.regs->msr = ext_msr; 1167 1158 1168 - /* Make sure we save the guest FPU/Altivec/VSX state */ 1169 - kvmppc_giveup_ext(vcpu, MSR_FP); 1170 - kvmppc_giveup_ext(vcpu, MSR_VEC); 1171 - kvmppc_giveup_ext(vcpu, MSR_VSX); 1172 - 1173 - /* Restore FPU state from stack */ 1159 + /* Restore FPU/VSX state from stack */ 1174 1160 memcpy(current->thread.fpr, fpr, sizeof(current->thread.fpr)); 1175 1161 current->thread.fpscr.val = fpscr; 1176 1162 current->thread.fpexc_mode = fpexc_mode;
-3
arch/powerpc/kvm/book3s_rmhandlers.S
··· 234 234 #ifdef CONFIG_ALTIVEC 235 235 define_load_up(altivec) 236 236 #endif 237 - #ifdef CONFIG_VSX 238 - define_load_up(vsx) 239 - #endif 240 237 241 238 #include "book3s_segment.S"