Skip to content

Commit

Permalink
checkpoint: it works!!!! user-space works and IRQs work
Browse files Browse the repository at this point in the history
Timer IRQs are now handled, some fixes in seL4 itself were needed
  • Loading branch information
Ivan-Velickovic committed Nov 1, 2024
1 parent 8ffd50b commit 9a2ee1f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 24 deletions.
Binary file modified examples/simple/board/qemu_virt_riscv64/rootfs.cpio.gz
Binary file not shown.
1 change: 1 addition & 0 deletions examples/simple/board/qemu_virt_riscv64/simple.system
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
</virtual_machine>

<irq irq="10" id="1" />
<irq irq="130" id="2" />
</protection_domain>
</system>
18 changes: 14 additions & 4 deletions examples/simple/vmm.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#if defined(CONFIG_ARCH_RISCV)
#include <libvmm/arch/riscv/linux.h>
#include <libvmm/arch/riscv/fault.h>
#include <libvmm/arch/riscv/plic.h>
#endif

// @ivanv: ideally we would have none of these hardcoded values
Expand Down Expand Up @@ -59,6 +60,10 @@
* across platforms. */
#define SERIAL_IRQ_CH 1

#if defined(BOARD_qemu_virt_riscv64)
#define VTIMER_IRQ_CH 2
#endif

