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

MIPS: KVM: Expose FPU registers

Add KVM register numbers for the MIPS FPU registers, and implement
access to them with the KVM_GET_ONE_REG / KVM_SET_ONE_REG ioctls when
the FPU capability is enabled (exposed in a later patch) and present in
the guest according to its Config1.FP bit.

The registers are accessible in the current mode of the guest, with each
sized access showing what the guest would see with an equivalent access,
and like the architecture they may become UNPREDICTABLE if the FR mode
is changed. When FR=0, odd doubles are inaccessible as they do not exist
in that mode.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Cc: Paul Burton <paul.burton@imgtec.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Gleb Natapov <gleb@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
Cc: linux-api@vger.kernel.org
Cc: linux-doc@vger.kernel.org

+114 -11
+16
Documentation/virtual/kvm/api.txt
··· 1979 1979 MIPS | KVM_REG_MIPS_COUNT_CTL | 64 1980 1980 MIPS | KVM_REG_MIPS_COUNT_RESUME | 64 1981 1981 MIPS | KVM_REG_MIPS_COUNT_HZ | 64 1982 + MIPS | KVM_REG_MIPS_FPR_32(0..31) | 32 1983 + MIPS | KVM_REG_MIPS_FPR_64(0..31) | 64 1984 + MIPS | KVM_REG_MIPS_FCR_IR | 32 1985 + MIPS | KVM_REG_MIPS_FCR_CSR | 32 1982 1986 1983 1987 ARM registers are mapped using the lower 32 bits. The upper 16 of that 1984 1988 is the register group type, or coprocessor number: ··· 2035 2031 2036 2032 MIPS KVM control registers (see above) have the following id bit patterns: 2037 2033 0x7030 0000 0002 <reg:16> 2034 + 2035 + MIPS FPU registers (see KVM_REG_MIPS_FPR_{32,64}() above) have the following 2036 + id bit patterns depending on the size of the register being accessed. They are 2037 + always accessed according to the current guest FPU mode (Status.FR and 2038 + Config5.FRE), i.e. as the guest would see them, and they become unpredictable 2039 + if the guest FPU mode is changed: 2040 + 0x7020 0000 0003 00 <0:3> <reg:5> (32-bit FPU registers) 2041 + 0x7030 0000 0003 00 <0:3> <reg:5> (64-bit FPU registers) 2042 + 2043 + MIPS FPU control registers (see KVM_REG_MIPS_FCR_{IR,CSR} above) have the 2044 + following id bit patterns: 2045 + 0x7020 0000 0003 01 <0:3> <reg:5> 2038 2046 2039 2047 2040 2048 4.69 KVM_GET_ONE_REG
+27 -10
arch/mips/include/uapi/asm/kvm.h
··· 36 36 37 37 /* 38 38 * for KVM_GET_FPU and KVM_SET_FPU 39 - * 40 - * If Status[FR] is zero (32-bit FPU), the upper 32-bits of the FPRs 41 - * are zero filled. 42 39 */ 43 40 struct kvm_fpu { 44 - __u64 fpr[32]; 45 - __u32 fir; 46 - __u32 fccr; 47 - __u32 fexr; 48 - __u32 fenr; 49 - __u32 fcsr; 50 - __u32 pad; 51 41 }; 52 42 53 43 ··· 58 68 * 59 69 * Register set = 2: KVM specific registers (see definitions below). 60 70 * 71 + * Register set = 3: FPU registers (see definitions below). 72 + * 61 73 * Other sets registers may be added in the future. Each set would 62 74 * have its own identifier in bits[31..16]. 63 75 */ ··· 67 75 #define KVM_REG_MIPS_GP (KVM_REG_MIPS | 0x0000000000000000ULL) 68 76 #define KVM_REG_MIPS_CP0 (KVM_REG_MIPS | 0x0000000000010000ULL) 69 77 #define KVM_REG_MIPS_KVM (KVM_REG_MIPS | 0x0000000000020000ULL) 78 + #define KVM_REG_MIPS_FPU (KVM_REG_MIPS | 0x0000000000030000ULL) 70 79 71 80 72 81 /* ··· 145 152 * discontinuities in CP0_Count. 146 153 */ 147 154 #define KVM_REG_MIPS_COUNT_HZ (KVM_REG_MIPS_KVM | KVM_REG_SIZE_U64 | 2) 155 + 156 + 157 + /* 158 + * KVM_REG_MIPS_FPU - Floating Point registers. 159 + * 160 + * bits[15..8] - Register subset (see definitions below). 161 + * bits[7..5] - Must be zero. 162 + * bits[4..0] - Register number within register subset. 163 + */ 164 + 165 + #define KVM_REG_MIPS_FPR (KVM_REG_MIPS_FPU | 0x0000000000000000ULL) 166 + #define KVM_REG_MIPS_FCR (KVM_REG_MIPS_FPU | 0x0000000000000100ULL) 167 + 168 + /* 169 + * KVM_REG_MIPS_FPR - Floating point / Vector registers. 170 + */ 171 + #define KVM_REG_MIPS_FPR_32(n) (KVM_REG_MIPS_FPR | KVM_REG_SIZE_U32 | (n)) 172 + #define KVM_REG_MIPS_FPR_64(n) (KVM_REG_MIPS_FPR | KVM_REG_SIZE_U64 | (n)) 173 + 174 + /* 175 + * KVM_REG_MIPS_FCR - Floating point control registers. 176 + */ 177 + #define KVM_REG_MIPS_FCR_IR (KVM_REG_MIPS_FCR | KVM_REG_SIZE_U32 | 0) 178 + #define KVM_REG_MIPS_FCR_CSR (KVM_REG_MIPS_FCR | KVM_REG_SIZE_U32 | 31) 148 179 149 180 150 181 /*
+71 -1
arch/mips/kvm/mips.c
··· 526 526 const struct kvm_one_reg *reg) 527 527 { 528 528 struct mips_coproc *cop0 = vcpu->arch.cop0; 529 + struct mips_fpu_struct *fpu = &vcpu->arch.fpu; 529 530 int ret; 530 531 s64 v; 532 + unsigned int idx; 531 533 532 534 switch (reg->id) { 535 + /* General purpose registers */ 533 536 case KVM_REG_MIPS_R0 ... KVM_REG_MIPS_R31: 534 537 v = (long)vcpu->arch.gprs[reg->id - KVM_REG_MIPS_R0]; 535 538 break; ··· 546 543 v = (long)vcpu->arch.pc; 547 544 break; 548 545 546 + /* Floating point registers */ 547 + case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31): 548 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 549 + return -EINVAL; 550 + idx = reg->id - KVM_REG_MIPS_FPR_32(0); 551 + /* Odd singles in top of even double when FR=0 */ 552 + if (kvm_read_c0_guest_status(cop0) & ST0_FR) 553 + v = get_fpr32(&fpu->fpr[idx], 0); 554 + else 555 + v = get_fpr32(&fpu->fpr[idx & ~1], idx & 1); 556 + break; 557 + case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31): 558 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 559 + return -EINVAL; 560 + idx = reg->id - KVM_REG_MIPS_FPR_64(0); 561 + /* Can't access odd doubles in FR=0 mode */ 562 + if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR)) 563 + return -EINVAL; 564 + v = get_fpr64(&fpu->fpr[idx], 0); 565 + break; 566 + case KVM_REG_MIPS_FCR_IR: 567 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 568 + return -EINVAL; 569 + v = boot_cpu_data.fpu_id; 570 + break; 571 + case KVM_REG_MIPS_FCR_CSR: 572 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 573 + return -EINVAL; 574 + v = fpu->fcr31; 575 + break; 576 + 577 + /* Co-processor 0 registers */ 549 578 case KVM_REG_MIPS_CP0_INDEX: 550 579 v = (long)kvm_read_c0_guest_index(cop0); 551 580 break; ··· 671 636 const struct kvm_one_reg *reg) 672 637 { 673 638 struct mips_coproc *cop0 = vcpu->arch.cop0; 674 - u64 v; 639 + struct mips_fpu_struct *fpu = &vcpu->arch.fpu; 640 + s64 v; 641 + unsigned int idx; 675 642 676 643 if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) { 677 644 u64 __user *uaddr64 = (u64 __user *)(long)reg->addr; ··· 692 655 } 693 656 694 657 switch (reg->id) { 658 + /* General purpose registers */ 695 659 case KVM_REG_MIPS_R0: 696 660 /* Silently ignore requests to set $0 */ 697 661 break; ··· 709 671 vcpu->arch.pc = v; 710 672 break; 711 673 674 + /* Floating point registers */ 675 + case KVM_REG_MIPS_FPR_32(0) ... KVM_REG_MIPS_FPR_32(31): 676 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 677 + return -EINVAL; 678 + idx = reg->id - KVM_REG_MIPS_FPR_32(0); 679 + /* Odd singles in top of even double when FR=0 */ 680 + if (kvm_read_c0_guest_status(cop0) & ST0_FR) 681 + set_fpr32(&fpu->fpr[idx], 0, v); 682 + else 683 + set_fpr32(&fpu->fpr[idx & ~1], idx & 1, v); 684 + break; 685 + case KVM_REG_MIPS_FPR_64(0) ... KVM_REG_MIPS_FPR_64(31): 686 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 687 + return -EINVAL; 688 + idx = reg->id - KVM_REG_MIPS_FPR_64(0); 689 + /* Can't access odd doubles in FR=0 mode */ 690 + if (idx & 1 && !(kvm_read_c0_guest_status(cop0) & ST0_FR)) 691 + return -EINVAL; 692 + set_fpr64(&fpu->fpr[idx], 0, v); 693 + break; 694 + case KVM_REG_MIPS_FCR_IR: 695 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 696 + return -EINVAL; 697 + /* Read-only */ 698 + break; 699 + case KVM_REG_MIPS_FCR_CSR: 700 + if (!kvm_mips_guest_has_fpu(&vcpu->arch)) 701 + return -EINVAL; 702 + fpu->fcr31 = v; 703 + break; 704 + 705 + /* Co-processor 0 registers */ 712 706 case KVM_REG_MIPS_CP0_INDEX: 713 707 kvm_write_c0_guest_index(cop0, v); 714 708 break;