Skip to content
Permalink
Browse files
RISC-V: KVM: Initial skeletal support for in-kernel AIA irqchip
To incrementally implement in-kernel AIA irqchip support, we first
add minimal skeletal support which only compiles and detects AIA
hardware support at boot-time but does not provide any in-kernel
irqchip support.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
  • Loading branch information
avpatel committed Aug 11, 2021
1 parent abeb40d commit 3239d0fad48d4b7fab5b275cef7889fac99f53ca
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 21 deletions.
@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/

#ifndef __KVM_RISCV_AIA_H
#define __KVM_RISCV_AIA_H

#include <linux/jump_label.h>
#include <linux/kvm_types.h>

struct kvm_aia {
bool in_kernel;
};

struct kvm_vcpu_aia {
};

#define irqchip_in_kernel(k) (!!((k)->arch.aia.in_kernel))

extern unsigned int kvm_riscv_aia_nr_guests;
extern unsigned int kvm_riscv_aia_max_ids;
DECLARE_STATIC_KEY_FALSE(kvm_riscv_aia_available);
#define kvm_riscv_aia_available() \
static_branch_unlikely(&kvm_riscv_aia_available)

static inline void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu)
{
}

static inline void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
{
}

static inline bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu,
u64 mask)
{
return false;
}

static inline void kvm_riscv_vcpu_aia_update_hvip(struct kvm_vcpu *vcpu)
{
}

static inline void kvm_riscv_vcpu_aia_reset(struct kvm_vcpu *vcpu)
{
}

static inline int kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu)
{
return 0;
}

static inline void kvm_riscv_vcpu_aia_deinit(struct kvm_vcpu *vcpu)
{
}

static inline void kvm_riscv_vcpu_aia_load(struct kvm_vcpu *vcpu, int cpu)
{
}

static inline void kvm_riscv_vcpu_aia_put(struct kvm_vcpu *vcpu)
{
}

static inline int kvm_riscv_vcpu_aia_get_csr(struct kvm_vcpu *vcpu,
unsigned long reg_num,
unsigned long *out_val)
{
*out_val = 0;
return 0;
}

static inline int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
unsigned long reg_num,
unsigned long val)
{
return 0;
}

#define KVM_RISCV_VCPU_AIA_CSR_FUNCS

static inline int kvm_riscv_aia_inject_msi(struct kvm *kvm,
struct kvm_msi *msi)
{
return 0;
}

static inline int kvm_riscv_aia_inject_irq(struct kvm *kvm,
unsigned int irq, bool level)
{
return 0;
}

static inline void kvm_riscv_aia_init_vm(struct kvm *kvm)
{
}

static inline void kvm_riscv_aia_destroy_vm(struct kvm *kvm)
{
}

unsigned long kvm_riscv_aia_config(void);
void kvm_riscv_aia_enable(void);
void kvm_riscv_aia_disable(void);
int kvm_riscv_aia_init(void);
void kvm_riscv_aia_exit(void);

#endif
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/kvm.h>
#include <linux/kvm_types.h>
#include <asm/kvm_aia.h>
#include <asm/kvm_vcpu_timer.h>

#ifdef CONFIG_64BIT
@@ -29,6 +30,8 @@
#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(1)
#define KVM_REQ_UPDATE_HGATP KVM_ARCH_REQ(2)

#define KVM_IRQCHIP_NUM_PINS 1023

struct kvm_vm_stat {
struct kvm_vm_stat_generic generic;
};
@@ -66,6 +69,9 @@ struct kvm_arch {

/* Guest Timer */
struct kvm_guest_timer timer;

/* AIA guest/VM context */
struct kvm_aia aia;
};