#if defined(BOARD_qemu_virt_aarch64)
#define SERIAL_IRQ 33
#elif defined(BOARD_qemu_virt_riscv64)
Expand Down Expand Up @@ -133,10 +138,15 @@ void init(void) {
void notified(microkit_channel ch) {
switch (ch) {
case SERIAL_IRQ_CH: {
// bool success = virq_inject(GUEST_VCPU_ID, SERIAL_IRQ);
// if (!success) {
// LOG_VMM_ERR("IRQ %d dropped on vCPU %d\n", SERIAL_IRQ, GUEST_VCPU_ID);
// }
bool success = virq_inject(GUEST_VCPU_ID, SERIAL_IRQ);
if (!success) {
LOG_VMM_ERR("IRQ %d dropped on vCPU %d\n", SERIAL_IRQ, GUEST_VCPU_ID);
}
break;
}
case VTIMER_IRQ_CH: {
// TODO: handle vcpu id properly
plic_inject_timer_irq(0);
break;
}
default:
Expand Down
28 changes: 14 additions & 14 deletions src/arch/riscv/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
#define MACHINE_IMPL_ID 0
#define MACHINE_VENDOR_ID 0

/* What SBI extensions we actually support */
/*
* List of SBI extensions. Note that what extensions
* we actually support is a subset of this list.
*/
// TODO: support system suspend
enum sbi_extensions {
SBI_EXTENSION_BASE = 0x10,
Expand All @@ -34,7 +37,7 @@ enum sbi_extensions {
SBI_EXTENSION_DEBUG_CONSOLE = 0x4442434e,
};

enum sbi_ {
enum sbi_debug_extension {
SBI_CONSOLE_PUTCHAR = 1,
};

Expand Down Expand Up @@ -155,10 +158,10 @@ extern uintptr_t guest_ram_vaddr;
#define FUNCT3_CLW 0b010

struct fault_instruction fault_decode_instruction(size_t vcpu_id, seL4_UserContext *regs, seL4_Word ip) {
LOG_VMM("decoding at ip 0x%lx\n", ip);
// LOG_VMM("decoding at ip 0x%lx\n", ip);
seL4_Word guest_physical = guest_virtual_physical(ip, vcpu_id);
assert(guest_physical >= guest_ram_vaddr && guest_physical <= guest_ram_vaddr + 0x10000000);
LOG_VMM("guest_physical: 0x%lx\n", guest_physical);
// LOG_VMM("guest_physical: 0x%lx\n", guest_physical);
/*
* For guest OSes that use the RISC-V 'C' extension we must read the instruction 16-bits at
* a time, in order to avoid UB since it is valid for *all* instructions, compressed or not,
Expand All @@ -167,7 +170,7 @@ struct fault_instruction fault_decode_instruction(size_t vcpu_id, seL4_UserConte
uint16_t instruction_lo = *((uint16_t *)guest_physical);
uint16_t instruction_hi = *(uint16_t *)(guest_physical + 16);
uint32_t instruction = ((uint32_t)instruction_hi << 16) | instruction_lo;
LOG_VMM("guest_physical: 0x%lx, instruction: 0x%lx, instruction_lo: 0x%x, instruction_hi: 0x%x\n", guest_physical, instruction, instruction_lo, instruction_hi);
// LOG_VMM("guest_physical: 0x%lx, instruction: 0x%lx, instruction_lo: 0x%x, instruction_hi: 0x%x\n", guest_physical, instruction, instruction_lo, instruction_hi);
// TODO: check this.
uint8_t op_code = instruction & 0x7f;
/* funct3 is from bits 12:14. */
Expand All @@ -180,14 +183,12 @@ struct fault_instruction fault_decode_instruction(size_t vcpu_id, seL4_UserConte
/* If we are in here, we are dealing with a compressed instruction */
switch (instruction_lo >> 13) {
case FUNCT3_CSW:
LOG_VMM("compressed store\n");
return (struct fault_instruction){
.op_code = OP_CODE_STORE,
.width = 2,
.rs2 = (instruction_lo >> 2) & (BIT(3) - 1),
};
case FUNCT3_CLW:
LOG_VMM("compressed load\n");
return (struct fault_instruction){
.op_code = OP_CODE_LOAD,
.width = 2,
Expand All @@ -199,14 +200,12 @@ struct fault_instruction fault_decode_instruction(size_t vcpu_id, seL4_UserConte

switch (op_code) {
case OP_CODE_STORE:
LOG_VMM("store\n");
return (struct fault_instruction){
.op_code = OP_CODE_STORE,
.width = 4,
.rs2 = (instruction >> 20) & (BIT(5) - 1),
};
case OP_CODE_LOAD:
LOG_VMM("load\n");
return (struct fault_instruction){
.op_code = OP_CODE_LOAD,
.width = 4,
Expand Down Expand Up @@ -258,7 +257,7 @@ static bool fault_handle_sbi_timer(size_t vcpu_id, seL4_Word sbi_fid, seL4_UserC
seL4_Error err = seL4_RISCV_VCPU_WriteRegs(BASE_VCPU_CAP + vcpu_id, seL4_VCPUReg_TIMER, stime_value);
assert(!err);
}
LOG_VMM("setting timer stime_value: 0x%lx, curr_time: 0x%lx\n", stime_value, curr_time);
// LOG_VMM("setting timer stime_value: 0x%lx, curr_time: 0x%lx\n", stime_value, curr_time);

regs->a0 = SBI_SUCCESS;

Expand Down Expand Up @@ -296,14 +295,15 @@ static bool fault_handle_sbi_base(size_t vcpu_id, seL4_Word sbi_fid, seL4_UserCo
seL4_Word probe_eid = regs->a0;
switch (probe_eid) {
case SBI_EXTENSION_BASE:
// case SBI_EXTENSION_TIMER:
case SBI_EXTENSION_TIMER:
case SBI_EXTENSION_HART_STATE_MANAGEMENT:
case SBI_EXTENSION_SYSTEM_RESET:
case SBI_EXTENSION_DEBUG_CONSOLE:
regs->a0 = SBI_SUCCESS;
regs->a1 = 1;
return true;
default:
// TODO: print out string name of extension
LOG_VMM("guest probed for SBI EID 0x%lx that is not supported\n", probe_eid);
regs->a0 = SBI_ERR_NOT_SUPPORTED;
return true;
Expand All @@ -322,7 +322,7 @@ static bool fault_handle_sbi(size_t vcpu_id, seL4_UserContext *regs) {
seL4_Word sbi_eid = regs->a7;
/* SBI function ID for the given extension */
seL4_Word sbi_fid = regs->a6;
LOG_VMM("SBI handle EID 0x%lx, FID: 0x%lx\n", sbi_eid, sbi_fid);
// LOG_VMM("SBI handle EID 0x%lx, FID: 0x%lx\n", sbi_eid, sbi_fid);
switch (sbi_eid) {
case SBI_EXTENSION_BASE:
// TODO: error handling
Expand Down Expand Up @@ -353,11 +353,11 @@ bool fault_handle(size_t vcpu_id, microkit_msginfo msginfo) {
seL4_UserContext regs;
seL4_TCB_ReadRegisters(BASE_VM_TCB_CAP + vcpu_id, false, 0, sizeof(seL4_UserContext) / sizeof(seL4_Word), &regs);
size_t label = microkit_msginfo_get_label(msginfo);
LOG_VMM("handling fault '%s'\n", fault_to_string(label));
// LOG_VMM("handling fault '%s'\n", fault_to_string(label));
bool success = false;
switch (label) {
case seL4_Fault_VMFault: {
LOG_VMM("fault on addr 0x%lx at pc 0x%lx\n", seL4_GetMR(seL4_VMFault_Addr), regs.pc);
// LOG_VMM("fault on addr 0x%lx at pc 0x%lx\n", seL4_GetMR(seL4_VMFault_Addr), regs.pc);
seL4_Word addr = seL4_GetMR(seL4_VMFault_Addr);
seL4_Word fsr = seL4_GetMR(seL4_VMFault_FSR);
if (addr >= PLIC_ADDR && addr < PLIC_ADDR + PLIC_SIZE) {
Expand Down
48 changes: 42 additions & 6 deletions src/arch/riscv/plic.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <libvmm/arch/riscv/plic.h>
#include <libvmm/arch/riscv/fault.h>

#define DEBUG_PLIC
// #define DEBUG_PLIC

#if defined(DEBUG_PLIC)
#define LOG_PLIC(...) do{ LOG_VMM("PLIC: "); printf(__VA_ARGS__); }while(0)
Expand Down Expand Up @@ -36,7 +36,7 @@ struct plic_regs plic_regs;
#define SIP_TIMER (1 << 5)

bool plic_inject_timer_irq(size_t vcpu_id) {
LOG_VMM("injecting timer irq\n");
// LOG_VMM("injecting timer irq\n");
seL4_RISCV_VCPU_ReadRegs_t res = seL4_RISCV_VCPU_ReadRegs(BASE_VCPU_CAP + vcpu_id, seL4_VCPUReg_SIP);
assert(!res.error);
seL4_Word sip = res.value;
Expand All @@ -52,15 +52,22 @@ bool plic_inject_timer_irq(size_t vcpu_id) {
return true;
}

#define SIP_EXTERNAL (1 << 9)

// TODO: this is for context 0
#define PLIC_IRQ_ENABLE_START 0x2000
#define PLIC_IRQ_ENABLE_END 0x1F1FFC

// TODO: need to check whether the ENDs are correct, I think they might be off by one
#define PLIC_IRQ_PRIORITY_START 0x0
#define PLIC_IRQ_PRIORITY_END 0xFFC

#define PLIC_PRIOTIY_THRESHOLD_CONTEXT_1_START 0x201000
#define PLIC_PRIOTIY_THRESHOLD_CONTEXT_1_END 0x201004
#define PLIC_CLAIM_COMPLETE_CONTEXT_1_START 0x201004
#define PLIC_CLAIM_COMPLETE_CONTEXT_1_END 0x201008

uint32_t plic_pending_irq = 0;

static bool plic_handle_fault_read(size_t vcpu_id, size_t offset, seL4_UserContext *regs, struct fault_instruction *instruction) {
LOG_PLIC("handling read at offset: 0x%lx\n", offset);
Expand All @@ -76,6 +83,17 @@ static bool plic_handle_fault_read(size_t vcpu_id, size_t offset, seL4_UserConte
data = plic_regs.enable_bits[context][enable_group];
break;
}
case PLIC_CLAIM_COMPLETE_CONTEXT_1_START: {
LOG_PLIC("read complete claim for pending IRQ %d\n", plic_pending_irq);
data = plic_pending_irq;
plic_pending_irq = 0;
seL4_RISCV_VCPU_ReadRegs_t sip = seL4_RISCV_VCPU_ReadRegs(BASE_VCPU_CAP + vcpu_id, seL4_VCPUReg_SIP);
assert(!sip.error);
sip.value &= ~SIP_EXTERNAL;
int err = seL4_RISCV_VCPU_WriteRegs(BASE_VCPU_CAP + vcpu_id, seL4_VCPUReg_SIP, sip.value);
assert(!err);
break;
}
default:
LOG_PLIC("invalid offset 0x%lx\n", offset);
return false;
Expand Down Expand Up @@ -136,11 +154,22 @@ static bool plic_handle_fault_write(size_t vcpu_id, size_t offset, seL4_UserCont
plic_regs.priority[irq_index] = data;
break;
}
case PLIC_PRIOTIY_THRESHOLD_CONTEXT_1_START...PLIC_PRIOTIY_THRESHOLD_CONTEXT_1_END: {
case PLIC_PRIOTIY_THRESHOLD_CONTEXT_1_START: {
LOG_PLIC("write priority threshold for context %d: %d\n", 1, data);
plic_regs.priority_threshold[1] = data;
break;
}
case PLIC_CLAIM_COMPLETE_CONTEXT_1_START: {
LOG_PLIC("write complete claim for pending IRQ %d\n", plic_pending_irq);
/* TODO: we should be checking here, and probably in a lot of other places, that the
* IRQ attempting to be claimed is actually enabled. */
/* TODO: double check but when we get a claim we should be clearing the pending bit */
// size_t irq_pending_group = plic_pending_irq / 32;
plic_pending_irq = 0;
// plic_regs.pending_bits[irq_pending_group] = 0;
microkit_irq_ack(1);
break;
}
default:
LOG_PLIC("invalid offset 0x%lx\n", offset);
return false;
Expand All @@ -149,8 +178,6 @@ static bool plic_handle_fault_write(size_t vcpu_id, size_t offset, seL4_UserCont
return true;
}

#define SIP_EXTERNAL (1 << 9)

#define PLIC_MAX_REGISTERED_IRQS 10

struct virq {
Expand Down Expand Up @@ -178,17 +205,26 @@ bool plic_register_irq(size_t vcpu_id, size_t irq, virq_ack_fn_t ack_fn, void *a
}

bool plic_inject_irq(size_t vcpu_id, int irq) {
// size_t context = irq / 128;
size_t enable_group = irq / 128;
if ((plic_regs.enable_bits[1][enable_group] & (1 << irq)) == 0) {
return true;
}

seL4_RISCV_VCPU_ReadRegs_t res = seL4_RISCV_VCPU_ReadRegs(BASE_VCPU_CAP + vcpu_id, seL4_VCPUReg_SIP);
assert(!res.error);
seL4_Word sip = res.value;

size_t irq_pending_group = irq / 32;
size_t irq_bit = irq % 32;

LOG_PLIC("injecting for IRQ %d (group: %d, bit: %d)\n", irq, irq_pending_group, irq_bit);
// LOG_PLIC("injecting for IRQ %d (group: %d, bit: %d)\n", irq, irq_pending_group, irq_bit);

// TODO: should we check if it's already pending?

// assert(plic_pending_irq == 0);
plic_pending_irq = irq;

plic_regs.pending_bits[irq_pending_group] |= 1 << irq_bit;

sip |= SIP_EXTERNAL;
Expand Down

0 comments on commit 9a2ee1f

Please sign in to comment.