Skip to content

Commit

Permalink
feat: working on gic
Browse files Browse the repository at this point in the history
Signed-off-by: Zone.N <[email protected]>
  • Loading branch information
MRNIU committed Jan 19, 2025
1 parent aa1aec7 commit 2a0ca6a
Show file tree
Hide file tree
Showing 5 changed files with 377 additions and 10 deletions.
2 changes: 1 addition & 1 deletion 3rd/cpu_io
3 changes: 2 additions & 1 deletion src/kernel/arch/aarch64/arch_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ auto ArchInit(uint32_t argc, const uint8_t* argv) -> uint32_t {
uart.PutChar('!');
uart.PutChar('\n');

// __asm__ volatile("sev");
/// @todo psci
/// https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/psci.txt

return 0;
}
Expand Down
246 changes: 244 additions & 2 deletions src/kernel/arch/aarch64/interrupt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,238 @@

#include "interrupt.h"

#include "gic.h"
#include "io.hpp"
#include "kernel_fdt.hpp"
#include "kernel_log.hpp"
#include "sk_cstdio"

uint64_t dist_base_addr_ = 0;
uint64_t redist_base_addr_ = 0;

#define D_CTLR 0x0
#define D_TYPER 0x4
#define D_IGROUPR(n) (0x80 + (uint64_t)(n)*4)
#define D_ISENABLER(n) (0x100 + (uint64_t)(n)*4)
#define D_ICENABLER(n) (0x180 + (uint64_t)(n)*4)
#define D_ISPENDR(n) (0x200 + (uint64_t)(n)*4)
#define D_ICPENDR(n) (0x280 + (uint64_t)(n)*4)
#define D_IPRIORITYR(n) (0x400 + (uint64_t)(n)*4)
#define D_ITARGETSR(n) (0x800 + (uint64_t)(n)*4)
#define D_ICFGR(n) (0xc00 + (uint64_t)(n)*4)

#define R_CTLR 0x0
#define R_WAKER 0x14

#define SGI_BASE 0x10000
#define R_IGROUPR0 (SGI_BASE + 0x80)
#define R_ISENABLER0 (SGI_BASE + 0x100)
#define R_ICENABLER0 (SGI_BASE + 0x180)
#define R_ICPENDR0 (SGI_BASE + 0x280)
#define R_IPRIORITYR(n) (SGI_BASE + 0x400 + (n)*4)
#define R_ICFGR0 (SGI_BASE + 0xc00)
#define R_ICFGR1 (SGI_BASE + 0xc04)
#define R_IGRPMODR0 (SGI_BASE + 0xd00)

#define UART0 (KERNBASE + 0x09000000L)
#define UART0_IRQ 33

// virtio mmio interface
#define VIRTIO0 (0x0a000000L)
#define VIRTIO0_IRQ 48

#define TIMER0_IRQ 27

// interrupt controller GICv3
#define GICV3 (0x08000000L)
#define GICV3_REDIST (0x080a0000L)

#define NCPU 4

static void gic_setup_ppi(uint32_t cpuid, uint32_t intid)
__attribute__((unused));
static void gic_setup_spi(uint32_t intid);

static struct { char *rdist_addrs[NCPU]; } gicv3;

static void gicd_write(uint32_t off, uint32_t val) {
io::Out<uint32_t>(dist_base_addr_ + off, val);
}

static uint32_t gicd_read(uint32_t off) {
return io::In<uint32_t>(dist_base_addr_ + off);
}

static void gicr_write(uint32_t cpuid, uint32_t off, uint32_t val) {
io::Out<uint32_t>((uint64_t)gicv3.rdist_addrs[cpuid] + off, val);
}

static uint32_t gicr_read(uint32_t cpuid, uint32_t off) {
return io::In<uint32_t>((uint64_t)gicv3.rdist_addrs[cpuid] + off);
}

static void giccinit() {
cpu_io::ICC_IGRPEN1_EL1::Write(0);
cpu_io::ICC_PMR_EL1::Priority::Set();
}