struct kvm_mmio_decode {
@@ -203,6 +209,9 @@ struct kvm_vcpu_arch {
/* SBI context */
struct kvm_sbi_context sbi_context;

/* AIA VCPU context */
struct kvm_vcpu_aia aia;

/* Cache pages needed to program page tables with spinlock held */
struct kvm_mmu_page_cache mmu_page_cache;

@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <asm/ptrace.h>

#define __KVM_HAVE_IRQ_LINE
#define __KVM_HAVE_READONLY_MEM

#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
@@ -48,6 +49,7 @@ struct kvm_sregs {
/* CONFIG registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
struct kvm_riscv_config {
unsigned long isa;
unsigned long aia;
};

/* CORE registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
@@ -97,6 +99,9 @@ struct kvm_riscv_timer {
#define KVM_REG_RISCV_CONFIG (0x01 << KVM_REG_RISCV_TYPE_SHIFT)
#define KVM_REG_RISCV_CONFIG_REG(name) \
(offsetof(struct kvm_riscv_config, name) / sizeof(unsigned long))
#define KVM_REG_RISCV_CONFIG_AIA_SUPPORTED 0x1
#define KVM_REG_RISCV_CONFIG_AIA_NR_GUESTS_MASK 0x3f
#define KVM_REG_RISCV_CONFIG_AIA_NR_GUESTS_SHIFT 1

/* Core registers are mapped as type 2 */
#define KVM_REG_RISCV_CORE (0x02 << KVM_REG_RISCV_TYPE_SHIFT)
@@ -107,6 +112,10 @@ struct kvm_riscv_timer {
#define KVM_REG_RISCV_CSR (0x03 << KVM_REG_RISCV_TYPE_SHIFT)
#define KVM_REG_RISCV_CSR_REG(name) \
(offsetof(struct kvm_riscv_csr, name) / sizeof(unsigned long))
#define KVM_REG_RISCV_CSR_GENERAL_FIRST 0
#define KVM_REG_RISCV_CSR_GENERAL_LAST KVM_REG_RISCV_CSR_REG(scounteren)
#define KVM_REG_RISCV_CSR_AIA_FIRST (KVM_REG_RISCV_CSR_GENERAL_LAST + 1)
#define KVM_REG_RISCV_CSR_AIA_LAST KVM_REG_RISCV_CSR_AIA_FIRST

/* Timer registers are mapped as type 4 */
#define KVM_REG_RISCV_TIMER (0x04 << KVM_REG_RISCV_TYPE_SHIFT)
@@ -123,6 +132,9 @@ struct kvm_riscv_timer {
#define KVM_REG_RISCV_FP_D_REG(name) \
(offsetof(struct __riscv_d_ext_state, name) / sizeof(__u64))

/* One single KVM irqchip, ie. the AIA */
#define KVM_NR_IRQCHIPS 1

#endif

#endif /* __LINUX_KVM_RISCV_H */
@@ -27,6 +27,10 @@ config KVM
select KVM_GENERIC_DIRTYLOG_READ_PROTECT
select HAVE_KVM_VCPU_ASYNC_IOCTL
select HAVE_KVM_EVENTFD
select HAVE_KVM_IRQFD
select HAVE_KVM_MSI
select HAVE_KVM_IRQCHIP
select HAVE_KVM_IRQ_ROUTING
select SRCU
help
Support hosting virtualized guest machines.
@@ -13,6 +13,7 @@ kvm-y += $(KVM)/kvm_main.o
kvm-y += $(KVM)/coalesced_mmio.o
kvm-y += $(KVM)/binary_stats.o
kvm-y += $(KVM)/eventfd.o
kvm-y += $(KVM)/irqchip.o
kvm-y += main.o
kvm-y += vm.o
kvm-y += vmid.o
@@ -23,3 +24,4 @@ kvm-y += vcpu_exit.o
kvm-y += vcpu_switch.o
kvm-y += vcpu_sbi.o
kvm-y += vcpu_timer.o
kvm-y += aia/aia.o
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/

#include <linux/bitops.h>
#include <linux/irqchip/riscv-imsic.h>
#include <linux/kvm_host.h>
#include <asm/hwcap.h>

unsigned int kvm_riscv_aia_nr_guests;
unsigned int kvm_riscv_aia_max_ids;
DEFINE_STATIC_KEY_FALSE(kvm_riscv_aia_available);

static inline void aia_set_hvictl(bool ext_irq_pending)
{
unsigned long hvictl;

/*
* HVICTL.IID == 9 and HVICTL.IPRIO == 0 represents
* no interupt in HVICTL.
*/

hvictl = (IRQ_S_EXT << HVICTL_IID_SHIFT) & HVICTL_IID;
hvictl |= (ext_irq_pending) ? 1 : 0;
csr_write(CSR_HVICTL, hvictl);
}

unsigned long kvm_riscv_aia_config(void)
{
unsigned long ret = 0;

if (!kvm_riscv_aia_available())
return 0;

ret = KVM_REG_RISCV_CONFIG_AIA_SUPPORTED;
ret |= (kvm_riscv_aia_nr_guests &
KVM_REG_RISCV_CONFIG_AIA_NR_GUESTS_MASK) <<
KVM_REG_RISCV_CONFIG_AIA_NR_GUESTS_SHIFT;

return ret;
}

void kvm_riscv_aia_enable(void)
{
if (!kvm_riscv_aia_available())
return;

aia_set_hvictl(false);
csr_write(CSR_HVIPRIO1, 0x0);
csr_write(CSR_HVIPRIO2, 0x0);
#ifndef CONFIG_64BIT
csr_write(CSR_HVIPH, 0x0);
csr_write(CSR_HIDELEGH, 0x0);
csr_write(CSR_HVIPRIO1H, 0x0);
csr_write(CSR_HVIPRIO2H, 0x0);
#endif
}

void kvm_riscv_aia_disable(void)
{
if (!kvm_riscv_aia_available())
return;

aia_set_hvictl(false);
}

int kvm_riscv_aia_init(void)
{
unsigned int hgeie_bits;

if (!riscv_aia_available)
return -ENODEV;

/* Figure-out number of bits in HGEIE */
csr_write(CSR_HGEIE, -1UL);
hgeie_bits = fls_long(csr_read(CSR_HGEIE));
csr_write(CSR_HGEIE, 0);

/* Find number of guest files */
kvm_riscv_aia_nr_guests = min(imsic_num_cpu_pages(), hgeie_bits);
if (kvm_riscv_aia_nr_guests)
kvm_riscv_aia_nr_guests--;

/* Find number of guest MSI IDs */
kvm_riscv_aia_max_ids = IMSIC_MAX_ID;
if (kvm_riscv_aia_nr_guests)
kvm_riscv_aia_max_ids = imsic_num_ids() + 1;

/* Enable KVM AIA support */
static_branch_enable(&kvm_riscv_aia_available);

return 0;
}

void kvm_riscv_aia_exit(void)
{
}
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* irq.h: in kernel interrupt controller related definitions
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
*
* This header is included by irqchip.c.
*/

#ifndef __IRQ_H
#define __IRQ_H

#include <asm/kvm_aia.h>

#endif
@@ -53,17 +53,22 @@ int kvm_arch_hardware_enable(void)

csr_write(CSR_HVIP, 0);

kvm_riscv_aia_enable();

return 0;
}

void kvm_arch_hardware_disable(void)
{
kvm_riscv_aia_disable();

csr_write(CSR_HEDELEG, 0);
csr_write(CSR_HIDELEG, 0);
}

int kvm_arch_init(void *opaque)
{
int rc;
const char *str;

if (!riscv_isa_extension_available(NULL, h)) {
@@ -85,6 +90,10 @@ int kvm_arch_init(void *opaque)

kvm_riscv_stage2_vmid_detect();

rc = kvm_riscv_aia_init();
if (rc && rc != -ENODEV)
return rc;

kvm_info("hypervisor extension available\n");

switch (kvm_riscv_stage2_mode()) {
@@ -104,11 +113,16 @@ int kvm_arch_init(void *opaque)

kvm_info("VMID %ld bits available\n", kvm_riscv_stage2_vmid_bits());

if (kvm_riscv_aia_available())
kvm_info("AIA irqchip available with %d guest imsics\n",
kvm_riscv_aia_nr_guests);

return 0;
}

void kvm_arch_exit(void)
{
kvm_riscv_aia_exit();
}

static int riscv_kvm_init(void)

0 comments on commit 3239d0f

Please sign in to comment.