Skip to content

Commit

Permalink
feat(riscv): add vimsic support
Browse files Browse the repository at this point in the history
Co-authored-by: Jose Martins <[email protected]>

Signed-off-by: Jose Martins <[email protected]>
  • Loading branch information
D3boker1 committed Sep 26, 2024
1 parent c282e99 commit bd10f03
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 20 deletions.
4 changes: 3 additions & 1 deletion src/arch/riscv/inc/arch/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <bao.h>
#include <irqc.h>
#include <arch/sbi.h>
#include <arch/interrupts.h>

#define REG_RA (1)
#define REG_SP (2)
Expand Down Expand Up @@ -52,6 +51,9 @@ struct arch_vm_platform {
struct {
paddr_t base;
} aplic;
struct {
paddr_t base;
} imsic;
} aia;
} irqc;
};
Expand Down
1 change: 1 addition & 0 deletions src/arch/riscv/irqc/aia/inc/irqc.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <arch/sbi.h>
#elif (IRQC == AIA)
#include <imsic.h>
#include <vimsic.h>
#endif

#define IRQC_TIMR_INT_ID (APLIC_MAX_INTERRUPTS + 1)
Expand Down
11 changes: 0 additions & 11 deletions src/arch/riscv/irqc/aia/inc/vaplic.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,6 @@ void vaplic_inject(struct vcpu* vcpu, irqid_t id);
*/
void vaplic_set_hw(struct vm* vm, irqid_t id);

/**
* @brief Wrapper for the virtual irqc initialization function
*
* @param vm Virtual Machine
* @param vm_irqc_dscrp virtual irqc platform configuration
*/
static inline void virqc_init(struct vm* vm, const union vm_irqc_dscrp* vm_irqc_dscrp)
{
vaplic_init(vm, vm_irqc_dscrp);
}

/**
* @brief Injects a given interrupt into a virtual cpu
*
Expand Down
26 changes: 26 additions & 0 deletions src/arch/riscv/irqc/aia/inc/vimsic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Bao Project and Contributors. All rights reserved.
*/

#ifndef VIMSIC_H
#define VIMSIC_H

#include <bao.h>

/**
* @brief Maps the guest IMSIC addresses into the physical IMSIC adresses
*
* Guest's VCPUs assume no indexation order, e.g., VCPU 0 can map to CPU 2. This mapping
* occurs at run-time (during bao initialization), therefore, also the IMSIC mapping needs to
* be done at initialization time.
* The vimsic_init function needs to be executed by every VM's virtual CPU. Then, it will
* calculate the physical IMSIC address based on the physical CPU into which the VCPU is
* mapped. Finally, it adds a new entry in the MMU.
*
* @param vm Virtual Machine
* @param vm_irqc_dscrp Virtual Machine Description
*/
void vimsic_init(struct vm* vm, const union vm_irqc_dscrp* vm_irqc_dscrp);

#endif // VIMSIC_H
102 changes: 95 additions & 7 deletions src/arch/riscv/irqc/aia/vaplic.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
#include <mem.h>
#include <interrupts.h>
#include <arch/csrs.h>
#if (IRQC == AIA)
#include <imsic.h>
#endif

#define APLIC_MIN_PRIO (0xFF)
#define UPDATE_ALL_HARTS (~0U)

#define SET_INTP_REG(reg, intp_id) (reg[intp_id / 32] = bit32_set(reg[intp_id / 32], intp_id % 32))
Expand Down Expand Up @@ -44,9 +46,13 @@ static inline cpuid_t vaplic_vcpuid_to_pcpuid(struct vcpu* vcpu, vcpuid_t vhart)

static uint32_t vaplic_get_domaincfg(struct vcpu* vcpu);
static uint32_t vaplic_get_target(struct vcpu* vcpu, irqid_t intp_id);
#if (IRQC == APLIC)
#define APLIC_MIN_PRIO (0xFF)

static uint32_t vaplic_get_idelivery(struct vcpu* vcpu, idcid_t idc_id);
static uint32_t vaplic_get_iforce(struct vcpu* vcpu, idcid_t idc_id);
static uint32_t vaplic_get_ithreshold(struct vcpu* vcpu, idcid_t idc_id);
#endif