static void gicdinit() {
gicd_write(D_CTLR, 0);

uint32_t typer = gicd_read(D_TYPER);
uint32_t lines = typer & 0x1f;

printf("lines %d\n", lines);

for (int i = 0; i < lines; i++) gicd_write(D_IGROUPR(i), ~0);
}

static void gicrinit(uint32_t cpuid) {
gicr_write(cpuid, R_CTLR, 0);

cpu_io::ICC_SRE_EL1::Write(cpu_io::ICC_SRE_EL1::Read() | 1);

gicr_write(cpuid, R_IGROUPR0, ~0);
gicr_write(cpuid, R_IGRPMODR0, 0);

uint32_t waker = gicr_read(cpuid, R_WAKER);
gicr_write(cpuid, R_WAKER, waker & ~(1 << 1));
while (gicr_read(cpuid, R_WAKER) & (1 << 2))
;
}

static void gic_enable() {
gicd_write(D_CTLR, (1 << 1));
cpu_io::ICC_IGRPEN1_EL1::Write(1);
}

void gicv3init() {
for (int i = 0; i < NCPU; i++) {
gicv3.rdist_addrs[i] = (char *)(GICV3_REDIST + (i)*0x20000);
}

gicdinit();

gic_setup_spi(UART0_IRQ);
gic_setup_spi(VIRTIO0_IRQ);
}

void gicv3inithart() {
// int cpu = cpuid();
int cpu = 0;

giccinit();
gicrinit(cpu);

gic_setup_ppi(cpu, TIMER0_IRQ);

gic_enable();
}

static void gic_enable_int(uint32_t intid) {
uint32_t is = gicd_read(D_ISENABLER(intid / 32));
is |= 1 << (intid % 32);
gicd_write(D_ISENABLER(intid / 32), is);
}

int gic_int_enabled(uint32_t intid) {
uint32_t is = gicd_read(D_ISENABLER(intid / 32));
return is & (1 << (intid % 32));
}

static void gic_clear_pending(uint32_t intid) {
uint32_t ic = gicd_read(D_ICPENDR(intid / 32));
ic |= 1 << (intid % 32);
gicd_write(D_ICPENDR(intid / 32), ic);
}

static void gic_set_prio0(uint32_t intid) {
// set priority to 0
uint32_t p = gicd_read(D_IPRIORITYR(intid / 4));
p &= ~((uint32_t)0xff << (intid % 4 * 8)); // set prio 0
gicd_write(D_IPRIORITYR(intid / 4), p);
}

static void gic_set_target(uint32_t intid, uint32_t cpuid) {
uint32_t itargetsr = gicd_read(D_ITARGETSR(intid / 4));
itargetsr &= ~((uint32_t)0xff << (intid % 4 * 8));
gicd_write(D_ITARGETSR(intid / 4),
itargetsr | ((uint32_t)(1 << cpuid) << (intid % 4 * 8)));
}

static void gicr_enable_int(uint32_t cpuid, uint32_t intid) {
uint32_t is = gicr_read(cpuid, R_ISENABLER0);
is |= 1 << (intid % 32);
gicr_write(cpuid, R_ISENABLER0, is);
}

static void gicr_clear_pending(uint32_t cpuid, uint32_t intid) {
uint32_t ic = gicr_read(cpuid, R_ICPENDR0);
ic |= 1 << (intid % 32);
gicr_write(cpuid, R_ICPENDR0, ic);
}

static void gicr_set_prio0(uint32_t cpuid, uint32_t intid) {
uint32_t p = gicr_read(cpuid, R_IPRIORITYR(intid / 4));
p &= ~((uint32_t)0xff << (intid % 4 * 8)); // set prio 0
gicr_write(cpuid, R_IPRIORITYR(intid / 4), p);
}

static void gic_setup_ppi(uint32_t cpuid, uint32_t intid) {
gicr_set_prio0(cpuid, intid);
gicr_clear_pending(cpuid, intid);
gicr_enable_int(cpuid, intid);
}

static void gic_setup_spi(uint32_t intid) {
gic_set_prio0(intid);

// all interrupts are handled by cpu0 
gic_set_target(intid, 0);

gic_clear_pending(intid);
gic_enable_int(intid);
}

