From 422cd14aac89cd3e7c62fb9d2d13a11767efc948 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 24 Feb 2018 05:33:16 -0800 Subject: [PATCH] Implement driver for the GIC-400. --- exosphere/interrupt.c | 98 +++++++++++++++++++++++++++++++++++++++++++ exosphere/interrupt.h | 49 ++++++++++++++++++++++ exosphere/mmu.h | 2 +- 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 exosphere/interrupt.c create mode 100644 exosphere/interrupt.h diff --git a/exosphere/interrupt.c b/exosphere/interrupt.c new file mode 100644 index 000000000..1ed4800fd --- /dev/null +++ b/exosphere/interrupt.c @@ -0,0 +1,98 @@ +#include +#include + +#include "utils.h" +#include "interrupt.h" + +/* Global of registered handlers. */ +struct { + unsigned int id; + void (*handler)(void); +} g_registered_interrupts[MAX_REGISTERED_INTERRUPTS] = { {0, NULL}, {0, NULL}, {0, NULL}, {0, NULL} }; + +/* Prototypes for internal (private) functions. */ +unsigned int get_interrupt_id(void); + + +/* Initializes the GIC. TODO: This must be called during wakeup. */ +void intr_initialize_gic(void) { + /* Setup interrupts 0-0x1F as nonsecure with highest non-secure priority. */ + GICD_IGROUPR[0] = 0xFFFFFFFF; + for (unsigned int i = 0; i < 0x20; i++) { + GICD_IPRIORITYR[i] = GIC_PRI_HIGHEST; + } + + /* Setup the GICC. */ + GICC_CTLR = 0x1D9; + GICC_PMR = GIC_PRI_HIGHEST; + GICC_BPR = 7; +} + +/* Sets an interrupt's group in the GICD. */ +void intr_set_group(unsigned int id, int group) { + GICD_IGROUPR[id >> 5] = (GICD_IGROUPR[id >> 5] & (~(1 << (id & 0x1F)))) | ((group & 1) << (id & 0x1F)); +} + +/* Sets an interrupt id as pending in the GICD. */ +void intr_set_pending(unsigned int id) { + GICD_ISPENDR[id >> 5] = 1 << (id & 0x1F); +} + +/* Sets an interrupt's priority in the GICD. */ +void intr_set_priority(unsigned int id, uint8_t priority) { + GICD_IPRIORITYR[id] = priority; +} + +/* Sets an interrupt's target CPU mask in the GICD. */ +void intr_set_cpu_mask(unsigned int id, uint8_t mask) { + GICD_ITARGETSR[id] = mask; +} + +/* Sets an interrupt's edge/level bits in the GICD. */ +void intr_set_edge_level(unsigned int id, int edge_level) { + GICD_ICFGR[id >> 4] = (GICD_ICFGR[id >> 4] & (~(3 << ((id & 0xF) << 1))) | (((edge_level & 1) << 1) << ((id & 0xF) << 1)); +} + +/* Sets an interrupt's enabled status in the GICD. */ +void intr_set_enabled(unsigned int id, int enabled) { + GICD_ISENABLER[id >> 5] = (enabled & 1) << (id & 0x1F); +} + +/* To be called by FIQ handler. */ +void handle_registered_interrupt(void) { + unsigned int interrupt_id = get_interrupt_id(); + if (interrupt_id <= 0xDF) { + bool found_handler = false; + for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { + if (g_registered_interrupts[i].id == interrupt_id) { + found_handler = true; + g_registered_interrupts[i].handler(); + /* Mark that interrupt is done. */ + GICC_EOIR = interrupt_id; + break; + } + } + /* We must have found a handler, or something went wrong. */ + if (!found_handler) { + panic(); + } + } +} + +/* Registers an interrupt into the global. */ +void intr_register_handler(unsigned int id, void (*handler)(void)) { + bool registered_handler = false; + for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { + if (g_registered_interrupts[i].id == 0) { + g_registered_interrupts[i].handler = handler; + g_registered_interrupts[i].id = id; + registered_handler = true; + break; + } + } + /* Failure to register is an error condition. */ + if (!registered_handler) { + panic(); + } +} + diff --git a/exosphere/interrupt.h b/exosphere/interrupt.h new file mode 100644 index 000000000..3b631f38c --- /dev/null +++ b/exosphere/interrupt.h @@ -0,0 +1,49 @@ +#ifndef EXOSPHERE_INTERRUPT_H +#define EXOSPHERE_INTERRUPT_H + +#include +#include "mmu.h" + +/* Exosphere driver for the Tegra X1 GIC-400 registers. */ + + +#define MAX_REGISTERED_INTERRUPTS 4 +#define INTERRUPT_ID_SECURITY_ENGINE 0x5A + +#define GICD_BASE (mmio_get_device_address(MMIO_DEVID_GICD)) +#define GICC_BASE (mmio_get_device_address(MMIO_DEVID_GICC)) + +#define GICD_IGROUPR ((volatile uint32_t *)(GICD_BASE + 0x080ULL)) +#define GICD_ISENABLER ((volatile uint32_t *)(GICD_BASE + 0x100ULL)) +#define GICD_ISPENDR ((volatile uint32_t *)(GICD_BASE + 0x200ULL)) +#define GICD_IPRIORITYR ((volatile uint8_t *)(GICD_BASE + 0x400ULL)) +#define GICD_ITARGETSR ((volatile uint8_t *)(GICD_BASE + 0x800ULL)) +#define GICD_ICFGR ((volatile uint32_t *)(GICD_BASE + 0xC00ULL)) + +#define GICC_CTLR (*((volatile uint32_t *)(GICC_BASE + 0x0000ULL))) +#define GICC_PMR (*((volatile uint32_t *)(GICC_BASE + 0x0004ULL))) +#define GICC_BPR (*((volatile uint32_t *)(GICC_BASE + 0x0008ULL))) +#define GICC_IAR (*((volatile uint32_t *)(GICC_BASE + 0x000CULL))) +#define GICC_EOIR (*((volatile uint32_t *)(GICC_BASE + 0x0010ULL))) + +#define GIC_PRI_HIGHEST_SECURE 0x00 +#define GIC_PRI_HIGHEST_NONSECURE 0x80 + +#define GIC_GROUP_SECURE 0 +#define GIC_GROUP_NONSECURE 1 + +/* To be called by FIQ handler. */ +void handle_registered_interrupt(void); + +/* Initializes the GIC. TODO: This must be called during wakeup. */ +void intr_initialize_gic(void); + + +void intr_register_handler(unsigned int id, void (*handler)(void)); +void intr_set_group(unsigned int id, int group); +void intr_set_pending(unsigned int id); +void intr_set_priority(unsigned int id, uint8_t priority); +void intr_set_cpu_mask(unsigned int id, uint8_t mask); +void intr_set_edge_level(unsigned int id, int edge_level); +void intr_set_enabled(unsigned int id, int enabled); +#endif \ No newline at end of file diff --git a/exosphere/mmu.h b/exosphere/mmu.h index 640ce7923..3f32a7c44 100644 --- a/exosphere/mmu.h +++ b/exosphere/mmu.h @@ -199,7 +199,7 @@ static const struct { }; #define MMIO_DEVID_GICD 0 -#define MMIO_DEVID_ICC 1 +#define MMIO_DEVID_GICC 1 #define MMIO_DEVID_UART_A 2 #define MMIO_DEVID_CLKRST 3 #define MMIO_DEVID_RTC_PMC 4