15fed84a8SAndrew Jones // SPDX-License-Identifier: GPL-2.0
25fed84a8SAndrew Jones /*
35fed84a8SAndrew Jones * Copyright (c) 2023 Ventana Micro Systems Inc.
45fed84a8SAndrew Jones */
55fed84a8SAndrew Jones
6f61ce890SAndrew Jones #include <linux/kconfig.h>
7f61ce890SAndrew Jones #include <linux/kernel.h>
85fed84a8SAndrew Jones #include <linux/kvm_host.h>
9e9f12b5fSAndrew Jones #include <linux/mm.h>
10e9f12b5fSAndrew Jones #include <linux/sizes.h>
115fed84a8SAndrew Jones
12e9f12b5fSAndrew Jones #include <asm/bug.h>
13e9f12b5fSAndrew Jones #include <asm/current.h>
145fed84a8SAndrew Jones #include <asm/kvm_vcpu_sbi.h>
15e9f12b5fSAndrew Jones #include <asm/page.h>
165fed84a8SAndrew Jones #include <asm/sbi.h>
17e9f12b5fSAndrew Jones #include <asm/uaccess.h>
185fed84a8SAndrew Jones
kvm_riscv_vcpu_sbi_sta_reset(struct kvm_vcpu * vcpu)1938b3390eSAndrew Jones void kvm_riscv_vcpu_sbi_sta_reset(struct kvm_vcpu *vcpu)
2038b3390eSAndrew Jones {
2138b3390eSAndrew Jones vcpu->arch.sta.shmem = INVALID_GPA;
2238b3390eSAndrew Jones vcpu->arch.sta.last_steal = 0;
2338b3390eSAndrew Jones }
2438b3390eSAndrew Jones
kvm_riscv_vcpu_record_steal_time(struct kvm_vcpu * vcpu)252a1f6bf0SAndrew Jones void kvm_riscv_vcpu_record_steal_time(struct kvm_vcpu *vcpu)
262a1f6bf0SAndrew Jones {
2738b3390eSAndrew Jones gpa_t shmem = vcpu->arch.sta.shmem;
28e9f12b5fSAndrew Jones u64 last_steal = vcpu->arch.sta.last_steal;
29f072b272SAndrew Jones __le32 __user *sequence_ptr;
30f072b272SAndrew Jones __le64 __user *steal_ptr;
31f072b272SAndrew Jones __le32 sequence_le;
32f072b272SAndrew Jones __le64 steal_le;
33f072b272SAndrew Jones u32 sequence;
34f072b272SAndrew Jones u64 steal;
35e9f12b5fSAndrew Jones unsigned long hva;
36e9f12b5fSAndrew Jones gfn_t gfn;
3738b3390eSAndrew Jones
3838b3390eSAndrew Jones if (shmem == INVALID_GPA)
3938b3390eSAndrew Jones return;
40e9f12b5fSAndrew Jones
41e9f12b5fSAndrew Jones /*
42e9f12b5fSAndrew Jones * shmem is 64-byte aligned (see the enforcement in
43e9f12b5fSAndrew Jones * kvm_sbi_sta_steal_time_set_shmem()) and the size of sbi_sta_struct
44e9f12b5fSAndrew Jones * is 64 bytes, so we know all its offsets are in the same page.
45e9f12b5fSAndrew Jones */
46e9f12b5fSAndrew Jones gfn = shmem >> PAGE_SHIFT;
47e9f12b5fSAndrew Jones hva = kvm_vcpu_gfn_to_hva(vcpu, gfn);
48e9f12b5fSAndrew Jones
49e9f12b5fSAndrew Jones if (WARN_ON(kvm_is_error_hva(hva))) {
50e9f12b5fSAndrew Jones vcpu->arch.sta.shmem = INVALID_GPA;
51e9f12b5fSAndrew Jones return;
52e9f12b5fSAndrew Jones }
53e9f12b5fSAndrew Jones
54f072b272SAndrew Jones sequence_ptr = (__le32 __user *)(hva + offset_in_page(shmem) +
55e9f12b5fSAndrew Jones offsetof(struct sbi_sta_struct, sequence));
56f072b272SAndrew Jones steal_ptr = (__le64 __user *)(hva + offset_in_page(shmem) +
57e9f12b5fSAndrew Jones offsetof(struct sbi_sta_struct, steal));
58e9f12b5fSAndrew Jones
59f072b272SAndrew Jones if (WARN_ON(get_user(sequence_le, sequence_ptr)))
60e9f12b5fSAndrew Jones return;
61e9f12b5fSAndrew Jones
62f072b272SAndrew Jones sequence = le32_to_cpu(sequence_le);
63e9f12b5fSAndrew Jones sequence += 1;
64e9f12b5fSAndrew Jones
65e9f12b5fSAndrew Jones if (WARN_ON(put_user(cpu_to_le32(sequence), sequence_ptr)))
66e9f12b5fSAndrew Jones return;
67e9f12b5fSAndrew Jones
68f072b272SAndrew Jones if (!WARN_ON(get_user(steal_le, steal_ptr))) {
69f072b272SAndrew Jones steal = le64_to_cpu(steal_le);
70e9f12b5fSAndrew Jones vcpu->arch.sta.last_steal = READ_ONCE(current->sched_info.run_delay);
71e9f12b5fSAndrew Jones steal += vcpu->arch.sta.last_steal - last_steal;
72e9f12b5fSAndrew Jones WARN_ON(put_user(cpu_to_le64(steal), steal_ptr));
73e9f12b5fSAndrew Jones }
74e9f12b5fSAndrew Jones
75e9f12b5fSAndrew Jones sequence += 1;
76e9f12b5fSAndrew Jones WARN_ON(put_user(cpu_to_le32(sequence), sequence_ptr));
77e9f12b5fSAndrew Jones
78e9f12b5fSAndrew Jones kvm_vcpu_mark_page_dirty(vcpu, gfn);
792a1f6bf0SAndrew Jones }
802a1f6bf0SAndrew Jones
kvm_sbi_sta_steal_time_set_shmem(struct kvm_vcpu * vcpu)815fed84a8SAndrew Jones static int kvm_sbi_sta_steal_time_set_shmem(struct kvm_vcpu *vcpu)
825fed84a8SAndrew Jones {
83e9f12b5fSAndrew Jones struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
84e9f12b5fSAndrew Jones unsigned long shmem_phys_lo = cp->a0;
85e9f12b5fSAndrew Jones unsigned long shmem_phys_hi = cp->a1;
86e9f12b5fSAndrew Jones u32 flags = cp->a2;
87e9f12b5fSAndrew Jones struct sbi_sta_struct zero_sta = {0};
88e9f12b5fSAndrew Jones unsigned long hva;
89e9f12b5fSAndrew Jones bool writable;
90e9f12b5fSAndrew Jones gpa_t shmem;
91e9f12b5fSAndrew Jones int ret;
92e9f12b5fSAndrew Jones
93e9f12b5fSAndrew Jones if (flags != 0)
94e9f12b5fSAndrew Jones return SBI_ERR_INVALID_PARAM;
95e9f12b5fSAndrew Jones
96*3ddb6d4dSAtish Patra if (shmem_phys_lo == SBI_SHMEM_DISABLE &&
97*3ddb6d4dSAtish Patra shmem_phys_hi == SBI_SHMEM_DISABLE) {
98e9f12b5fSAndrew Jones vcpu->arch.sta.shmem = INVALID_GPA;
99e9f12b5fSAndrew Jones return 0;
100e9f12b5fSAndrew Jones }
101e9f12b5fSAndrew Jones
102e9f12b5fSAndrew Jones if (shmem_phys_lo & (SZ_64 - 1))
103e9f12b5fSAndrew Jones return SBI_ERR_INVALID_PARAM;
104e9f12b5fSAndrew Jones
105e9f12b5fSAndrew Jones shmem = shmem_phys_lo;
106e9f12b5fSAndrew Jones
107e9f12b5fSAndrew Jones if (shmem_phys_hi != 0) {
108e9f12b5fSAndrew Jones if (IS_ENABLED(CONFIG_32BIT))
109e9f12b5fSAndrew Jones shmem |= ((gpa_t)shmem_phys_hi << 32);
110e9f12b5fSAndrew Jones else
111e9f12b5fSAndrew Jones return SBI_ERR_INVALID_ADDRESS;
112e9f12b5fSAndrew Jones }
113e9f12b5fSAndrew Jones
114e9f12b5fSAndrew Jones hva = kvm_vcpu_gfn_to_hva_prot(vcpu, shmem >> PAGE_SHIFT, &writable);
115e9f12b5fSAndrew Jones if (kvm_is_error_hva(hva) || !writable)
116e9f12b5fSAndrew Jones return SBI_ERR_INVALID_ADDRESS;
117e9f12b5fSAndrew Jones
118e9f12b5fSAndrew Jones ret = kvm_vcpu_write_guest(vcpu, shmem, &zero_sta, sizeof(zero_sta));
119e9f12b5fSAndrew Jones if (ret)
1205fed84a8SAndrew Jones return SBI_ERR_FAILURE;
121e9f12b5fSAndrew Jones
122e9f12b5fSAndrew Jones vcpu->arch.sta.shmem = shmem;
123e9f12b5fSAndrew Jones vcpu->arch.sta.last_steal = current->sched_info.run_delay;
124e9f12b5fSAndrew Jones
125e9f12b5fSAndrew Jones return 0;
1265fed84a8SAndrew Jones }
1275fed84a8SAndrew Jones
kvm_sbi_ext_sta_handler(struct kvm_vcpu * vcpu,struct kvm_run * run,struct kvm_vcpu_sbi_return * retdata)1285fed84a8SAndrew Jones static int kvm_sbi_ext_sta_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
1295fed84a8SAndrew Jones struct kvm_vcpu_sbi_return *retdata)
1305fed84a8SAndrew Jones {
1315fed84a8SAndrew Jones struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
1325fed84a8SAndrew Jones unsigned long funcid = cp->a6;
1335fed84a8SAndrew Jones int ret;
1345fed84a8SAndrew Jones
1355fed84a8SAndrew Jones switch (funcid) {
1365fed84a8SAndrew Jones case SBI_EXT_STA_STEAL_TIME_SET_SHMEM:
1375fed84a8SAndrew Jones ret = kvm_sbi_sta_steal_time_set_shmem(vcpu);
1385fed84a8SAndrew Jones break;
1395fed84a8SAndrew Jones default:
1405fed84a8SAndrew Jones ret = SBI_ERR_NOT_SUPPORTED;
1415fed84a8SAndrew Jones break;
1425fed84a8SAndrew Jones }
1435fed84a8SAndrew Jones
1445fed84a8SAndrew Jones retdata->err_val = ret;
1455fed84a8SAndrew Jones
1465fed84a8SAndrew Jones return 0;
1475fed84a8SAndrew Jones }
1485fed84a8SAndrew Jones
kvm_sbi_ext_sta_probe(struct kvm_vcpu * vcpu)1495fed84a8SAndrew Jones static unsigned long kvm_sbi_ext_sta_probe(struct kvm_vcpu *vcpu)
1505fed84a8SAndrew Jones {
151e9f12b5fSAndrew Jones return !!sched_info_on();
1525fed84a8SAndrew Jones }
1535fed84a8SAndrew Jones
1545fed84a8SAndrew Jones const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_sta = {
1555fed84a8SAndrew Jones .extid_start = SBI_EXT_STA,
1565fed84a8SAndrew Jones .extid_end = SBI_EXT_STA,
1575fed84a8SAndrew Jones .handler = kvm_sbi_ext_sta_handler,
1585fed84a8SAndrew Jones .probe = kvm_sbi_ext_sta_probe,
1595fed84a8SAndrew Jones };
160f61ce890SAndrew Jones
kvm_riscv_vcpu_get_reg_sbi_sta(struct kvm_vcpu * vcpu,unsigned long reg_num,unsigned long * reg_val)161f61ce890SAndrew Jones int kvm_riscv_vcpu_get_reg_sbi_sta(struct kvm_vcpu *vcpu,
162f61ce890SAndrew Jones unsigned long reg_num,
163f61ce890SAndrew Jones unsigned long *reg_val)
164f61ce890SAndrew Jones {
165f61ce890SAndrew Jones switch (reg_num) {
166f61ce890SAndrew Jones case KVM_REG_RISCV_SBI_STA_REG(shmem_lo):
167f61ce890SAndrew Jones *reg_val = (unsigned long)vcpu->arch.sta.shmem;
168f61ce890SAndrew Jones break;
169f61ce890SAndrew Jones case KVM_REG_RISCV_SBI_STA_REG(shmem_hi):
170f61ce890SAndrew Jones if (IS_ENABLED(CONFIG_32BIT))
171f61ce890SAndrew Jones *reg_val = upper_32_bits(vcpu->arch.sta.shmem);
172f61ce890SAndrew Jones else
173f61ce890SAndrew Jones *reg_val = 0;
174f61ce890SAndrew Jones break;
175f61ce890SAndrew Jones default:
176f61ce890SAndrew Jones return -EINVAL;
177f61ce890SAndrew Jones }
178f61ce890SAndrew Jones
179f61ce890SAndrew Jones return 0;
180f61ce890SAndrew Jones }
181f61ce890SAndrew Jones
kvm_riscv_vcpu_set_reg_sbi_sta(struct kvm_vcpu * vcpu,unsigned long reg_num,unsigned long reg_val)182f61ce890SAndrew Jones int kvm_riscv_vcpu_set_reg_sbi_sta(struct kvm_vcpu *vcpu,
183f61ce890SAndrew Jones unsigned long reg_num,
184f61ce890SAndrew Jones unsigned long reg_val)
185f61ce890SAndrew Jones {
186f61ce890SAndrew Jones switch (reg_num) {
187f61ce890SAndrew Jones case KVM_REG_RISCV_SBI_STA_REG(shmem_lo):
188f61ce890SAndrew Jones if (IS_ENABLED(CONFIG_32BIT)) {
189f61ce890SAndrew Jones gpa_t hi = upper_32_bits(vcpu->arch.sta.shmem);
190f61ce890SAndrew Jones
191f61ce890SAndrew Jones vcpu->arch.sta.shmem = reg_val;
192f61ce890SAndrew Jones vcpu->arch.sta.shmem |= hi << 32;
193f61ce890SAndrew Jones } else {
194f61ce890SAndrew Jones vcpu->arch.sta.shmem = reg_val;
195f61ce890SAndrew Jones }
196f61ce890SAndrew Jones break;
197f61ce890SAndrew Jones case KVM_REG_RISCV_SBI_STA_REG(shmem_hi):
198f61ce890SAndrew Jones if (IS_ENABLED(CONFIG_32BIT)) {
199f61ce890SAndrew Jones gpa_t lo = lower_32_bits(vcpu->arch.sta.shmem);
200f61ce890SAndrew Jones
201f61ce890SAndrew Jones vcpu->arch.sta.shmem = ((gpa_t)reg_val << 32);
202f61ce890SAndrew Jones vcpu->arch.sta.shmem |= lo;
203f61ce890SAndrew Jones } else if (reg_val != 0) {
204f61ce890SAndrew Jones return -EINVAL;
205f61ce890SAndrew Jones }
206f61ce890SAndrew Jones break;
207f61ce890SAndrew Jones default:
208f61ce890SAndrew Jones return -EINVAL;
209f61ce890SAndrew Jones }
210f61ce890SAndrew Jones
211f61ce890SAndrew Jones return 0;
212f61ce890SAndrew Jones }
213