void vaplic_set_hw(struct vm* vm, irqid_t intp_id)
{
Expand All @@ -55,6 +61,21 @@ void vaplic_set_hw(struct vm* vm, irqid_t intp_id)
}
}

#if (IRQC == AIA)
/**
* @brief Returns the target guest index of a given interrupt
*
* @param vcpu virtual cpu
* @param intp_id interrupt ID
* @return uint8_t guest hart index of the given interrupt
*/
static inline uint8_t vaplic_get_target_guest(struct vcpu* vcpu, irqid_t intp_id)
{
return (vaplic_get_target(vcpu, intp_id) >> APLIC_TARGET_GUEST_IDX_SHIFT) &
APLIC_TARGET_GUEST_INDEX_MASK;
}
#endif

/**
* @brief Returns the target hart index of a given interrupt
*
Expand Down Expand Up @@ -163,6 +184,11 @@ static bool vaplic_set_pend(struct vcpu* vcpu, irqid_t intp_id)
return ret;
}

enum { UPDATE_HART_LINE };
static void vaplic_ipi_handler(uint32_t event, uint64_t data);
CPU_MSG_HANDLER(vaplic_ipi_handler, VPLIC_IPI_ID)

#if (IRQC == APLIC)
/**
* @brief Updates the topi register with with the highest pend & en interrupt id
*
Expand Down Expand Up @@ -213,10 +239,6 @@ static bool vaplic_update_topi(struct vcpu* vcpu)
return ret;
}

enum { UPDATE_HART_LINE };
static void vaplic_ipi_handler(uint32_t event, uint64_t data);
CPU_MSG_HANDLER(vaplic_ipi_handler, VPLIC_IPI_ID)

/**
* @brief Updates the interrupt line for a single hart
*
Expand All @@ -243,6 +265,30 @@ static void vaplic_update_hart_line(struct vcpu* vcpu, vcpuid_t vhart_index)
}
}

#elif (IRQC == AIA)
static void vaplic_update_hart_imsic(struct vcpu* vcpu, vcpuid_t vhart_index)
{
struct vaplic* vaplic = &vcpu->vm->arch.vaplic;
cpuid_t pcpu_id = vaplic_vcpuid_to_pcpuid(vcpu, vhart_index);
bool domain_enbl = !!(vaplic_get_domaincfg(vcpu) & APLIC_DOMAINCFG_IE);
size_t target_guest = 0;

if (pcpu_id == cpu()->id) {
for (irqid_t i = 1; i < APLIC_MAX_INTERRUPTS; i++) {
if ((vaplic_get_hart_index(vcpu, i) == vcpu->id) && vaplic_get_pend(vcpu, i) &&
vaplic_get_enbl(vcpu, i) && domain_enbl) {
target_guest = vaplic_get_target_guest(vcpu, i);
imsic_inject_pend(target_guest, i);
CLR_INTP_REG(vaplic->ip, i);
}
}
} else {
struct cpu_msg msg = { (uint32_t)VPLIC_IPI_ID, UPDATE_HART_LINE, vhart_index };
cpu_send_msg(pcpu_id, &msg);
}
}
#endif

/**
* @brief Triggers the hart/harts interrupt line update.
*
Expand All @@ -257,10 +303,18 @@ static void vaplic_update_hart(struct vcpu* vcpu, size_t vhart_index)

if (vhart_index == UPDATE_ALL_HARTS) {
for (size_t i = 0; i < vaplic->idc_num; i++) {
#if (IRQC == APLIC)
vaplic_update_hart_line(vcpu, (vcpuid_t)i);
#elif (IRQC == AIA)
vaplic_update_hart_imsic(vcpu, (vcpuid_t)i);
#endif
}
} else if ((uint16_t)vhart_index < vaplic->idc_num) {
#if (IRQC == APLIC)
vaplic_update_hart_line(vcpu, (vcpuid_t)vhart_index);
#elif (IRQC == AIA)
vaplic_update_hart_imsic(vcpu, (vcpuid_t)vhart_index);
#endif
}
}

Expand Down Expand Up @@ -295,7 +349,11 @@ static void vaplic_set_domaincfg(struct vcpu* vcpu, uint32_t new_val)
/** Update only the virtual domaincfg */
/** Only Interrupt Enable is configurable */
new_val &= APLIC_DOMAINCFG_IE;
#if (IRQC == APLIC)
new_val &= ~APLIC_DOMAINCFG_DM;
#elif (IRQC == AIA)
new_val |= APLIC_DOMAINCFG_DM;
#endif
vaplic->domaincfg = new_val | APLIC_DOMAINCFG_RO80;
vaplic_update_hart(vcpu, UPDATE_ALL_HARTS);
spin_unlock(&vaplic->lock);
Expand Down Expand Up @@ -645,7 +703,12 @@ static void vaplic_set_target(struct vcpu* vcpu, irqid_t intp_id, uint32_t new_v
{
struct vaplic* vaplic = &vcpu->vm->arch.vaplic;
vcpuid_t hart_index = (new_val >> APLIC_TARGET_HART_IDX_SHIFT) & APLIC_TARGET_HART_IDX_MASK;
#if (IRQC == APLIC)
uint8_t priority = (uint8_t)(new_val & APLIC_IPRIO_MASK);
#elif (IRQC == AIA)
uint8_t guest_index = (new_val >> APLIC_TARGET_GUEST_IDX_SHIFT) & APLIC_TARGET_GUEST_INDEX_MASK;
irqid_t eiid = new_val & APLIC_TARGET_EEID_MASK;
#endif
cpuid_t pcpu_id = vm_translate_to_pcpuid(vcpu->vm, hart_index);
vcpuid_t prev_hart_index = 0;

Expand All @@ -657,20 +720,37 @@ static void vaplic_set_target(struct vcpu* vcpu, irqid_t intp_id, uint32_t new_v
pcpu_id = vm_translate_to_pcpuid(vcpu->vm, hart_index);
}

#if (IRQC == APLIC)
new_val &= APLIC_TARGET_DIRECT_MASK;
if (priority == 0) {
new_val |= APLIC_TARGET_MAX_PRIO;
priority = APLIC_TARGET_MAX_PRIO;
}
#elif (IRQC == AIA)
new_val &= APLIC_TARGET_MSI_MASK;
#endif

if (vaplic_get_active(vcpu, intp_id) && vaplic_get_target(vcpu, intp_id) != new_val) {
prev_hart_index = vaplic_get_hart_index(vcpu, intp_id);
if (vaplic_get_hw(vcpu, intp_id)) {
aplic_set_target_hart(intp_id, pcpu_id);
#if (IRQC == APLIC)
aplic_set_target_prio(intp_id, priority);
priority = aplic_get_target_prio(intp_id);
#elif (IRQC == AIA)
aplic_set_target_guest(intp_id, 1);
aplic_set_target_eiid(intp_id, eiid);
eiid = aplic_get_target_eiid(intp_id);
#endif
}
vaplic->target[intp_id] =
(uint32_t)((hart_index << APLIC_TARGET_HART_IDX_SHIFT) | priority);

#if (IRQC == APLIC)
vaplic->target[intp_id] = (uint32_t)(hart_index << APLIC_TARGET_HART_IDX_SHIFT) | priority;
#elif (IRQC == AIA)
vaplic->target[intp_id] = (uint32_t)(hart_index << APLIC_TARGET_HART_IDX_SHIFT) |
(uint32_t)(guest_index << APLIC_TARGET_GUEST_IDX_SHIFT) | eiid;
#endif

if (prev_hart_index != hart_index) {
vaplic_update_hart(vcpu, prev_hart_index);
}
Expand All @@ -697,6 +777,7 @@ static uint32_t vaplic_get_target(struct vcpu* vcpu, irqid_t intp_id)
return ret;
}

#if (IRQC == APLIC)
/**
* @brief Set idelivery register for a given idc.
*
Expand Down Expand Up @@ -854,6 +935,7 @@ static uint32_t vaplic_get_claimi(struct vcpu* vcpu, idcid_t idc_id)
spin_unlock(&vaplic->lock);
return ret;
}
#endif

/**
* @brief domaincfg register access emulation function
Expand Down Expand Up @@ -1028,6 +1110,7 @@ static void vaplic_emul_target_access(struct emul_access* acc)
}
}

#if (IRQC == APLIC)
/**
* @brief idelivery register access emulation function
*
Expand Down Expand Up @@ -1103,6 +1186,7 @@ static void vaplic_emul_claimi_access(struct emul_access* acc, idcid_t idc_id)
vcpu_writereg(cpu()->vcpu, acc->reg, vaplic_get_claimi(cpu()->vcpu, idc_id));
}
}
#endif

/**
* @brief Injects a given interrupt into a given vcpu
Expand Down Expand Up @@ -1239,6 +1323,7 @@ static bool vaplic_domain_emul_handler(struct emul_access* acc)
return true;
}

#if (IRQC == APLIC)
/**
* @brief Function to handle writes (or reads) to (from) IDC structure.
*
Expand Down Expand Up @@ -1281,6 +1366,7 @@ static bool vaplic_idc_emul_handler(struct emul_access* acc)
}
return true;
}
#endif

void vaplic_init(struct vm* vm, const union vm_irqc_dscrp* vm_irqc_dscrp)
{
Expand All @@ -1295,11 +1381,13 @@ void vaplic_init(struct vm* vm, const union vm_irqc_dscrp* vm_irqc_dscrp)

vm_emul_add_mem(vm, &vm->arch.vaplic.aplic_domain_emul);

#if (IRQC == APLIC)
vm->arch.vaplic.aplic_idc_emul =
(struct emul_mem){ .va_base = vm_irqc_dscrp->aia.aplic.base + APLIC_IDC_OFF,
.size = sizeof(struct aplic_idc_hw) * vm->arch.vaplic.idc_num,
.handler = vaplic_idc_emul_handler };

vm_emul_add_mem(vm, &vm->arch.vaplic.aplic_idc_emul);
#endif
}
}
30 changes: 30 additions & 0 deletions src/arch/riscv/irqc/aia/vimsic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Bao Project and Contributors. All rights reserved.
*/

#include <vm.h>
#include <arch/platform.h>
#include <mem.h>
#include <imsic.h>

/** We only support 1 guest per hart at the moment */
#define VS_FILE_IDX 1

void vimsic_init(struct vm* vm, const union vm_irqc_dscrp* vm_irqc_dscrp)
{
struct vcpu* vcpu = cpu()->vcpu;
cpuid_t pcpu_id = vcpu->phys_id;
vcpuid_t vcpu_id = vcpu->id;
paddr_t imsic_paddr;
vaddr_t imsic_vaddr;

imsic_vaddr = vm_irqc_dscrp->aia.imsic.base + (PAGE_SIZE * vcpu_id);

imsic_paddr = platform.arch.irqc.aia.imsic.base +
(PAGE_SIZE * ((IMSIC_NUM_FILES * pcpu_id) + VS_FILE_IDX));

if (imsic_vaddr != INVALID_VA) {
mem_alloc_map_dev(&vm->as, SEC_VM_ANY, imsic_vaddr, imsic_paddr, 1);
}
}
2 changes: 1 addition & 1 deletion src/arch/riscv/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void vcpu_arch_reset(struct vcpu* vcpu, vaddr_t entry)

csrs_sscratch_write((uintptr_t)&vcpu->regs);

vcpu->regs.hstatus = HSTATUS_SPV | HSTATUS_VSXL_64;
vcpu->regs.hstatus = HSTATUS_SPV | HSTATUS_VSXL_64 | (1ULL << HSTATUS_VGEIN_OFF);
vcpu->regs.sstatus = SSTATUS_SPP_BIT | SSTATUS_FS_DIRTY | SSTATUS_XS_DIRTY;
vcpu->regs.sepc = entry;
vcpu->regs.a0 = vcpu->arch.hart_id = vcpu->id;
Expand Down

0 comments on commit bd10f03

Please sign in to comment.