diff --git a/kernel/src/arch/mach_bcm283x/aux.rs b/kernel/src/arch/mach_bcm283x/aux.rs new file mode 100644 index 0000000..0e1d078 --- /dev/null +++ b/kernel/src/arch/mach_bcm283x/aux.rs @@ -0,0 +1,70 @@ +use crate::{ + arch::{mmio_read, mmio_write}, + dev::{irq::InterruptHandler, serial::SerialDevice, Device}, +}; +use address::PhysicalAddress; + +pub struct AuxUart; +pub struct Aux; + +impl Aux { + const REG_AUX_ENABLES: PhysicalAddress = PhysicalAddress::new(0x3F215004); + const AUX_ENABLES_MUART: u32 = 1 << 0; + + pub unsafe fn enable_uart(&self) { + let tmp = mmio_read(Self::REG_AUX_ENABLES); + mmio_write(Self::REG_AUX_ENABLES, tmp | Self::AUX_ENABLES_MUART); + } +} + +impl AuxUart { + const AUX_MU_BASE: PhysicalAddress = PhysicalAddress::new(0x3F215000); + const REG_AUX_MU_IO: PhysicalAddress = Self::AUX_MU_BASE.add(0x40); + const REG_AUX_MU_IER: PhysicalAddress = Self::AUX_MU_BASE.add(0x44); + const REG_AUX_MU_CNTL: PhysicalAddress = Self::AUX_MU_BASE.add(0x60); + + const AUX_MU_CNTL_TE: u32 = 1 << 1; + const AUX_MU_CNTL_RE: u32 = 1 << 0; + + const AUX_MU_IER_RIE: u32 = 1 << 0; +} + +impl InterruptHandler for Aux { + fn do_irq(&self, _irq: u32) {} +} + +impl Device for AuxUart { + fn name(&self) -> &'static str { + "BCM283x Mini-UART" + } + + unsafe fn enable(&self) { + AUX.enable_uart(); + + mmio_write(Self::REG_AUX_MU_IER, Self::AUX_MU_IER_RIE); + mmio_write( + Self::REG_AUX_MU_CNTL, + Self::AUX_MU_CNTL_TE | Self::AUX_MU_CNTL_RE, + ); + } + + unsafe fn disable(&self) {} +} + +impl SerialDevice for AuxUart { + fn send(&self, ch: u8) { + unsafe { + mmio_write(Self::REG_AUX_MU_IO, ch as u32); + } + } +} + +impl InterruptHandler for AuxUart { + fn do_irq(&self, _irq: u32) { + let byte = unsafe { mmio_read(Self::REG_AUX_MU_IO) } as u8; + debugln!("{}", byte as char); + } +} + +pub static AUX: Aux = Aux; +pub static UART: AuxUart = AuxUart; diff --git a/kernel/src/arch/mach_bcm283x/intc.rs b/kernel/src/arch/mach_bcm283x/intc.rs new file mode 100644 index 0000000..ac7d332 --- /dev/null +++ b/kernel/src/arch/mach_bcm283x/intc.rs @@ -0,0 +1,138 @@ +use crate::{ + arch::{cpu, mmio_read, mmio_write}, + dev::{irq::InterruptController, Device}, +}; +use address::PhysicalAddress; + +pub struct Qa7Intc; +pub struct Bcm2837Intc; + +pub struct Intc { + qa7_intc: Qa7Intc, + bcm2837_intc: Bcm2837Intc, +} + +impl Bcm2837Intc { + const REG_PENDING_IRQ1: PhysicalAddress = PhysicalAddress::new(0x3F00B204); + const REG_PENDING_IRQ2: PhysicalAddress = PhysicalAddress::new(0x3F00B208); + + const REG_ENABLE_IRQ1: PhysicalAddress = PhysicalAddress::new(0x3F00B210); + const REG_ENABLE_IRQ2: PhysicalAddress = PhysicalAddress::new(0x3F00B214); +} + +impl Qa7Intc { + const REG_TIMER_INTC: PhysicalAddress = PhysicalAddress::new(0x40000040); + const REG_INT_SRC: PhysicalAddress = PhysicalAddress::new(0x40000060); + + const INTC_CNTPNSIRQ_IRQ: u32 = 1 << 1; +} + +impl Device for Intc { + fn name(&self) -> &'static str { + "BCM283x Interrupt Controller" + } + + unsafe fn enable(&self) {} + unsafe fn disable(&self) {} +} + +impl Device for Qa7Intc { + fn name(&self) -> &'static str { + "Broadcom QA7 Interrupt Controller" + } + + unsafe fn enable(&self) {} + unsafe fn disable(&self) {} +} + +impl Device for Bcm2837Intc { + fn name(&self) -> &'static str { + "BCM2837 Interrupt Controller" + } + + unsafe fn enable(&self) {} + unsafe fn disable(&self) {} +} + +impl InterruptController for Qa7Intc { + unsafe fn enable_irq(&self, irq: u32) { + match irq { + super::IRQ_LOCAL_TIMER => { + let phys_core_id = cpu::get_phys_id(); + let tmp = mmio_read(Self::REG_TIMER_INTC + 4 * phys_core_id); + mmio_write( + Self::REG_TIMER_INTC + 4 * phys_core_id, + tmp | Self::INTC_CNTPNSIRQ_IRQ, + ); + } + _ => panic!("Unhandled IRQ number: {}", irq), + } + } + + unsafe fn disable_irq(&self, _irq: u32) { + todo!(); + } + + fn is_irq_pending(&self, irq: u32) -> bool { + unsafe { mmio_read(Self::REG_INT_SRC) & (1 << irq) != 0 } + } + + unsafe fn clear_irq(&self, _irq: u32) {} +} + +impl InterruptController for Bcm2837Intc { + unsafe fn enable_irq(&self, irq: u32) { + if irq < 32 { + mmio_write(Self::REG_ENABLE_IRQ1, 1 << irq); + } else if irq < 64 { + mmio_write(Self::REG_ENABLE_IRQ2, 1 << (irq - 32)); + } + } + + unsafe fn disable_irq(&self, _irq: u32) { + todo!(); + } + + fn is_irq_pending(&self, irq: u32) -> bool { + if irq < 32 { + unsafe { mmio_read(Self::REG_PENDING_IRQ1) & (1 << irq) != 0 } + } else if irq < 64 { + unsafe { mmio_read(Self::REG_PENDING_IRQ2) & (1 << (irq - 32)) != 0 } + } else { + false + } + } + + unsafe fn clear_irq(&self, _irq: u32) { + todo!(); + } +} + +impl InterruptController for Intc { + unsafe fn enable_irq(&self, irq: u32) { + if irq < 16 { + self.qa7_intc.enable_irq(irq); + } else { + self.bcm2837_intc.enable_irq(irq - 16); + } + } + + unsafe fn disable_irq(&self, _irq: u32) { + todo!(); + } + + fn is_irq_pending(&self, irq: u32) -> bool { + if irq < 16 { + self.qa7_intc.is_irq_pending(irq) + } else { + self.bcm2837_intc.is_irq_pending(irq - 16) + } + } + + unsafe fn clear_irq(&self, _irq: u32) {} +} + +pub static INTC: Intc = Intc { + qa7_intc: Qa7Intc, + bcm2837_intc: Bcm2837Intc, +}; diff --git a/kernel/src/arch/mach_bcm283x/mod.rs b/kernel/src/arch/mach_bcm283x/mod.rs index 7465979..07324f4 100644 --- a/kernel/src/arch/mach_bcm283x/mod.rs +++ b/kernel/src/arch/mach_bcm283x/mod.rs @@ -1,52 +1,23 @@ +use crate::mem::phys::{init_from_iter, SimpleMemoryIterator, UsableMemory}; +use address::PhysicalAddress; + +pub mod aux; +pub mod intc; pub mod mbox; pub mod smp; pub mod timer; -use crate::{ - arch::{cpu, mmio_read, mmio_write}, - dev::irq::InterruptController, - mem::phys::{init_from_iter, SimpleMemoryIterator, UsableMemory}, -}; -use address::PhysicalAddress; - -pub struct Intc; - -impl Intc { - const REG_TIMER_INTC: PhysicalAddress = PhysicalAddress::new(0x40000040); - const REG_INT_SRC: PhysicalAddress = PhysicalAddress::new(0x40000060); - - const INTC_CNTPNSIRQ_IRQ: u32 = 1 << 1; -} - -impl InterruptController for Intc { - unsafe fn init(&self) {} - - unsafe fn enable_irq(&self, irq: u32) { - match irq { - IRQ_LOCAL_TIMER => { - let phys_core_id = cpu::get_phys_id(); - let tmp = mmio_read(Self::REG_TIMER_INTC + 4 * phys_core_id); - mmio_write(Self::REG_TIMER_INTC + 4 * phys_core_id, tmp | Self::INTC_CNTPNSIRQ_IRQ); - } - _ => panic!("Unhandled IRQ number: {}", irq), - } - } - - unsafe fn disable_irq(&self, _irq: u32) { - todo!(); - } - - fn is_irq_pending(&self, irq: u32) -> bool { - unsafe { mmio_read(Self::REG_INT_SRC) & (1 << irq) != 0 } - } - - unsafe fn clear_irq(&self, _irq: u32) { - } -} - -pub static INTC: Intc = Intc; - pub const IRQ_LOCAL_TIMER: u32 = 1; +pub const IRQ_AUX: u32 = 16 + 29; + +// TODO as long as AUX is not used for anything else? +pub const IRQ_UART: u32 = IRQ_AUX; + +pub use aux::UART as AUX_UART; +pub use intc::INTC; +// Configured as primary UART +pub use AUX_UART as UART; + // pub const INT_SRC_TIMER: u32 = 1 << 11; // pub const INT_SRC_MBOX0: u32 = 1 << 4; @@ -61,3 +32,5 @@ pub fn init_phys_memory() { init_from_iter(iter); } } + +pub fn init() {} diff --git a/kernel/src/arch/mach_virt/mod.rs b/kernel/src/arch/mach_virt/mod.rs index 778f17c..fcf2756 100644 --- a/kernel/src/arch/mach_virt/mod.rs +++ b/kernel/src/arch/mach_virt/mod.rs @@ -1,8 +1,21 @@ +use crate::arch; + pub mod timer; use address::PhysicalAddress; pub const IRQ_LOCAL_TIMER: u32 = 30; +pub const IRQ_UART: u32 = 32 + 1; +pub const IRQ_RTC: u32 = 32 + 2; pub const GICD_BASE: PhysicalAddress = PhysicalAddress::new(0x08000000usize); pub const GICC_BASE: PhysicalAddress = PhysicalAddress::new(0x08010000usize); + +pub const PL031_BASE: PhysicalAddress = PhysicalAddress::new(0x09010000usize); +pub const PL011_BASE: PhysicalAddress = PhysicalAddress::new(0x09000000usize); + +pub fn init() { + unsafe { + arch::timer::enable_rtc(); + } +} diff --git a/kernel/src/arch/timer.rs b/kernel/src/arch/timer.rs index 3710a91..1c12321 100644 --- a/kernel/src/arch/timer.rs +++ b/kernel/src/arch/timer.rs @@ -1,24 +1,61 @@ use crate::{ arch::{intrin, machine}, - dev::irq::{self, InterruptController, InterruptHandler}, + dev::{ + irq::{self, InterruptController, InterruptHandler}, + Device, + }, }; struct ArmTimer; +impl Device for ArmTimer { + fn name(&self) -> &'static str { + "ARM Generic Timer" + } + + unsafe fn enable(&self) { + intrin::write_cntp_ctl_el0(1); + } + + unsafe fn disable(&self) { + intrin::write_cntp_ctl_el0(0); + } +} + impl InterruptHandler for ArmTimer { fn do_irq(&self, _irq: u32) { + debugln!("T"); unsafe { - intrin::write_cntp_tval_el0(100000); + intrin::write_cntp_tval_el0(10000000); } } } pub unsafe fn enable_local_timer() { + LOCAL_TIMER.enable(); + let intc = irq::get_intc(); irq::set_irq_handler(machine::IRQ_LOCAL_TIMER, &LOCAL_TIMER); intc.enable_irq(machine::IRQ_LOCAL_TIMER); +} - intrin::write_cntp_ctl_el0(1); +// TODO bcm283x RTC? +cfg_if! { + if #[cfg(feature = "mach_virt")] { + use crate::dev::pl031::Pl031; + + static PL031: Pl031 = Pl031::new(machine::PL031_BASE); + + use PL031 as RTC; + + pub unsafe fn enable_rtc() { + RTC.enable(); + + let intc = irq::get_intc(); + irq::set_irq_handler(machine::IRQ_RTC, &RTC); + intc.enable_irq(machine::IRQ_RTC); + } + } } static LOCAL_TIMER: ArmTimer = ArmTimer; diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs index d6f5ea3..4180f9b 100644 --- a/kernel/src/debug.rs +++ b/kernel/src/debug.rs @@ -1,5 +1,4 @@ -use crate::arch::mmio_write; -use address::PhysicalAddress; +use crate::dev::serial::{SerialDevice, SERIAL0}; use core::fmt; use spin::Mutex; @@ -7,15 +6,7 @@ struct Debug; impl Debug { fn putc(&mut self, ch: u8) { - unsafe { - cfg_if! { - if #[cfg(feature = "mach_rpi3b")] { - mmio_write(PhysicalAddress::new(0x3F215040), ch as u32); - } else if #[cfg(feature = "mach_virt")] { - mmio_write(PhysicalAddress::new(0x09000000), ch as u32); - } - } - } + SERIAL0.send(ch); } } diff --git a/kernel/src/dev/irq.rs b/kernel/src/dev/irq.rs index 907682a..baecd9e 100644 --- a/kernel/src/dev/irq.rs +++ b/kernel/src/dev/irq.rs @@ -1,3 +1,4 @@ +use crate::dev::Device; use alloc::collections::LinkedList; #[repr(C)] @@ -28,9 +29,7 @@ pub struct IrqRegisters { far: usize, } -pub trait InterruptController { - unsafe fn init(&self); - +pub trait InterruptController: Device { unsafe fn enable_irq(&self, irq: u32); unsafe fn disable_irq(&self, irq: u32); @@ -91,5 +90,5 @@ cfg_if! { } pub unsafe fn init() { - get_intc().init(); + get_intc().enable(); } diff --git a/kernel/src/dev/irq/gic.rs b/kernel/src/dev/irq/gic.rs index fbe68b7..a6cd749 100644 --- a/kernel/src/dev/irq/gic.rs +++ b/kernel/src/dev/irq/gic.rs @@ -1,6 +1,6 @@ use crate::{ arch::{mmio_read, mmio_write}, - dev::irq::InterruptController, + dev::{irq::InterruptController, Device}, }; use address::PhysicalAddress; @@ -9,13 +9,23 @@ pub struct Gic { gicc_base: PhysicalAddress, } -impl InterruptController for Gic { - unsafe fn init(&self) { +impl Device for Gic { + fn name(&self) -> &'static str { + "ARM Generic Interrupt Controller" + } + + unsafe fn enable(&self) { mmio_write(self.gicd_base + Self::GICD_CTLR, Self::GICD_CTLR_ENABLE); mmio_write(self.gicc_base + Self::GICC_CTLR, Self::GICC_CTLR_ENABLE); mmio_write(self.gicc_base + Self::GICC_PMR, 0xFF); } + unsafe fn disable(&self) { + todo!() + } +} + +impl InterruptController for Gic { unsafe fn enable_irq(&self, irq: u32) { self.set_irq_config(irq, 1); self.unmask_irq(irq); diff --git a/kernel/src/dev/mod.rs b/kernel/src/dev/mod.rs index aac1186..ee3358f 100644 --- a/kernel/src/dev/mod.rs +++ b/kernel/src/dev/mod.rs @@ -1 +1,11 @@ pub mod irq; +pub mod serial; + +pub mod pl011; +pub mod pl031; + +pub trait Device { + fn name(&self) -> &'static str; + unsafe fn enable(&self); + unsafe fn disable(&self); +} diff --git a/kernel/src/dev/pl011.rs b/kernel/src/dev/pl011.rs new file mode 100644 index 0000000..44d4b41 --- /dev/null +++ b/kernel/src/dev/pl011.rs @@ -0,0 +1,78 @@ +use crate::{ + arch::{mmio_read, mmio_write}, + dev::{irq::InterruptHandler, serial::SerialDevice, Device}, +}; +use address::PhysicalAddress; + +pub struct Pl011 { + base: PhysicalAddress, +} + +impl InterruptHandler for Pl011 { + fn do_irq(&self, _irq: u32) { + let tmp = unsafe { mmio_read(self.base + Self::UARTRIS) }; + if tmp & Self::UARTRIS_RXRIS != 0 { + let ch = unsafe { mmio_read(self.base + Self::UARTDR) } as u8; + debugln!("{}", ch as char); + unsafe { + mmio_write(self.base + Self::UARTICR, Self::UARTICR_RXIC); + } + } + } +} + +impl Device for Pl011 { + fn name(&self) -> &'static str { + "PL011 UART" + } + + unsafe fn enable(&self) { + mmio_write(self.base + Self::UARTCR, 0); + mmio_write(self.base + Self::UARTCLR_H, 3 << 5); + mmio_write( + self.base + Self::UARTIMSC, + Self::UARTIMSC_RXIM, + ); + mmio_write( + self.base + Self::UARTCR, + Self::UARTCR_TXE | Self::UARTCR_RXE | Self::UARTCR_UARTEN, + ); + } + + unsafe fn disable(&self) {} +} + +impl SerialDevice for Pl011 { + fn send(&self, ch: u8) { + unsafe { + while mmio_read(self.base + Self::UARTFR) & Self::UARTFR_BUSY != 0 {} + mmio_write(self.base + Self::UARTDR, ch as u32); + } + } +} + +impl Pl011 { + const UARTDR: usize = 0x00; + const UARTFR: usize = 0x18; + const UARTCLR_H: usize = 0x2C; + const UARTCR: usize = 0x30; + const UARTIMSC: usize = 0x38; + const UARTRIS: usize = 0x3C; + const UARTICR: usize = 0x44; + + const UARTCR_UARTEN: u32 = 1 << 0; + const UARTCR_TXE: u32 = 1 << 8; + const UARTCR_RXE: u32 = 1 << 9; + + const UARTFR_BUSY: u32 = 1 << 3; + + const UARTIMSC_RXIM: u32 = 1 << 4; + + const UARTRIS_RXRIS: u32 = 1 << 4; + + const UARTICR_RXIC: u32 = 1 << 4; + + pub const fn new(base: PhysicalAddress) -> Self { + Self { base } + } +} diff --git a/kernel/src/dev/pl031.rs b/kernel/src/dev/pl031.rs new file mode 100644 index 0000000..c144f37 --- /dev/null +++ b/kernel/src/dev/pl031.rs @@ -0,0 +1,47 @@ +use crate::{ + arch::{mmio_read, mmio_write}, + dev::{irq::InterruptHandler, Device}, +}; +use address::PhysicalAddress; + +pub struct Pl031 { + base: PhysicalAddress, +} + +impl Pl031 { + const RTCDR: usize = 0x00; + const RTCMR: usize = 0x04; + const RTCCR: usize = 0x0C; + const RTCIMSC: usize = 0x10; + const RTCICR: usize = 0x1C; + + pub const fn new(base: PhysicalAddress) -> Self { + Self { base } + } +} + +impl Device for Pl031 { + fn name(&self) -> &'static str { + "ARM PL031 RTC" + } + + unsafe fn enable(&self) { + let tmp = mmio_read(self.base + Self::RTCDR); + mmio_write(self.base + Self::RTCMR, tmp + 1); + + mmio_write(self.base + Self::RTCIMSC, 1); + mmio_write(self.base + Self::RTCCR, 1); + } + + unsafe fn disable(&self) {} +} + +impl InterruptHandler for Pl031 { + fn do_irq(&self, _irq: u32) { + let time_int = unsafe { mmio_read(self.base + Self::RTCDR) }; + unsafe { + mmio_write(self.base + Self::RTCICR, 1); + mmio_write(self.base + Self::RTCMR, time_int + 1); + } + } +} diff --git a/kernel/src/dev/serial.rs b/kernel/src/dev/serial.rs new file mode 100644 index 0000000..f9ba21d --- /dev/null +++ b/kernel/src/dev/serial.rs @@ -0,0 +1,23 @@ +use crate::dev::Device; + +pub trait SerialDevice: Device { + fn send(&self, ch: u8); +} + +cfg_if! { + if #[cfg(feature = "mach_rpi3b")] { + use crate::arch::mach_bcm283x; + + pub use mach_bcm283x::UART as SERIAL0; + } else { + use crate::{dev::pl011::Pl011, arch::machine}; + + pub static SERIAL0: Pl011 = Pl011::new(machine::PL011_BASE); + } +} + +pub fn init() { + unsafe { + SERIAL0.enable(); + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index b2c4bbf..a20d6bd 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -21,12 +21,13 @@ pub mod dev; #[cfg(feature = "fdt-rs")] pub mod fdt; pub mod mem; +pub mod time; pub use mem::KernelSpace; use address::PhysicalAddress; -use arch::{cpu, intrin, smp, timer}; -use dev::irq; +use arch::{timer, cpu, intrin, smp}; +use dev::irq::{self, InterruptController}; pub fn entry_common() -> ! { smp::init_ipi_delivery(); @@ -43,6 +44,7 @@ pub fn entry_common() -> ! { #[no_mangle] extern "C" fn kernel_bsp_main(fdt_base: PhysicalAddress) -> ! { cpu::init(0); + dev::serial::init(); cfg_if! { if #[cfg(feature = "fdt-rs")] { @@ -55,6 +57,14 @@ extern "C" fn kernel_bsp_main(fdt_base: PhysicalAddress) -> ! { } } mem::heap::init(); + arch::machine::init(); + + // Enable IRQs for SERIAL0 + let intc = irq::get_intc(); + irq::set_irq_handler(arch::machine::IRQ_UART, &dev::serial::SERIAL0); + unsafe { + intc.enable_irq(arch::machine::IRQ_UART); + } debug!("BSP init finished\n"); //smp::wakeup_ap_cpus(); diff --git a/kernel/src/time.rs b/kernel/src/time.rs new file mode 100644 index 0000000..cbad19b --- /dev/null +++ b/kernel/src/time.rs @@ -0,0 +1,9 @@ +#[derive(Debug)] +pub struct Time { + pub year: u32, + pub mday: u8, + pub mon: u8, + pub hour: u8, + pub min: u8, + pub sec: u8 +} diff --git a/qemu.sh b/qemu.sh index 4a8a659..1985c79 100755 --- a/qemu.sh +++ b/qemu.sh @@ -6,10 +6,15 @@ if [ -z "${MACH}" ]; then MACH=rpi3b fi +if [ -z "$QEMU_BIN" ]; then + QEMU_BIN=qemu-system-aarch64 +fi + ARCH=aarch64-unknown-none-${MACH} KERNEL=target/${ARCH}/debug/kernel -QEMU_OPTS="" +QEMU_OPTS="-chardev stdio,nowait,id=char0,mux=on \ + -mon chardev=char0" if [ "$QEMU_DINT" = 1 ]; then QEMU_OPTS="$QEMU_OPTS -d int" @@ -19,22 +24,23 @@ case ${MACH} in rpi3b) QEMU_OPTS="$QEMU_OPTS \ -serial null \ - -serial stdio \ + -serial chardev:char0 \ -dtb bcm2837-rpi-3-b.dtb \ -M raspi3b" ;; virt) KERNEL=target/${ARCH}/debug/kernel.bin QEMU_OPTS="$QEMU_OPTS \ - -serial stdio \ + -serial chardev:char0 \ -M virt,virtualization=on \ -cpu cortex-a57 \ -m 256" esac QEMU_OPTS="$QEMU_OPTS \ -kernel ${KERNEL} \ + -display none \ -s" ./build.sh -qemu-system-aarch64 ${QEMU_OPTS} +${QEMU_BIN} ${QEMU_OPTS}