RISC-V: KVM: Add SBI HSM extension in KVM
SBI HSM extension allows OS to start/stop harts any time. It also allows ordered booting of harts instead of random booting. Implement SBI HSM exntesion and designate the vcpu 0 as the boot vcpu id. All other non-zero non-booting vcpus should be brought up by the OS implementing HSM extension. If the guest OS doesn't implement HSM extension, only single vcpu will be available to OS. Signed-off-by: Atish Patra <atish.patra@wdc.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Signed-off-by: Anup Patel <anup.patel@wdc.com>
This commit is contained in:
parent
5f862df558
commit
3e1d86569c
5 changed files with 134 additions and 0 deletions
|
@ -90,6 +90,7 @@ enum sbi_hsm_hart_status {
|
||||||
#define SBI_ERR_INVALID_PARAM -3
|
#define SBI_ERR_INVALID_PARAM -3
|
||||||
#define SBI_ERR_DENIED -4
|
#define SBI_ERR_DENIED -4
|
||||||
#define SBI_ERR_INVALID_ADDRESS -5
|
#define SBI_ERR_INVALID_ADDRESS -5
|
||||||
|
#define SBI_ERR_ALREADY_AVAILABLE -6
|
||||||
|
|
||||||
extern unsigned long sbi_spec_version;
|
extern unsigned long sbi_spec_version;
|
||||||
struct sbiret {
|
struct sbiret {
|
||||||
|
|
|
@ -22,4 +22,5 @@ kvm-y += vcpu_sbi.o
|
||||||
kvm-$(CONFIG_RISCV_SBI_V01) += vcpu_sbi_v01.o
|
kvm-$(CONFIG_RISCV_SBI_V01) += vcpu_sbi_v01.o
|
||||||
kvm-y += vcpu_sbi_base.o
|
kvm-y += vcpu_sbi_base.o
|
||||||
kvm-y += vcpu_sbi_replace.o
|
kvm-y += vcpu_sbi_replace.o
|
||||||
|
kvm-y += vcpu_sbi_hsm.o
|
||||||
kvm-y += vcpu_timer.o
|
kvm-y += vcpu_timer.o
|
||||||
|
|
|
@ -53,6 +53,17 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||||
struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr;
|
struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr;
|
||||||
struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
|
struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
|
||||||
struct kvm_cpu_context *reset_cntx = &vcpu->arch.guest_reset_context;
|
struct kvm_cpu_context *reset_cntx = &vcpu->arch.guest_reset_context;
|
||||||
|
bool loaded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preemption should be disabled here because it races with
|
||||||
|
* kvm_sched_out/kvm_sched_in(called from preempt notifiers) which
|
||||||
|
* also calls vcpu_load/put.
|
||||||
|
*/
|
||||||
|
get_cpu();
|
||||||
|
loaded = (vcpu->cpu != -1);
|
||||||
|
if (loaded)
|
||||||
|
kvm_arch_vcpu_put(vcpu);
|
||||||
|
|
||||||
memcpy(csr, reset_csr, sizeof(*csr));
|
memcpy(csr, reset_csr, sizeof(*csr));
|
||||||
|
|
||||||
|
@ -64,6 +75,11 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
WRITE_ONCE(vcpu->arch.irqs_pending, 0);
|
WRITE_ONCE(vcpu->arch.irqs_pending, 0);
|
||||||
WRITE_ONCE(vcpu->arch.irqs_pending_mask, 0);
|
WRITE_ONCE(vcpu->arch.irqs_pending_mask, 0);
|
||||||
|
|
||||||
|
/* Reset the guest CSRs for hotplug usecase */
|
||||||
|
if (loaded)
|
||||||
|
kvm_arch_vcpu_load(vcpu, smp_processor_id());
|
||||||
|
put_cpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
|
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
|
||||||
|
@ -101,6 +117,13 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
|
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* vcpu with id 0 is the designated boot cpu.
|
||||||
|
* Keep all vcpus with non-zero id in power-off state so that
|
||||||
|
* they can be brought up using SBI HSM extension.
|
||||||
|
*/
|
||||||
|
if (vcpu->vcpu_idx != 0)
|
||||||
|
kvm_riscv_vcpu_power_off(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
||||||
|
|
|
@ -25,6 +25,8 @@ static int kvm_linux_err_map_sbi(int err)
|
||||||
return SBI_ERR_INVALID_ADDRESS;
|
return SBI_ERR_INVALID_ADDRESS;
|
||||||
case -EOPNOTSUPP:
|
case -EOPNOTSUPP:
|
||||||
return SBI_ERR_NOT_SUPPORTED;
|
return SBI_ERR_NOT_SUPPORTED;
|
||||||
|
case -EALREADY:
|
||||||
|
return SBI_ERR_ALREADY_AVAILABLE;
|
||||||
default:
|
default:
|
||||||
return SBI_ERR_FAILURE;
|
return SBI_ERR_FAILURE;
|
||||||
};
|
};
|
||||||
|
@ -43,6 +45,7 @@ extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_base;
|
||||||
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time;
|
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time;
|
||||||
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi;
|
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi;
|
||||||
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence;
|
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence;
|
||||||
|
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm;
|
||||||
|
|
||||||
static const struct kvm_vcpu_sbi_extension *sbi_ext[] = {
|
static const struct kvm_vcpu_sbi_extension *sbi_ext[] = {
|
||||||
&vcpu_sbi_ext_v01,
|
&vcpu_sbi_ext_v01,
|
||||||
|
@ -50,6 +53,7 @@ static const struct kvm_vcpu_sbi_extension *sbi_ext[] = {
|
||||||
&vcpu_sbi_ext_time,
|
&vcpu_sbi_ext_time,
|
||||||
&vcpu_sbi_ext_ipi,
|
&vcpu_sbi_ext_ipi,
|
||||||
&vcpu_sbi_ext_rfence,
|
&vcpu_sbi_ext_rfence,
|
||||||
|
&vcpu_sbi_ext_hsm,
|
||||||
};
|
};
|
||||||
|
|
||||||
void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
|
|
105
arch/riscv/kvm/vcpu_sbi_hsm.c
Normal file
105
arch/riscv/kvm/vcpu_sbi_hsm.c
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Atish Patra <atish.patra@wdc.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/kvm_host.h>
|
||||||
|
#include <asm/csr.h>
|
||||||
|
#include <asm/sbi.h>
|
||||||
|
#include <asm/kvm_vcpu_sbi.h>
|
||||||
|
|
||||||
|
static int kvm_sbi_hsm_vcpu_start(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm_cpu_context *reset_cntx;
|
||||||
|
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
|
||||||
|
struct kvm_vcpu *target_vcpu;
|
||||||
|
unsigned long target_vcpuid = cp->a0;
|
||||||
|
|
||||||
|
target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid);
|
||||||
|
if (!target_vcpu)
|
||||||
|
return -EINVAL;
|
||||||
|
if (!target_vcpu->arch.power_off)
|
||||||
|
return -EALREADY;
|
||||||
|
|
||||||
|
reset_cntx = &target_vcpu->arch.guest_reset_context;
|
||||||
|
/* start address */
|
||||||
|
reset_cntx->sepc = cp->a1;
|
||||||
|
/* target vcpu id to start */
|
||||||
|
reset_cntx->a0 = target_vcpuid;
|
||||||
|
/* private data passed from kernel */
|
||||||
|
reset_cntx->a1 = cp->a2;
|
||||||
|
kvm_make_request(KVM_REQ_VCPU_RESET, target_vcpu);
|
||||||
|
|
||||||
|
kvm_riscv_vcpu_power_on(target_vcpu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_sbi_hsm_vcpu_stop(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
if (vcpu->arch.power_off)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
kvm_riscv_vcpu_power_off(vcpu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_sbi_hsm_vcpu_get_status(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
|
||||||
|
unsigned long target_vcpuid = cp->a0;
|
||||||
|
struct kvm_vcpu *target_vcpu;
|
||||||
|
|
||||||
|
target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid);
|
||||||
|
if (!target_vcpu)
|
||||||
|
return -EINVAL;
|
||||||
|
if (!target_vcpu->arch.power_off)
|
||||||
|
return SBI_HSM_HART_STATUS_STARTED;
|
||||||
|
else
|
||||||
|
return SBI_HSM_HART_STATUS_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_sbi_ext_hsm_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
||||||
|
unsigned long *out_val,
|
||||||
|
struct kvm_cpu_trap *utrap,
|
||||||
|
bool *exit)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
|
||||||
|
struct kvm *kvm = vcpu->kvm;
|
||||||
|
unsigned long funcid = cp->a6;
|
||||||
|
|
||||||
|
switch (funcid) {
|
||||||
|
case SBI_EXT_HSM_HART_START:
|
||||||
|
mutex_lock(&kvm->lock);
|
||||||
|
ret = kvm_sbi_hsm_vcpu_start(vcpu);
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
break;
|
||||||
|
case SBI_EXT_HSM_HART_STOP:
|
||||||
|
ret = kvm_sbi_hsm_vcpu_stop(vcpu);
|
||||||
|
break;
|
||||||
|
case SBI_EXT_HSM_HART_STATUS:
|
||||||
|
ret = kvm_sbi_hsm_vcpu_get_status(vcpu);
|
||||||
|
if (ret >= 0) {
|
||||||
|
*out_val = ret;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm = {
|
||||||
|
.extid_start = SBI_EXT_HSM,
|
||||||
|
.extid_end = SBI_EXT_HSM,
|
||||||
|
.handler = kvm_sbi_ext_hsm_handler,
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue