xref: /linux/arch/riscv/kvm/vcpu_sbi_sta.c (revision f4b0c4b508364fde023e4f7b9f23f7e38c663dfe)
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