Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
KVM: arm64: Get SDEI event and state by firmware registers
This introduces firmware registers to get SDEI information.

  * KVM_REG_ARM_SDEI_EVENT
    Events which are visible on one specific vcpu.

  * KVM_REG_ARM_SDEI_STATE
    PE mask state on the specific vcpu.

  * KVM_REG_ARM_SDEI_CRITICAL_CONTEXT
    The interrupted or preempted context by event with critical priority.

  * KVM_REG_ARM_SDEI_NORMAL_CONTEXT
    The interrupted or preempted context by event with normal priority.

Signed-off-by: Gavin Shan <gshan@redhat.com>
  • Loading branch information
Gavin Shan committed Apr 5, 2022
1 parent 99beb6d commit c2e5de5
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 2 deletions.
10 changes: 9 additions & 1 deletion arch/arm64/include/asm/kvm_sdei.h
Expand Up @@ -95,6 +95,8 @@ struct kvm_sdei_vcpu {
#define KVM_SDEI_EVENT_NUM_TYPE_PHYS 0
#define KVM_SDEI_EVENT_NUM_TYPE_VIRT 1

#define KVM_SDEI_INVALID_EVENT 0xFFFFFFFF

static inline bool kvm_sdei_is_virtual(unsigned int num)
{
unsigned int type;
Expand All @@ -107,6 +109,11 @@ static inline bool kvm_sdei_is_virtual(unsigned int num)
return false;
}

static inline bool kvm_sdei_is_invalid(unsigned int num)
{
return num == KVM_SDEI_INVALID_EVENT;
}

static inline bool kvm_sdei_is_sw_signaled(unsigned int num)
{
return num == SDEI_SW_SIGNALED_EVENT;
Expand All @@ -115,7 +122,7 @@ static inline bool kvm_sdei_is_sw_signaled(unsigned int num)
static inline bool kvm_sdei_is_supported(unsigned int num)
{
return kvm_sdei_is_sw_signaled(num) ||
kvm_sdei_is_virtual(num);
(kvm_sdei_is_virtual(num) && !kvm_sdei_is_invalid(num));
}

static inline bool kvm_sdei_is_critical(unsigned char priority)
Expand Down Expand Up @@ -154,6 +161,7 @@ int kvm_sdei_inject_event(struct kvm_vcpu *vcpu, unsigned int num,
bool immediate);
int kvm_sdei_cancel_event(struct kvm_vcpu *vcpu, unsigned int num);
void kvm_sdei_deliver_event(struct kvm_vcpu *vcpu);
int kvm_sdei_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu);
void kvm_sdei_destroy_vcpu(struct kvm_vcpu *vcpu);

Expand Down
10 changes: 9 additions & 1 deletion arch/arm64/include/uapi/asm/kvm.h
Expand Up @@ -258,8 +258,10 @@ struct kvm_arm_copy_mte_tags {

/* KVM-as-firmware specific pseudo-registers */
#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT)
#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
#define KVM_REG_ARM_FW_REG_SIZED(r, sz) (KVM_REG_ARM64 | KVM_REG_SIZE_##sz | \
KVM_REG_ARM_FW | ((r) & 0xffff))
#define KVM_REG_ARM_FW_REG(r) KVM_REG_ARM_FW_REG_SIZED(r, U64)

#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1)
#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0
Expand All @@ -286,6 +288,12 @@ struct kvm_arm_copy_mte_tags {
#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL 1
#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED 2

/* SDEI firmware registers */
#define KVM_REG_ARM_SDEI_EVENT KVM_REG_ARM_FW_REG_SIZED(4, U2048)
#define KVM_REG_ARM_SDEI_STATE KVM_REG_ARM_FW_REG(5)
#define KVM_REG_ARM_SDEI_CRITICAL_CONTEXT KVM_REG_ARM_FW_REG_SIZED(6, U2048)
#define KVM_REG_ARM_SDEI_NORMAL_CONTEXT KVM_REG_ARM_FW_REG_SIZED(7, U2048)

/* SVE registers */
#define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)

Expand Down
6 changes: 6 additions & 0 deletions arch/arm64/kvm/guest.c
Expand Up @@ -24,6 +24,7 @@
#include <asm/fpsimd.h>
#include <asm/kvm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_sdei.h>
#include <asm/sigcontext.h>

#include "trace.h"
Expand Down Expand Up @@ -717,6 +718,11 @@ static int get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
return kvm_psci_get_fw_reg(vcpu, reg);
case KVM_REG_ARM_SDEI_EVENT:
case KVM_REG_ARM_SDEI_STATE:
case KVM_REG_ARM_SDEI_CRITICAL_CONTEXT:
case KVM_REG_ARM_SDEI_NORMAL_CONTEXT:
return kvm_sdei_get_fw_reg(vcpu, reg);
default:
return -ENOENT;
}
Expand Down
114 changes: 114 additions & 0 deletions arch/arm64/kvm/sdei.c
Expand Up @@ -869,6 +869,120 @@ void kvm_sdei_deliver_event(struct kvm_vcpu *vcpu)
spin_unlock(&vsdei->lock);
}

