diff --git a/src/arch/aarch64/device/irqchip/gic.rs b/src/arch/aarch64/device/irqchip/gic.rs new file mode 100644 index 0000000000..d92325203d --- /dev/null +++ b/src/arch/aarch64/device/irqchip/gic.rs @@ -0,0 +1,195 @@ +use core::ptr::{read_volatile, write_volatile}; + +use fdt::DeviceTree; + +use crate::device::io_mmap; +use syscall::{Result, error::{Error, EINVAL}}; + +use super::InterruptController; + +static GICD_CTLR: u32 = 0x000; +static GICD_TYPER: u32 = 0x004; +static GICD_ISENABLER: u32 = 0x100; +static GICD_ICENABLER: u32 = 0x180; +static GICD_IPRIORITY: u32 = 0x400; +static GICD_ITARGETSR: u32 = 0x800; +static GICD_ICFGR: u32 = 0xc00; + +static GICC_EOIR: u32 = 0x0010; +static GICC_IAR: u32 = 0x000c; +static GICC_CTLR: u32 = 0x0000; +static GICC_PMR: u32 = 0x0004; + +pub struct GenericInterruptController { + gic_dist_if: GicDistIf, + gic_cpu_if: GicCpuIf, +} + +impl GenericInterruptController { + pub fn new() -> Self { + let gic_dist_if = GicDistIf { + address: 0, + ncpus: 0, + nirqs: 0, + }; + let gic_cpu_if = GicCpuIf { + address: 0, + }; + + GenericInterruptController { gic_dist_if, gic_cpu_if } + } + pub fn parse(fdt: Option<&DeviceTree>) -> Result<(usize, usize, usize, usize)> { + match fdt { + //TODO: remove hard code for qemu-virt + None => Ok((0x800_0000, 0x1_0000, 0x801_0000, 0x1_0000)), + Some(dtb) => { + //TODO: try to parse dtb using stable library + Err(Error::new(EINVAL)) + } + } + } +} + +impl InterruptController for GenericInterruptController { + fn irq_init(&mut self, fdt: Option<&DeviceTree>) -> Result<()> { + let (dist_addr, dist_size, cpu_addr, cpu_size) = + GenericInterruptController::parse(fdt).unwrap(); + + unsafe { + //TODO: do kernel memory map using node.ranges + + // Map in the Distributor interface + io_mmap(dist_addr, dist_size); + // Map in CPU0's interface + io_mmap(cpu_addr, cpu_size); + + self.gic_cpu_if.init(cpu_addr); + self.gic_dist_if.init(dist_addr); + + // Enable CPU0's GIC interface + self.gic_dist_if.write(GICC_CTLR, 1); + // Set CPU0's Interrupt Priority Mask + self.gic_dist_if.write(GICC_PMR, 0xff); + + } + Ok(()) + } + fn irq_ack(&mut self) -> u32 { + unsafe { self.gic_cpu_if.irq_ack() } + } + fn irq_eoi(&mut self, irq_num: u32) { + unsafe { self.gic_cpu_if.irq_eoi(irq_num) } + } + fn irq_enable(&mut self, irq_num: u32) { + unsafe { self.gic_dist_if.irq_enable(irq_num) } + } + fn irq_disable(&mut self, irq_num: u32) { + unsafe { self.gic_dist_if.irq_disable(irq_num) } + } +} + +pub struct GicDistIf { + pub address: usize, + pub ncpus: u32, + pub nirqs: u32, +} + +impl GicDistIf { + unsafe fn init(&mut self, addr: usize) { + self.address = addr; + + // Disable IRQ Distribution + self.write(GICD_CTLR, 0); + + let typer = self.read(GICD_TYPER); + self.ncpus = ((typer & (0x7 << 5)) >> 5) + 1; + self.nirqs = ((typer & 0x1f) + 1) * 32; + println!("gic: Distributor supports {:?} CPUs and {:?} IRQs", self.ncpus, self.nirqs); + + // Set all SPIs to level triggered + for irq in (32..self.nirqs).step_by(16) { + self.write(GICD_ICFGR + ((irq / 16) * 4), 0); + } + + // Disable all SPIs + for irq in (32..self.nirqs).step_by(32) { + self.write(GICD_ICENABLER + ((irq / 32) * 4), 0xffff_ffff); + } + + // Affine all SPIs to CPU0 and set priorities for all IRQs + for irq in 0..self.nirqs { + if irq > 31 { + let ext_offset = GICD_ITARGETSR + (4 * (irq / 4)); + let int_offset = irq % 4; + let mut val = self.read(ext_offset); + val |= 0b0000_0001 << (8 * int_offset); + self.write(ext_offset, val); + } + + let ext_offset = GICD_IPRIORITY + (4 * (irq / 4)); + let int_offset = irq % 4; + let mut val = self.read(ext_offset); + val |= 0b0000_0000 << (8 * int_offset); + self.write(ext_offset, val); + } + + // Enable IRQ distribution + self.write(GICD_CTLR, 0x1); + } + + unsafe fn irq_enable(&mut self, irq: u32) { + let offset = GICD_ISENABLER + (4 * (irq / 32)); + let shift = 1 << (irq % 32); + let mut val = self.read(offset); + val |= shift; + self.write(offset, val); + } + + unsafe fn irq_disable(&mut self, irq: u32) { + let offset = GICD_ICENABLER + (4 * (irq / 32)); + let shift = 1 << (irq % 32); + let mut val = self.read(offset); + val |= shift; + self.write(offset, val); + } + + unsafe fn read(&self, reg: u32) -> u32 { + let val = read_volatile((self.address + reg as usize) as *const u32); + val + } + + unsafe fn write(&mut self, reg: u32, value: u32) { + write_volatile((self.address + reg as usize) as *mut u32, value); + } +} + +pub struct GicCpuIf { + pub address: usize, +} + +impl GicCpuIf { + fn init(&mut self, addr: usize) { + self.address = addr; + } + + unsafe fn irq_ack(&mut self) -> u32 { + let irq = self.read(GICC_IAR) & 0x1ff; + if irq == 1023 { + panic!("irq_ack: got ID 1023!!!"); + } + irq + } + + unsafe fn irq_eoi(&mut self, irq: u32) { + self.write(GICC_EOIR, irq); + } + + unsafe fn read(&self, reg: u32) -> u32 { + let val = read_volatile((self.address + reg as usize) as *const u32); + val + } + + unsafe fn write(&mut self, reg: u32, value: u32) { + write_volatile((self.address + reg as usize) as *mut u32, value); + } +} diff --git a/src/arch/aarch64/device/irqchip/mod.rs b/src/arch/aarch64/device/irqchip/mod.rs new file mode 100644 index 0000000000..972b3b05df --- /dev/null +++ b/src/arch/aarch64/device/irqchip/mod.rs @@ -0,0 +1,44 @@ +use alloc::{boxed::Box, vec::Vec}; +use syscall::Result; +use fdt::DeviceTree; + +mod gic; + +trait InterruptController { + fn irq_init(&mut self, fdt: Option<&DeviceTree>) -> Result<()>; + fn irq_ack(&mut self) -> u32; + fn irq_eoi(&mut self, irq_num: u32); + fn irq_enable(&mut self, irq_num: u32); + fn irq_disable(&mut self, irq_num: u32); +} + +struct IrqChipCore { + //TODO: support multi level interrupt constrollers + ic: Vec>, + main_ic_idx: usize, +} + +impl IrqChipCore { + pub fn irq_ack(&mut self) -> u32 { + self.ic[self.main_ic_idx].irq_ack() + } + + pub fn irq_eoi(&mut self, irq_num: u32) { + self.ic[self.main_ic_idx].irq_eoi(irq_num) + } + + pub fn irq_enable(&mut self, irq_num: u32) { + self.ic[self.main_ic_idx].irq_enable(irq_num) + } + + pub fn irq_disable(&mut self, irq_num: u32) { + self.ic[self.main_ic_idx].irq_disable(irq_num) + } +} + +static IRQ_CHIP = IrqChipCore { ic: Vec::new(), main_ic_idx: 0 }; + +pub fn init(fdt: Option<&DeviceTree>) { + let ic = Box::new(gic::GenericInterruptController::new()); + let irq_chip_core = IrqChipCore { ic }; +} diff --git a/src/arch/aarch64/device/mod.rs b/src/arch/aarch64/device/mod.rs index ce1d6c3715..343f3f5496 100644 --- a/src/arch/aarch64/device/mod.rs +++ b/src/arch/aarch64/device/mod.rs @@ -1,5 +1,9 @@ +use crate::memory::Frame; +use crate::paging::{KernelMapper, PhysicalAddress, Page, PageFlags, VirtualAddress}; + pub mod cpu; pub mod gic; +pub mod irqchip; pub mod generic_timer; pub mod serial; pub mod rtc; @@ -21,3 +25,21 @@ pub unsafe fn init_noncore() { pub unsafe fn init_ap() { } + +//map physical addr X to virtual addr PHYS_OFFSET + X +pub unsafe fn io_mmap(addr: usize, io_size: usize) { + let mut mapper = KernelMapper::lock(); + + let start_frame = Frame::containing_address(PhysicalAddress::new(addr)); + let end_frame = Frame::containing_address(PhysicalAddress::new(addr + io_size - 1)); + for frame in Frame::range_inclusive(start_frame, end_frame) { + let page = Page::containing_address(VirtualAddress::new(frame.start_address().data() + crate::PHYS_OFFSET)); + mapper + .get_mut() + .expect("failed to access KernelMapper for mapping GIC distributor") + .map_phys(page.start_address(), frame.start_address(), PageFlags::new().write(true)) + .expect("failed to map GIC distributor") + .flush(); + } + +}