nixos-riscv/opensbi-test-plic.patch
2024-08-21 12:29:52 +02:00

194 lines
5.6 KiB
Diff

diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c
index 0ae604a..7b1d95e 100644
--- a/lib/sbi/sbi_irqchip.c
+++ b/lib/sbi/sbi_irqchip.c
@@ -9,6 +9,9 @@
#include <sbi/sbi_irqchip.h>
#include <sbi/sbi_platform.h>
+#include <sbi/sbi_console.h>
+
+static void do_plic_test(void);
static int default_irqfn(void)
{
@@ -37,8 +40,10 @@ int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot)
if (rc)
return rc;
- if (ext_irqfn != default_irqfn)
- csr_set(CSR_MIE, MIP_MEIP);
+ //csr_set(CSR_MIE, MIP_SEIP);
+ //csr_set(CSR_MSTATUS, MSTATUS_SIE);
+
+ do_plic_test();
return 0;
}
@@ -47,8 +52,163 @@ void sbi_irqchip_exit(struct sbi_scratch *scratch)
{
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
- if (ext_irqfn != default_irqfn)
- csr_clear(CSR_MIE, MIP_MEIP);
+ //csr_clear(CSR_MIE, MIP_SEIP);
+ //csr_clear(CSR_MSTATUS, MSTATUS_SIE);
sbi_platform_irqchip_exit(plat);
}
+
+
+/* ----------------- PLIC tests ---------------- */
+
+
+#define MIE_MEIE (1UL << 11) // Machine External Interrupt Enable
+#define SIE_SEIE (1UL << 9)
+#define MIDELEG_SEIE (1UL << 9) // Delegate Machine External Interrupt to Supervisor
+#define PLIC_TIMER_PORT 4
+// Base address of PLIC
+#define PLIC_BASE 0x0000000040800000UL
+#define PLIC_PRIORITY_OFFSET 0x0UL
+#define PLIC_PENDING_OFFSET 0x1000UL
+#define PLIC_ENABLE_OFFSET 0x2000UL
+#define PLIC_THRESHOLD_OFFSET 0x200000UL
+#define PLIC_CLAIM_OFFSET 0x200004UL
+
+// Aux timer
+#define AUX_TIMER_BASE 0x40010000UL
+#define MTIMECMP_OFFSET 0x4000UL
+#define MTIME_OFFSET 0xBFF8UL
+
+#define MSTATUS_MPP_MASK (3 << 11)
+#define MSTATUS_MPP_SUPERVISOR (1 << 11)
+
+static volatile unsigned long *mtime = (unsigned long *)(AUX_TIMER_BASE + MTIME_OFFSET);
+static volatile unsigned long *mtimecmp = (unsigned long *)(AUX_TIMER_BASE + MTIMECMP_OFFSET);
+
+
+static void dumpregs(int machine)
+{
+ char *prefix = "\t";
+ char *suffix = "\t";
+ sbi_printf("Registers:\n");
+ if (machine) {
+ sbi_printf("%sMIE%s: 0x%" PRILX "\n",
+ prefix, suffix, csr_read(CSR_MIE));
+ sbi_printf("%sMIP%s: 0x%" PRILX "\n",
+ prefix, suffix, csr_read(CSR_MIP));
+ sbi_printf("%sMSTATUS%s: 0x%" PRILX "\n",
+ prefix, suffix, csr_read(CSR_MSTATUS));
+ sbi_printf("%sMIDELEG%s: 0x%" PRILX "\n",
+ prefix, suffix, csr_read(CSR_MIDELEG));
+ }
+ sbi_printf("%sSIE%s: 0x%" PRILX "\n",
+ prefix, suffix, csr_read(CSR_SIE));
+ sbi_printf("%sSIP%s: 0x%" PRILX "\n",
+ prefix, suffix, csr_read(CSR_SIP));
+ sbi_printf("%sSSTATUS%s: 0x%" PRILX "\n",
+ prefix, suffix, csr_read(CSR_SSTATUS));
+}
+
+static void __attribute__((optimize("O0"))) switch_to_supervisor_mode(int (*target_address)(void))
+{
+ unsigned long mstatus;
+
+ // Read the current mstatus
+ asm volatile("csrr %0, mstatus" : "=r"(mstatus));
+
+ // Set the MPP field to supervisor mode
+ mstatus = (mstatus & ~MSTATUS_MPP_MASK) | MSTATUS_MPP_SUPERVISOR;
+
+ // Write back the modified mstatus
+ asm volatile("csrw mstatus, %0" : : "r"(mstatus));
+
+ // Set the mepc to the target address
+ asm volatile("csrw mepc, %0" : : "r"(target_address));
+
+ // Use mret to return to the specified address in supervisor mode
+ asm volatile("mret");
+}
+
+static int supervisor_mode_code(void)
+{
+ sbi_printf("Hello from supervisor\n");
+ dumpregs(0);
+
+ /* Enable timer interrupt */
+ *mtimecmp = *mtime + 10000;
+
+ sbi_printf("Timer alarm programmed\n");
+ sbi_printf("Waiting for interrupt...\n");
+ int i = 0;
+ char *s = "-\\|/";
+ while (1) {
+ for (volatile unsigned long j = 0; j < 100000; j++);
+ sbi_printf("\r%c", s[i++]);
+ if (i >= 4)
+ i = 0;
+ }
+ return 0;
+}
+
+static void supervisor_trap_entry(void)
+{
+ sbi_printf("\nSupervisor Trap Entry Reached!\n");
+}
+
+static void do_plic_test(void)
+{
+ sbi_printf("--- TESTING PLIC ---\n");
+
+ /* Disable auxiliar timer interrupt */
+ *mtimecmp = 0xffffffffUL;
+ sbi_printf("Timer interrupt disabled\n");
+
+
+ /* Enable supervisor interrupt delegation */
+ csr_set(CSR_SIE, SIE_SEIE); // Enable supervisor external interrupts
+ csr_set(CSR_SSTATUS, SSTATUS_SIE); // Enable global interrupts in supervisor mode
+ csr_set(CSR_MIDELEG, MIDELEG_SEIE); // Delegate machine interrupts to supervisor mode
+ csr_set(CSR_STVEC, &supervisor_trap_entry);
+ sbi_printf("Enabled supervisor delegation:\n");
+
+ dumpregs(1);
+
+ /* Configure PLIC aux timer input */
+ volatile unsigned *plic_priority = (unsigned *)(PLIC_BASE + PLIC_PRIORITY_OFFSET + PLIC_TIMER_PORT * 4);
+ volatile unsigned *plic_enable = (unsigned *)(PLIC_BASE + PLIC_ENABLE_OFFSET);
+ volatile unsigned *plic_threshold = (unsigned *)(PLIC_BASE + PLIC_THRESHOLD_OFFSET);
+ volatile unsigned *plic_claim = (unsigned *)(PLIC_BASE + PLIC_CLAIM_OFFSET);
+ volatile unsigned *plic_pending = (unsigned *)(PLIC_BASE + PLIC_PENDING_OFFSET);
+
+ sbi_printf("Enabling timer in PLIC\n");
+ *plic_priority = PLIC_TIMER_PORT;
+ *plic_threshold = PLIC_TIMER_PORT - 1;
+ *plic_enable |= (1 << PLIC_TIMER_PORT);
+
+ /* Clear interrupt */
+ sbi_printf("Pending: %d\n", *plic_pending);
+ unsigned claim = *plic_claim;
+ sbi_printf("Claim: %d\n", claim);
+ *plic_claim = claim;
+ sbi_printf("Pending: %d\n", *plic_pending);
+
+ sbi_printf("Clearing MIP\n");
+ csr_write(CSR_MIP, 0);
+
+ /* Enable external timer interrupts */
+ sbi_printf("Enabling MEIE in MIE register\n");
+ csr_set(CSR_MIE, MIE_MEIE); /* Needed? */
+ sbi_printf("Enabling MIE in MSTATUS register\n");
+ csr_set(CSR_MSTATUS, MSTATUS_MIE); /* Needed? */
+
+ sbi_printf("Switching to supervisor\n");
+
+ dumpregs(1);
+
+ switch_to_supervisor_mode(&supervisor_mode_code);
+
+ /* Never reached */
+ while (1);
+}
+
+