static int get_event(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
{
struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
struct kvm_sdei_exposed_event *exposed_event;
struct kvm_sdei_event *event;
u64 __user *uaddr = (u64 __user *)reg->addr;
u64 __user *uend = uaddr + KVM_REG_SIZE(reg->id) / sizeof(u64);
u64 val;
int i;

kvm_sdei_for_each_event(vsdei, event, i) {
if (uend - uaddr < 6)
return -ENOSPC;

exposed_event = event->exposed_event;
val = (u64)(event->route_mode) << 56 |
(u64)(exposed_event->priority) << 48 |
(u64)(exposed_event->signaled) << 40 |
(u64)(exposed_event->type) << 32 |
exposed_event->num;

if (put_user(val, uaddr++) ||
put_user(event->route_affinity, uaddr++) ||
put_user(event->ep_address, uaddr++) ||
put_user(event->ep_arg, uaddr++) ||
put_user(event->state, uaddr++) ||
put_user(event->event_count, uaddr++))
return -EFAULT;
}

/*
* Fill the left space with the invalid event number so that the
* context can be skipped by readers.
*/
val = KVM_SDEI_INVALID_EVENT;
while (uaddr < uend) {
if (put_user(val, uaddr))
return -EFAULT;

uaddr += 6;
}

return 0;
}

static int get_context(struct kvm_vcpu *vcpu,
const struct kvm_one_reg *reg,
bool is_critical)
{
struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
struct kvm_sdei_exposed_event *exposed_event;
struct kvm_sdei_vcpu_context *context;
u64 __user *uaddr = (u64 __user *)reg->addr;
u64 __user *uend = uaddr + KVM_REG_SIZE(reg->id) / sizeof(u64);
u64 val;
int i;

if (uend - uaddr < ARRAY_SIZE(context->regs) + 3)
return -ENOSPC;

context = is_critical ? &vsdei->context[SDEI_EVENT_PRIORITY_CRITICAL] :
&vsdei->context[SDEI_EVENT_PRIORITY_NORMAL];
exposed_event = context->event ? context->event->exposed_event : NULL;
val = exposed_event ? exposed_event->num : KVM_SDEI_INVALID_EVENT;

if (put_user(val, uaddr++) ||
put_user(context->pc, uaddr++) ||
put_user(context->pstate, uaddr++))
return -EFAULT;

for (i = 0; i < ARRAY_SIZE(context->regs); i++) {
if (put_user(context->regs[i], uaddr++))
return -EFAULT;
}

return 0;
}

int kvm_sdei_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
{
struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
void __user *uaddr = (void __user *)reg->addr;
u64 val;
int ret = 0;

if (!vsdei)
return -EPERM;

spin_lock(&vsdei->lock);

switch (reg->id) {
case KVM_REG_ARM_SDEI_EVENT:
ret = get_event(vcpu, reg);
break;
case KVM_REG_ARM_SDEI_STATE:
val = vsdei->masked ? 1 : 0;
if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
ret = -EFAULT;
break;
case KVM_REG_ARM_SDEI_CRITICAL_CONTEXT:
ret = get_context(vcpu, reg, true);
break;
case KVM_REG_ARM_SDEI_NORMAL_CONTEXT:
ret = get_context(vcpu, reg, false);
break;
default:
ret = -ENOENT;
}

spin_unlock(&vsdei->lock);

return ret;
}

void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu)
{
struct kvm_sdei_vcpu *vsdei;
Expand Down

0 comments on commit c2e5de5

Please sign in to comment.