1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2023 Ventana Micro Systems Inc. 4 */ 5 6 #include <linux/kconfig.h> 7 #include <linux/kernel.h> 8 #include <linux/kvm_host.h> 9 #include <linux/mm.h> 10 #include <linux/sizes.h> 11 12 #include <asm/bug.h> 13 #include <asm/current.h> 14 #include <asm/kvm_vcpu_sbi.h> 15 #include <asm/page.h> 16 #include <asm/sbi.h> 17 #include <asm/uaccess.h> 18 19 static void kvm_riscv_vcpu_sbi_sta_reset(struct kvm_vcpu *vcpu) 20 { 21 vcpu->arch.sta.shmem = INVALID_GPA; 22 vcpu->arch.sta.last_steal = 0; 23 } 24 25 void kvm_riscv_vcpu_record_steal_time(struct kvm_vcpu *vcpu) 26 { 27 gpa_t shmem = vcpu->arch.sta.shmem; 28 u64 last_steal = vcpu->arch.sta.last_steal; 29 __le32 __user *sequence_ptr; 30 __le64 __user *steal_ptr; 31 __le32 sequence_le; 32 __le64 steal_le; 33 u32 sequence; 34 u64 steal; 35 unsigned long hva; 36 gfn_t gfn; 37 38 if (shmem == INVALID_GPA) 39 return; 40 41 /* 42 * shmem is 64-byte aligned (see the enforcement in 43 * kvm_sbi_sta_steal_time_set_shmem()) and the size of sbi_sta_struct 44 * is 64 bytes, so we know all its offsets are in the same page. 45 */ 46 gfn = shmem >> PAGE_SHIFT; 47 hva = kvm_vcpu_gfn_to_hva(vcpu, gfn); 48 49 if (WARN_ON(kvm_is_error_hva(hva))) { 50 vcpu->arch.sta.shmem = INVALID_GPA; 51 return; 52 } 53 54 sequence_ptr = (__le32 __user *)(hva + offset_in_page(shmem) + 55 offsetof(struct sbi_sta_struct, sequence)); 56 steal_ptr = (__le64 __user *)(hva + offset_in_page(shmem) + 57 offsetof(struct sbi_sta_struct, steal)); 58 59 if (WARN_ON(get_user(sequence_le, sequence_ptr))) 60 return; 61 62 sequence = le32_to_cpu(sequence_le); 63 sequence += 1; 64 65 if (WARN_ON(put_user(cpu_to_le32(sequence), sequence_ptr))) 66 return; 67 68 if (!WARN_ON(get_user(steal_le, steal_ptr))) { 69 steal = le64_to_cpu(steal_le); 70 vcpu->arch.sta.last_steal = READ_ONCE(current->sched_info.run_delay); 71 steal += vcpu->arch.sta.last_steal - last_steal; 72 WARN_ON(put_user(cpu_to_le64(steal), steal_ptr)); 73 } 74 75 sequence += 1; 76 WARN_ON(put_user(cpu_to_le32(sequence), sequence_ptr)); 77 78 kvm_vcpu_mark_page_dirty(vcpu, gfn); 79 } 80 81 static int kvm_sbi_sta_steal_time_set_shmem(struct kvm_vcpu *vcpu) 82 { 83 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 84 unsigned long shmem_phys_lo = cp->a0; 85 unsigned long shmem_phys_hi = cp->a1; 86 u32 flags = cp->a2; 87 struct sbi_sta_struct zero_sta = {0}; 88 gpa_t shmem; 89 int ret; 90 91 if (flags != 0) 92 return SBI_ERR_INVALID_PARAM; 93 94 if (shmem_phys_lo == SBI_SHMEM_DISABLE && 95 shmem_phys_hi == SBI_SHMEM_DISABLE) { 96 vcpu->arch.sta.shmem = INVALID_GPA; 97 return 0; 98 } 99 100 if (shmem_phys_lo & (SZ_64 - 1)) 101 return SBI_ERR_INVALID_PARAM; 102 103 shmem = shmem_phys_lo; 104 105 if (shmem_phys_hi != 0) { 106 if (IS_ENABLED(CONFIG_32BIT)) 107 shmem |= ((gpa_t)shmem_phys_hi << 32); 108 else 109 return SBI_ERR_INVALID_ADDRESS; 110 } 111 112 /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */ 113 ret = kvm_vcpu_write_guest(vcpu, shmem, &zero_sta, sizeof(zero_sta)); 114 if (ret) 115 return SBI_ERR_INVALID_ADDRESS; 116 117 vcpu->arch.sta.shmem = shmem; 118 vcpu->arch.sta.last_steal = current->sched_info.run_delay; 119 120 return 0; 121 } 122 123 static int kvm_sbi_ext_sta_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, 124 struct kvm_vcpu_sbi_return *retdata) 125 { 126 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 127 unsigned long funcid = cp->a6; 128 int ret; 129 130 switch (funcid) { 131 case SBI_EXT_STA_STEAL_TIME_SET_SHMEM: 132 ret = kvm_sbi_sta_steal_time_set_shmem(vcpu); 133 break; 134 default: 135 ret = SBI_ERR_NOT_SUPPORTED; 136 break; 137 } 138 139 retdata->err_val = ret; 140 141 return 0; 142 } 143 144 static unsigned long kvm_sbi_ext_sta_probe(struct kvm_vcpu *vcpu) 145 { 146 return !!sched_info_on(); 147 } 148 149 static unsigned long kvm_sbi_ext_sta_get_state_reg_count(struct kvm_vcpu *vcpu) 150 { 151 return sizeof(struct kvm_riscv_sbi_sta) / sizeof(unsigned long); 152 } 153 154 static int kvm_sbi_ext_sta_get_reg(struct kvm_vcpu *vcpu, unsigned long reg_num, 155 unsigned long reg_size, void *reg_val) 156 { 157 unsigned long *value; 158 159 if (reg_size != sizeof(unsigned long)) 160 return -EINVAL; 161 value = reg_val; 162 163 switch (reg_num) { 164 case KVM_REG_RISCV_SBI_STA_REG(shmem_lo): 165 *value = (unsigned long)vcpu->arch.sta.shmem; 166 break; 167 case KVM_REG_RISCV_SBI_STA_REG(shmem_hi): 168 if (IS_ENABLED(CONFIG_32BIT)) 169 *value = upper_32_bits(vcpu->arch.sta.shmem); 170 else 171 *value = 0; 172 break; 173 default: 174 return -ENOENT; 175 } 176 177 return 0; 178 } 179 180 static int kvm_sbi_ext_sta_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num, 181 unsigned long reg_size, const void *reg_val) 182 { 183 unsigned long value; 184 gpa_t new_shmem = INVALID_GPA; 185 186 if (reg_size != sizeof(unsigned long)) 187 return -EINVAL; 188 value = *(const unsigned long *)reg_val; 189 190 switch (reg_num) { 191 case KVM_REG_RISCV_SBI_STA_REG(shmem_lo): 192 if (IS_ENABLED(CONFIG_32BIT)) { 193 gpa_t hi = upper_32_bits(vcpu->arch.sta.shmem); 194 195 new_shmem = value; 196 new_shmem |= hi << 32; 197 } else { 198 new_shmem = value; 199 } 200 break; 201 case KVM_REG_RISCV_SBI_STA_REG(shmem_hi): 202 if (IS_ENABLED(CONFIG_32BIT)) { 203 gpa_t lo = lower_32_bits(vcpu->arch.sta.shmem); 204 205 new_shmem = ((gpa_t)value << 32); 206 new_shmem |= lo; 207 } else if (value != 0) { 208 return -EINVAL; 209 } 210 break; 211 default: 212 return -ENOENT; 213 } 214 215 if (new_shmem != INVALID_GPA && !IS_ALIGNED(new_shmem, 64)) 216 return -EINVAL; 217 218 vcpu->arch.sta.shmem = new_shmem; 219 220 return 0; 221 } 222 223 const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_sta = { 224 .extid_start = SBI_EXT_STA, 225 .extid_end = SBI_EXT_STA, 226 .handler = kvm_sbi_ext_sta_handler, 227 .probe = kvm_sbi_ext_sta_probe, 228 .reset = kvm_riscv_vcpu_sbi_sta_reset, 229 .state_reg_subtype = KVM_REG_RISCV_SBI_STA, 230 .get_state_reg_count = kvm_sbi_ext_sta_get_state_reg_count, 231 .get_state_reg = kvm_sbi_ext_sta_get_reg, 232 .set_state_reg = kvm_sbi_ext_sta_set_reg, 233 }; 234