1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2021 Western Digital Corporation or its affiliates.
6 * Atish Patra <atish.patra@wdc.com>
7 * Anup Patel <anup.patel@wdc.com>
10 #include <linux/errno.h>
11 #include <linux/err.h>
12 #include <linux/kvm_host.h>
13 #include <linux/uaccess.h>
14 #include <asm/cpufeature.h>
17 void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu
*vcpu
)
19 struct kvm_cpu_context
*cntx
= &vcpu
->arch
.guest_context
;
21 cntx
->sstatus
&= ~SR_FS
;
22 if (riscv_isa_extension_available(vcpu
->arch
.isa
, f
) ||
23 riscv_isa_extension_available(vcpu
->arch
.isa
, d
))
24 cntx
->sstatus
|= SR_FS_INITIAL
;
26 cntx
->sstatus
|= SR_FS_OFF
;
29 static void kvm_riscv_vcpu_fp_clean(struct kvm_cpu_context
*cntx
)
31 cntx
->sstatus
&= ~SR_FS
;
32 cntx
->sstatus
|= SR_FS_CLEAN
;
35 void kvm_riscv_vcpu_guest_fp_save(struct kvm_cpu_context
*cntx
,
36 const unsigned long *isa
)
38 if ((cntx
->sstatus
& SR_FS
) == SR_FS_DIRTY
) {
39 if (riscv_isa_extension_available(isa
, d
))
40 __kvm_riscv_fp_d_save(cntx
);
41 else if (riscv_isa_extension_available(isa
, f
))
42 __kvm_riscv_fp_f_save(cntx
);
43 kvm_riscv_vcpu_fp_clean(cntx
);
47 void kvm_riscv_vcpu_guest_fp_restore(struct kvm_cpu_context
*cntx
,
48 const unsigned long *isa
)
50 if ((cntx
->sstatus
& SR_FS
) != SR_FS_OFF
) {
51 if (riscv_isa_extension_available(isa
, d
))
52 __kvm_riscv_fp_d_restore(cntx
);
53 else if (riscv_isa_extension_available(isa
, f
))
54 __kvm_riscv_fp_f_restore(cntx
);
55 kvm_riscv_vcpu_fp_clean(cntx
);
59 void kvm_riscv_vcpu_host_fp_save(struct kvm_cpu_context
*cntx
)
61 /* No need to check host sstatus as it can be modified outside */
62 if (riscv_isa_extension_available(NULL
, d
))
63 __kvm_riscv_fp_d_save(cntx
);
64 else if (riscv_isa_extension_available(NULL
, f
))
65 __kvm_riscv_fp_f_save(cntx
);
68 void kvm_riscv_vcpu_host_fp_restore(struct kvm_cpu_context
*cntx
)
70 if (riscv_isa_extension_available(NULL
, d
))
71 __kvm_riscv_fp_d_restore(cntx
);
72 else if (riscv_isa_extension_available(NULL
, f
))
73 __kvm_riscv_fp_f_restore(cntx
);
77 int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu
*vcpu
,
78 const struct kvm_one_reg
*reg
,
81 struct kvm_cpu_context
*cntx
= &vcpu
->arch
.guest_context
;
82 unsigned long __user
*uaddr
=
83 (unsigned long __user
*)(unsigned long)reg
->addr
;
84 unsigned long reg_num
= reg
->id
& ~(KVM_REG_ARCH_MASK
|
89 if ((rtype
== KVM_REG_RISCV_FP_F
) &&
90 riscv_isa_extension_available(vcpu
->arch
.isa
, f
)) {
91 if (KVM_REG_SIZE(reg
->id
) != sizeof(u32
))
93 if (reg_num
== KVM_REG_RISCV_FP_F_REG(fcsr
))
94 reg_val
= &cntx
->fp
.f
.fcsr
;
95 else if ((KVM_REG_RISCV_FP_F_REG(f
[0]) <= reg_num
) &&
96 reg_num
<= KVM_REG_RISCV_FP_F_REG(f
[31]))
97 reg_val
= &cntx
->fp
.f
.f
[reg_num
];
100 } else if ((rtype
== KVM_REG_RISCV_FP_D
) &&
101 riscv_isa_extension_available(vcpu
->arch
.isa
, d
)) {
102 if (reg_num
== KVM_REG_RISCV_FP_D_REG(fcsr
)) {
103 if (KVM_REG_SIZE(reg
->id
) != sizeof(u32
))
105 reg_val
= &cntx
->fp
.d
.fcsr
;
106 } else if ((KVM_REG_RISCV_FP_D_REG(f
[0]) <= reg_num
) &&
107 reg_num
<= KVM_REG_RISCV_FP_D_REG(f
[31])) {
108 if (KVM_REG_SIZE(reg
->id
) != sizeof(u64
))
110 reg_val
= &cntx
->fp
.d
.f
[reg_num
];
116 if (copy_to_user(uaddr
, reg_val
, KVM_REG_SIZE(reg
->id
)))
122 int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu
*vcpu
,
123 const struct kvm_one_reg
*reg
,
126 struct kvm_cpu_context
*cntx
= &vcpu
->arch
.guest_context
;
127 unsigned long __user
*uaddr
=
128 (unsigned long __user
*)(unsigned long)reg
->addr
;
129 unsigned long reg_num
= reg
->id
& ~(KVM_REG_ARCH_MASK
|
134 if ((rtype
== KVM_REG_RISCV_FP_F
) &&
135 riscv_isa_extension_available(vcpu
->arch
.isa
, f
)) {
136 if (KVM_REG_SIZE(reg
->id
) != sizeof(u32
))
138 if (reg_num
== KVM_REG_RISCV_FP_F_REG(fcsr
))
139 reg_val
= &cntx
->fp
.f
.fcsr
;
140 else if ((KVM_REG_RISCV_FP_F_REG(f
[0]) <= reg_num
) &&
141 reg_num
<= KVM_REG_RISCV_FP_F_REG(f
[31]))
142 reg_val
= &cntx
->fp
.f
.f
[reg_num
];
145 } else if ((rtype
== KVM_REG_RISCV_FP_D
) &&
146 riscv_isa_extension_available(vcpu
->arch
.isa
, d
)) {
147 if (reg_num
== KVM_REG_RISCV_FP_D_REG(fcsr
)) {
148 if (KVM_REG_SIZE(reg
->id
) != sizeof(u32
))
150 reg_val
= &cntx
->fp
.d
.fcsr
;
151 } else if ((KVM_REG_RISCV_FP_D_REG(f
[0]) <= reg_num
) &&
152 reg_num
<= KVM_REG_RISCV_FP_D_REG(f
[31])) {
153 if (KVM_REG_SIZE(reg
->id
) != sizeof(u64
))
155 reg_val
= &cntx
->fp
.d
.f
[reg_num
];
161 if (copy_from_user(reg_val
, uaddr
, KVM_REG_SIZE(reg
->id
)))