Commit f61ce890 authored by Andrew Jones's avatar Andrew Jones Committed by Anup Patel
Browse files

RISC-V: KVM: Add support for SBI STA registers



KVM userspace needs to be able to save and restore the steal-time
shared memory address. Provide the address through the get/set-one-reg
interface with two ulong-sized SBI STA extension registers (lo and hi).
64-bit KVM userspace must not set the hi register to anything other
than zero and is allowed to completely neglect saving/restoring it.

Reviewed-by: default avatarAnup Patel <anup@brainfault.org>
Signed-off-by: default avatarAndrew Jones <ajones@ventanamicro.com>
Signed-off-by: default avatarAnup Patel <anup@brainfault.org>
parent 5b9e4132
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -70,6 +70,11 @@ bool riscv_vcpu_supports_sbi_ext(struct kvm_vcpu *vcpu, int idx);
int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run);
void kvm_riscv_vcpu_sbi_init(struct kvm_vcpu *vcpu);

int kvm_riscv_vcpu_get_reg_sbi_sta(struct kvm_vcpu *vcpu, unsigned long reg_num,
				   unsigned long *reg_val);
int kvm_riscv_vcpu_set_reg_sbi_sta(struct kvm_vcpu *vcpu, unsigned long reg_num,
				   unsigned long reg_val);

#ifdef CONFIG_RISCV_SBI_V01
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01;
#endif
+9 −0
Original line number Diff line number Diff line
@@ -161,6 +161,12 @@ enum KVM_RISCV_SBI_EXT_ID {
	KVM_RISCV_SBI_EXT_MAX,
};

/* SBI STA extension registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
struct kvm_riscv_sbi_sta {
	unsigned long shmem_lo;
	unsigned long shmem_hi;
};

/* Possible states for kvm_riscv_timer */
#define KVM_RISCV_TIMER_STATE_OFF	0
#define KVM_RISCV_TIMER_STATE_ON	1
@@ -244,6 +250,9 @@ enum KVM_RISCV_SBI_EXT_ID {

/* Registers for specific SBI extensions are mapped as type 10 */
#define KVM_REG_RISCV_SBI_STATE		(0x0a << KVM_REG_RISCV_TYPE_SHIFT)
#define KVM_REG_RISCV_SBI_STA		(0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT)
#define KVM_REG_RISCV_SBI_STA_REG(name)		\
		(offsetof(struct kvm_riscv_sbi_sta, name) / sizeof(unsigned long))

/* Device Control API: RISC-V AIA */
#define KVM_DEV_RISCV_APLIC_ALIGN		0x1000
+23 −14
Original line number Diff line number Diff line
@@ -961,18 +961,19 @@ static unsigned long num_sbi_ext_regs(struct kvm_vcpu *vcpu)
	return copy_sbi_ext_reg_indices(vcpu, NULL);
}

static inline unsigned long num_sbi_regs(struct kvm_vcpu *vcpu)
{
	return 0;
}

static int copy_sbi_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
{
	int n = num_sbi_regs(vcpu);
	struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context;
	int total = 0;

	if (scontext->ext_status[KVM_RISCV_SBI_EXT_STA] == KVM_RISCV_SBI_EXT_STATUS_ENABLED) {
		u64 size = IS_ENABLED(CONFIG_32BIT) ? KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64;
		int n = sizeof(struct kvm_riscv_sbi_sta) / sizeof(unsigned long);

		for (int i = 0; i < n; i++) {
		u64 reg = KVM_REG_RISCV | KVM_REG_SIZE_U64 |
			  KVM_REG_RISCV_SBI_STATE | i;
			u64 reg = KVM_REG_RISCV | size |
				  KVM_REG_RISCV_SBI_STATE |
				  KVM_REG_RISCV_SBI_STA | i;

			if (uindices) {
				if (put_user(reg, uindices))
@@ -981,7 +982,15 @@ static int copy_sbi_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
			}
		}

	return n;
		total += n;
	}

	return total;
}

static inline unsigned long num_sbi_regs(struct kvm_vcpu *vcpu)
{
	return copy_sbi_reg_indices(vcpu, NULL);
}

static inline unsigned long num_vector_regs(const struct kvm_vcpu *vcpu)
+5 −0
Original line number Diff line number Diff line
@@ -345,6 +345,8 @@ int kvm_riscv_vcpu_set_reg_sbi(struct kvm_vcpu *vcpu,
	reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK;

	switch (reg_subtype) {
	case KVM_REG_RISCV_SBI_STA:
		return kvm_riscv_vcpu_set_reg_sbi_sta(vcpu, reg_num, reg_val);
	default:
		return -EINVAL;
	}
@@ -370,6 +372,9 @@ int kvm_riscv_vcpu_get_reg_sbi(struct kvm_vcpu *vcpu,
	reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK;

	switch (reg_subtype) {
	case KVM_REG_RISCV_SBI_STA:
		ret = kvm_riscv_vcpu_get_reg_sbi_sta(vcpu, reg_num, &reg_val);
		break;
	default:
		return -EINVAL;
	}
+55 −0
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@
 * Copyright (c) 2023 Ventana Micro Systems Inc.
 */

#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/kvm_host.h>

#include <asm/kvm_vcpu_sbi.h>
@@ -59,3 +61,56 @@ const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_sta = {
	.handler = kvm_sbi_ext_sta_handler,
	.probe = kvm_sbi_ext_sta_probe,
};

int kvm_riscv_vcpu_get_reg_sbi_sta(struct kvm_vcpu *vcpu,
				   unsigned long reg_num,
				   unsigned long *reg_val)
{
	switch (reg_num) {
	case KVM_REG_RISCV_SBI_STA_REG(shmem_lo):
		*reg_val = (unsigned long)vcpu->arch.sta.shmem;
		break;
	case KVM_REG_RISCV_SBI_STA_REG(shmem_hi):
		if (IS_ENABLED(CONFIG_32BIT))
			*reg_val = upper_32_bits(vcpu->arch.sta.shmem);
		else
			*reg_val = 0;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

int kvm_riscv_vcpu_set_reg_sbi_sta(struct kvm_vcpu *vcpu,
				   unsigned long reg_num,
				   unsigned long reg_val)
{
	switch (reg_num) {
	case KVM_REG_RISCV_SBI_STA_REG(shmem_lo):
		if (IS_ENABLED(CONFIG_32BIT)) {
			gpa_t hi = upper_32_bits(vcpu->arch.sta.shmem);

			vcpu->arch.sta.shmem = reg_val;
			vcpu->arch.sta.shmem |= hi << 32;
		} else {
			vcpu->arch.sta.shmem = reg_val;
		}
		break;
	case KVM_REG_RISCV_SBI_STA_REG(shmem_hi):
		if (IS_ENABLED(CONFIG_32BIT)) {
			gpa_t lo = lower_32_bits(vcpu->arch.sta.shmem);

			vcpu->arch.sta.shmem = ((gpa_t)reg_val << 32);
			vcpu->arch.sta.shmem |= lo;
		} else if (reg_val != 0) {
			return -EINVAL;
		}
		break;
	default:
		return -EINVAL;
	}

	return 0;
}