// irq from iar
int gic_iar_irq(uint32_t iar) { return iar & 0x3ff; }

// interrupt acknowledge register:
// ask GIC what interrupt we should serve.
uint32_t gic_iar() { return cpu_io::ICC_IAR1_EL1::Read(); }

// tell GIC we've served this IRQ.
void gic_eoi(uint32_t iar) { cpu_io::ICC_EOIR1_EL1::Write(iar); }

// check if it's an external interrupt and handle it.
// returns 2 if timer interrupt,
// 1 if other device,
// 0 if not recognized.
int devintr() {
uint32_t iar = gic_iar();
uint32_t irq = gic_iar_irq(iar);
int dev = 0;

if (irq == TIMER0_IRQ) {
// if (cpuid() == 0) {
// clockintr();
// }
// timerintr();
klog::Info("Timer interrupt\n");
dev = 2;
}
if (dev) {
gic_eoi(iar);
cpu_io::DisableInterrupt();
// cpu_io::EnableInterrupt();

cpu_io::CNTV_CTL_EL0::ENABLE::Clear();
cpu_io::CNTV_CTL_EL0::IMASK::Set();
}

return dev;
}

/**
* @brief 中断处理函数
* 由于 aarch64-linux-gnu-g++ 13.2.0 不支持 aarch64 的
Expand Down Expand Up @@ -61,6 +290,7 @@ extern "C" void sync_current_el_spx_handler(uint64_t sp) {

extern "C" void irq_current_el_spx_handler(uint64_t sp) {
klog::Err("IRQ Exception at Current EL with SPx\n");
devintr();
// 处理 IRQ 中断
// ...
}
Expand Down Expand Up @@ -147,6 +377,20 @@ uint32_t InterruptInit(uint32_t, uint8_t *) {

cpu_io::VBAR_EL1::Write(reinterpret_cast<uint64_t>(vector_table));

auto [gic_dist_base, gic_redist_base] =
Singleton<KernelFdt>::GetInstance().GetGIC();

dist_base_addr_ = gic_dist_base;
redist_base_addr_ = gic_redist_base;

Singleton<Gic>::GetInstance() =
std::move(Gic(gic_dist_base, gic_redist_base));

// asm("udf #0");

gicv3init();
gicv3inithart();

cpu_io::EnableInterrupt();

cpu_io::CNTV_CTL_EL0::ENABLE::Clear();
Expand All @@ -159,7 +403,5 @@ uint32_t InterruptInit(uint32_t, uint8_t *) {
cpu_io::CNTV_CTL_EL0::ENABLE::Set();
cpu_io::CNTV_CTL_EL0::IMASK::Clear();

asm("udf #0");

return 0;
}
19 changes: 18 additions & 1 deletion src/kernel/driver/gic/gic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,22 @@
#include "gic.h"

#include "io.hpp"
#include "kernel_log.hpp"

Gic::Gic(uint64_t dev_addr) : base_addr_(dev_addr) {}
Gic::Gic(uint64_t dist_addr, uint64_t redist_addr)
: dist_base_addr_(dist_addr), redist_base_addr_(redist_addr) {
if (dist_base_addr_ == 0 || redist_base_addr_ == 0) {
klog::Err("GIC base address is invalid. gicd [0x%X], gicc [0x%X]\n",
dist_base_addr_, redist_base_addr_);
throw;
}

klog::Info("dist 0x%X, redist 0x%X\n", dist_base_addr_, redist_base_addr_);

// 初始化 GIC Distributor
io::Out<uint32_t>(dist_base_addr_ + kGICD_CTLR, kGICD_CTLR_Enable);

// 初始化 GIC CPU Interface
io::Out<uint32_t>(redist_base_addr_ + kGICC_CTLR, kGICC_CTLR_Enable);
io::Out<uint32_t>(redist_base_addr_ + kGICC_PMR, 0xFF);
}
Loading

0 comments on commit 2a0ca6a

Please sign in to comment.