diff --git a/examples/simple/board/qemu_virt_riscv64/rootfs.cpio.gz b/examples/simple/board/qemu_virt_riscv64/rootfs.cpio.gz index e689dd29..be24cf90 100644 Binary files a/examples/simple/board/qemu_virt_riscv64/rootfs.cpio.gz and b/examples/simple/board/qemu_virt_riscv64/rootfs.cpio.gz differ diff --git a/examples/simple/board/qemu_virt_riscv64/simple.system b/examples/simple/board/qemu_virt_riscv64/simple.system index 5cb481b3..90f6e99a 100644 --- a/examples/simple/board/qemu_virt_riscv64/simple.system +++ b/examples/simple/board/qemu_virt_riscv64/simple.system @@ -19,5 +19,6 @@ + diff --git a/examples/simple/vmm.c b/examples/simple/vmm.c index caae0fdc..9e7d1ab3 100644 --- a/examples/simple/vmm.c +++ b/examples/simple/vmm.c @@ -17,6 +17,7 @@ #if defined(CONFIG_ARCH_RISCV) #include #include +#include #endif // @ivanv: ideally we would have none of these hardcoded values @@ -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) @@ -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: diff --git a/src/arch/riscv/fault.c b/src/arch/riscv/fault.c index 4aa56967..025a202e 100644 --- a/src/arch/riscv/fault.c +++ b/src/arch/riscv/fault.c @@ -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, @@ -34,7 +37,7 @@ enum sbi_extensions { SBI_EXTENSION_DEBUG_CONSOLE = 0x4442434e, }; -enum sbi_ { +enum sbi_debug_extension { SBI_CONSOLE_PUTCHAR = 1, }; @@ -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, @@ -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. */ @@ -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, @@ -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, @@ -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; @@ -296,7 +295,7 @@ 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: @@ -304,6 +303,7 @@ static bool fault_handle_sbi_base(size_t vcpu_id, seL4_Word sbi_fid, seL4_UserCo 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; @@ -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 @@ -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), ®s); 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) { diff --git a/src/arch/riscv/plic.c b/src/arch/riscv/plic.c index c8195f8a..485b941b 100644 --- a/src/arch/riscv/plic.c +++ b/src/arch/riscv/plic.c @@ -4,7 +4,7 @@ #include #include -#define DEBUG_PLIC +// #define DEBUG_PLIC #if defined(DEBUG_PLIC) #define LOG_PLIC(...) do{ LOG_VMM("PLIC: "); printf(__VA_ARGS__); }while(0) @@ -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; @@ -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); @@ -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; @@ -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; @@ -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 { @@ -178,6 +205,12 @@ 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; @@ -185,10 +218,13 @@ bool plic_inject_irq(size_t vcpu_id, int irq) { 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;