vcpu_fp.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2021 Western Digital Corporation or its affiliates.
  4. *
  5. * Authors:
  6. * Atish Patra <atish.patra@wdc.com>
  7. * Anup Patel <anup.patel@wdc.com>
  8. */
  9. #include <linux/errno.h>
  10. #include <linux/err.h>
  11. #include <linux/kvm_host.h>
  12. #include <linux/nospec.h>
  13. #include <linux/uaccess.h>
  14. #include <asm/cpufeature.h>
  15. #ifdef CONFIG_FPU
  16. void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu)
  17. {
  18. struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
  19. cntx->sstatus &= ~SR_FS;
  20. if (riscv_isa_extension_available(vcpu->arch.isa, f) ||
  21. riscv_isa_extension_available(vcpu->arch.isa, d))
  22. cntx->sstatus |= SR_FS_INITIAL;
  23. else
  24. cntx->sstatus |= SR_FS_OFF;
  25. }
  26. static void kvm_riscv_vcpu_fp_clean(struct kvm_cpu_context *cntx)
  27. {
  28. cntx->sstatus &= ~SR_FS;
  29. cntx->sstatus |= SR_FS_CLEAN;
  30. }
  31. void kvm_riscv_vcpu_guest_fp_save(struct kvm_cpu_context *cntx,
  32. const unsigned long *isa)
  33. {
  34. if ((cntx->sstatus & SR_FS) == SR_FS_DIRTY) {
  35. if (riscv_isa_extension_available(isa, d))
  36. __kvm_riscv_fp_d_save(cntx);
  37. else if (riscv_isa_extension_available(isa, f))
  38. __kvm_riscv_fp_f_save(cntx);
  39. kvm_riscv_vcpu_fp_clean(cntx);
  40. }
  41. }
  42. void kvm_riscv_vcpu_guest_fp_restore(struct kvm_cpu_context *cntx,
  43. const unsigned long *isa)
  44. {
  45. if ((cntx->sstatus & SR_FS) != SR_FS_OFF) {
  46. if (riscv_isa_extension_available(isa, d))
  47. __kvm_riscv_fp_d_restore(cntx);
  48. else if (riscv_isa_extension_available(isa, f))
  49. __kvm_riscv_fp_f_restore(cntx);
  50. kvm_riscv_vcpu_fp_clean(cntx);
  51. }
  52. }
  53. void kvm_riscv_vcpu_host_fp_save(struct kvm_cpu_context *cntx)
  54. {
  55. /* No need to check host sstatus as it can be modified outside */
  56. if (riscv_isa_extension_available(NULL, d))
  57. __kvm_riscv_fp_d_save(cntx);
  58. else if (riscv_isa_extension_available(NULL, f))
  59. __kvm_riscv_fp_f_save(cntx);
  60. }
  61. void kvm_riscv_vcpu_host_fp_restore(struct kvm_cpu_context *cntx)
  62. {
  63. if (riscv_isa_extension_available(NULL, d))
  64. __kvm_riscv_fp_d_restore(cntx);
  65. else if (riscv_isa_extension_available(NULL, f))
  66. __kvm_riscv_fp_f_restore(cntx);
  67. }
  68. #endif
  69. int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu,
  70. const struct kvm_one_reg *reg,
  71. unsigned long rtype)
  72. {
  73. struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
  74. unsigned long __user *uaddr =
  75. (unsigned long __user *)(unsigned long)reg->addr;
  76. unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
  77. KVM_REG_SIZE_MASK |
  78. rtype);
  79. void *reg_val;
  80. if ((rtype == KVM_REG_RISCV_FP_F) &&
  81. riscv_isa_extension_available(vcpu->arch.isa, f)) {
  82. if (KVM_REG_SIZE(reg->id) != sizeof(u32))
  83. return -EINVAL;
  84. if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr))
  85. reg_val = &cntx->fp.f.fcsr;
  86. else if ((KVM_REG_RISCV_FP_F_REG(f[0]) <= reg_num) &&
  87. reg_num <= KVM_REG_RISCV_FP_F_REG(f[31])) {
  88. reg_num = array_index_nospec(reg_num,
  89. ARRAY_SIZE(cntx->fp.f.f));
  90. reg_val = &cntx->fp.f.f[reg_num];
  91. } else
  92. return -ENOENT;
  93. } else if ((rtype == KVM_REG_RISCV_FP_D) &&
  94. riscv_isa_extension_available(vcpu->arch.isa, d)) {
  95. if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) {
  96. if (KVM_REG_SIZE(reg->id) != sizeof(u32))
  97. return -EINVAL;
  98. reg_val = &cntx->fp.d.fcsr;
  99. } else if ((KVM_REG_RISCV_FP_D_REG(f[0]) <= reg_num) &&
  100. reg_num <= KVM_REG_RISCV_FP_D_REG(f[31])) {
  101. if (KVM_REG_SIZE(reg->id) != sizeof(u64))
  102. return -EINVAL;
  103. reg_num = array_index_nospec(reg_num,
  104. ARRAY_SIZE(cntx->fp.d.f));
  105. reg_val = &cntx->fp.d.f[reg_num];
  106. } else
  107. return -ENOENT;
  108. } else
  109. return -ENOENT;
  110. if (copy_to_user(uaddr, reg_val, KVM_REG_SIZE(reg->id)))
  111. return -EFAULT;
  112. return 0;
  113. }
  114. int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu,
  115. const struct kvm_one_reg *reg,
  116. unsigned long rtype)
  117. {
  118. struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
  119. unsigned long __user *uaddr =
  120. (unsigned long __user *)(unsigned long)reg->addr;
  121. unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
  122. KVM_REG_SIZE_MASK |
  123. rtype);
  124. void *reg_val;
  125. if ((rtype == KVM_REG_RISCV_FP_F) &&
  126. riscv_isa_extension_available(vcpu->arch.isa, f)) {
  127. if (KVM_REG_SIZE(reg->id) != sizeof(u32))
  128. return -EINVAL;
  129. if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr))
  130. reg_val = &cntx->fp.f.fcsr;
  131. else if ((KVM_REG_RISCV_FP_F_REG(f[0]) <= reg_num) &&
  132. reg_num <= KVM_REG_RISCV_FP_F_REG(f[31])) {
  133. reg_num = array_index_nospec(reg_num,
  134. ARRAY_SIZE(cntx->fp.f.f));
  135. reg_val = &cntx->fp.f.f[reg_num];
  136. } else
  137. return -ENOENT;
  138. } else if ((rtype == KVM_REG_RISCV_FP_D) &&
  139. riscv_isa_extension_available(vcpu->arch.isa, d)) {
  140. if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) {
  141. if (KVM_REG_SIZE(reg->id) != sizeof(u32))
  142. return -EINVAL;
  143. reg_val = &cntx->fp.d.fcsr;
  144. } else if ((KVM_REG_RISCV_FP_D_REG(f[0]) <= reg_num) &&
  145. reg_num <= KVM_REG_RISCV_FP_D_REG(f[31])) {
  146. if (KVM_REG_SIZE(reg->id) != sizeof(u64))
  147. return -EINVAL;
  148. reg_num = array_index_nospec(reg_num,
  149. ARRAY_SIZE(cntx->fp.d.f));
  150. reg_val = &cntx->fp.d.f[reg_num];
  151. } else
  152. return -ENOENT;
  153. } else
  154. return -ENOENT;
  155. if (copy_from_user(reg_val, uaddr, KVM_REG_SIZE(reg->id)))
  156. return -EFAULT;
  157. return 0;
  158. }