x86/boot: Wrap literal addresses in absolute_pointer()
[linux-2.6-microblaze.git] / arch / riscv / kvm / vcpu_fp.c
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
10 #include <linux/errno.h>
11 #include <linux/err.h>
12 #include <linux/kvm_host.h>
13 #include <linux/uaccess.h>
14
15 #ifdef CONFIG_FPU
16 void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu)
17 {
18         unsigned long isa = vcpu->arch.isa;
19         struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
20
21         cntx->sstatus &= ~SR_FS;
22         if (riscv_isa_extension_available(&isa, f) ||
23             riscv_isa_extension_available(&isa, d))
24                 cntx->sstatus |= SR_FS_INITIAL;
25         else
26                 cntx->sstatus |= SR_FS_OFF;
27 }
28
29 static void kvm_riscv_vcpu_fp_clean(struct kvm_cpu_context *cntx)
30 {
31         cntx->sstatus &= ~SR_FS;
32         cntx->sstatus |= SR_FS_CLEAN;
33 }
34
35 void kvm_riscv_vcpu_guest_fp_save(struct kvm_cpu_context *cntx,
36                                   unsigned long isa)
37 {
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);
44         }
45 }
46
47 void kvm_riscv_vcpu_guest_fp_restore(struct kvm_cpu_context *cntx,
48                                      unsigned long isa)
49 {
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);
56         }
57 }
58
59 void kvm_riscv_vcpu_host_fp_save(struct kvm_cpu_context *cntx)
60 {
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);
66 }
67
68 void kvm_riscv_vcpu_host_fp_restore(struct kvm_cpu_context *cntx)
69 {
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);
74 }
75 #endif
76
77 int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu,
78                               const struct kvm_one_reg *reg,
79                               unsigned long rtype)
80 {
81         struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
82         unsigned long isa = vcpu->arch.isa;
83         unsigned long __user *uaddr =
84                         (unsigned long __user *)(unsigned long)reg->addr;
85         unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
86                                             KVM_REG_SIZE_MASK |
87                                             rtype);
88         void *reg_val;
89
90         if ((rtype == KVM_REG_RISCV_FP_F) &&
91             riscv_isa_extension_available(&isa, f)) {
92                 if (KVM_REG_SIZE(reg->id) != sizeof(u32))
93                         return -EINVAL;
94                 if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr))
95                         reg_val = &cntx->fp.f.fcsr;
96                 else if ((KVM_REG_RISCV_FP_F_REG(f[0]) <= reg_num) &&
97                           reg_num <= KVM_REG_RISCV_FP_F_REG(f[31]))
98                         reg_val = &cntx->fp.f.f[reg_num];
99                 else
100                         return -EINVAL;
101         } else if ((rtype == KVM_REG_RISCV_FP_D) &&
102                    riscv_isa_extension_available(&isa, d)) {
103                 if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) {
104                         if (KVM_REG_SIZE(reg->id) != sizeof(u32))
105                                 return -EINVAL;
106                         reg_val = &cntx->fp.d.fcsr;
107                 } else if ((KVM_REG_RISCV_FP_D_REG(f[0]) <= reg_num) &&
108                            reg_num <= KVM_REG_RISCV_FP_D_REG(f[31])) {
109                         if (KVM_REG_SIZE(reg->id) != sizeof(u64))
110                                 return -EINVAL;
111                         reg_val = &cntx->fp.d.f[reg_num];
112                 } else
113                         return -EINVAL;
114         } else
115                 return -EINVAL;
116
117         if (copy_to_user(uaddr, reg_val, KVM_REG_SIZE(reg->id)))
118                 return -EFAULT;
119
120         return 0;
121 }
122
123 int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu,
124                               const struct kvm_one_reg *reg,
125                               unsigned long rtype)
126 {
127         struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
128         unsigned long isa = vcpu->arch.isa;
129         unsigned long __user *uaddr =
130                         (unsigned long __user *)(unsigned long)reg->addr;
131         unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
132                                             KVM_REG_SIZE_MASK |
133                                             rtype);
134         void *reg_val;
135
136         if ((rtype == KVM_REG_RISCV_FP_F) &&
137             riscv_isa_extension_available(&isa, f)) {
138                 if (KVM_REG_SIZE(reg->id) != sizeof(u32))
139                         return -EINVAL;
140                 if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr))
141                         reg_val = &cntx->fp.f.fcsr;
142                 else if ((KVM_REG_RISCV_FP_F_REG(f[0]) <= reg_num) &&
143                           reg_num <= KVM_REG_RISCV_FP_F_REG(f[31]))
144                         reg_val = &cntx->fp.f.f[reg_num];
145                 else
146                         return -EINVAL;
147         } else if ((rtype == KVM_REG_RISCV_FP_D) &&
148                    riscv_isa_extension_available(&isa, d)) {
149                 if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) {
150                         if (KVM_REG_SIZE(reg->id) != sizeof(u32))
151                                 return -EINVAL;
152                         reg_val = &cntx->fp.d.fcsr;
153                 } else if ((KVM_REG_RISCV_FP_D_REG(f[0]) <= reg_num) &&
154                            reg_num <= KVM_REG_RISCV_FP_D_REG(f[31])) {
155                         if (KVM_REG_SIZE(reg->id) != sizeof(u64))
156                                 return -EINVAL;
157                         reg_val = &cntx->fp.d.f[reg_num];
158                 } else
159                         return -EINVAL;
160         } else
161                 return -EINVAL;
162
163         if (copy_from_user(reg_val, uaddr, KVM_REG_SIZE(reg->id)))
164                 return -EFAULT;
165
166         return 0;
167 }