diff --git a/Makefile b/Makefile index 5f9ced7..6bb660f 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,14 @@ QEMU_OPTS+=-kernel $(O)/kernel.bin \ -display none \ -net none endif +ifeq ($(MACH),rpi3) +QEMU_OPTS+=-kernel $(O)/kernel.bin \ + -initrd $(O)/initrd.img \ + -M raspi3b \ + -serial chardev:serial1 \ + -display none \ + -net none +endif endif ifeq ($(QEMU_DINT),1) diff --git a/etc/aarch64-rpi3.json b/etc/aarch64-rpi3.json new file mode 100644 index 0000000..53fccde --- /dev/null +++ b/etc/aarch64-rpi3.json @@ -0,0 +1,17 @@ +{ + "arch": "aarch64", + "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + "disable-redzone": true, + "executables": true, + "features": "+strict-align,+neon,+fp-armv8", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "llvm-target": "aarch64-unknown-none", + "max-atomic-width": 128, + "panic-strategy": "abort", + "relocation-model": "static", + "target-pointer-width": "64", + "pre-link-args": { + "ld.lld": [ "-Tetc/aarch64-rpi3.ld" ] + } +} diff --git a/etc/aarch64-rpi3.ld b/etc/aarch64-rpi3.ld new file mode 100644 index 0000000..ceb4907 --- /dev/null +++ b/etc/aarch64-rpi3.ld @@ -0,0 +1,45 @@ +ENTRY(_entry); + +KERNEL_OFFSET = 0xFFFFFF8000000000; +BASE_OFFSET = 0x80000; + +SECTIONS { + . = BASE_OFFSET; + + .text.lower : { + *(.text._entry) + } + + . = ALIGN(16); + . = . + KERNEL_OFFSET; + + PROVIDE(__kernel_start = .); + + .text : AT(. - KERNEL_OFFSET) { + *(.text._entry_upper) + *(.text*) + } + + . = ALIGN(4K); + .rodata : AT(. - KERNEL_OFFSET) { + *(.rodata*) + } + + . = ALIGN(4K); + .data : AT(. - KERNEL_OFFSET) { + *(.data*) + } + + . = ALIGN(4K); + PROVIDE(__bss_start_phys = . - KERNEL_OFFSET); + PROVIDE(__bss_start = .); + .bss : AT(. - KERNEL_OFFSET) { + *(COMMON) + *(.bss*) + . = ALIGN(4K); + } + PROVIDE(__bss_end_phys = . - KERNEL_OFFSET); + + PROVIDE(__kernel_end = .); + PROVIDE(__kernel_end_phys = . - KERNEL_OFFSET); +} diff --git a/fs/vfs/src/block.rs b/fs/vfs/src/block.rs new file mode 100644 index 0000000..3c142ef --- /dev/null +++ b/fs/vfs/src/block.rs @@ -0,0 +1,7 @@ +use error::Errno; + +pub trait BlockDevice { + fn read(&self, pos: usize, buf: &mut [u8]) -> Result<(), Errno>; + fn write(&self, pos: usize, buf: &[u8]) -> Result<(), Errno>; + // TODO ioctl and stuff +} diff --git a/fs/vfs/src/lib.rs b/fs/vfs/src/lib.rs index 2d01a4e..02b3ebd 100644 --- a/fs/vfs/src/lib.rs +++ b/fs/vfs/src/lib.rs @@ -3,6 +3,8 @@ extern crate alloc; +pub mod block; +pub use block::BlockDevice; pub mod fs; pub use fs::Filesystem; pub mod stat; diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 10d95f5..2d0956e 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -28,4 +28,4 @@ pl031 = [] mach_qemu = ["pl011", "pl031"] mach_orangepi3 = [] -mach_rpi3 = [] +mach_rpi3 = ["pl011"] diff --git a/kernel/src/arch/aarch64/mach_rpi3/emmc.rs b/kernel/src/arch/aarch64/mach_rpi3/emmc.rs new file mode 100644 index 0000000..88337ab --- /dev/null +++ b/kernel/src/arch/aarch64/mach_rpi3/emmc.rs @@ -0,0 +1,384 @@ +use crate::arch::machine::{self, Bcm283xMailbox}; +use crate::dev::sd::{ + SdCardIdentification, SdCardStatus, SdCommand, SdCommandNumber, SdCommandTransfer, + SdHostController, SdResponse, SdResponseType, +}; +use crate::dev::Device; +use crate::mem::virt::DeviceMemoryIo; +use crate::sync::IrqSafeSpinLock; +use crate::util::InitOnce; +use error::Errno; +use vfs::BlockDevice; + +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite}; + +register_structs! { + #[allow(non_snake_case)] + Regs { + (0x00 => ARG2: ReadWrite), + (0x04 => BLKSIZECNT: ReadWrite), + (0x08 => ARG1: ReadWrite), + (0x0C => CMDTM: ReadWrite), + (0x10 => RESP0: ReadOnly), + (0x14 => RESP1: ReadOnly), + (0x18 => RESP2: ReadOnly), + (0x1C => RESP3: ReadOnly), + (0x20 => DATA: ReadWrite), + (0x24 => STATUS: ReadOnly), + (0x28 => CONTROL0: ReadWrite), + (0x2C => CONTROL1: ReadWrite), + (0x30 => INTERRUPT: ReadWrite), + (0x34 => IRPT_MASK: ReadWrite), + (0x38 => IRPT_EN: ReadWrite), + (0x3C => CONTROL2: ReadWrite), + (0x40 => _res0), + (0x50 => FORCE_IRPT: ReadWrite), + (0x54 => _res1), + (0x70 => BOOT_TIMEOUT: ReadWrite), + (0x74 => DBG_SEL: ReadWrite), + (0x78 => @END), + } +} + +struct MmcInner { + regs: DeviceMemoryIo, + status: SdCardStatus, +} + +pub struct MassMediaController { + inner: InitOnce>, + base: usize, +} + +fn clock_divider(f_base: u32, f_target: u32) -> Result { + let mut target_div; + let mut div = 0; + if f_target <= f_base { + target_div = f_base / f_target; + if f_base % f_target != 0 { + target_div -= 1; + } + } else { + target_div = 1; + } + for first_bit in (0..31).rev() { + if target_div & (1 << first_bit) != 0 { + div = first_bit; + target_div &= !(1 << first_bit); + if target_div != 0 { + div += 1; + } + break; + } + } + + if div == u32::MAX { + div = 31; + } + if div >= 32 { + div = 31; + } + if div != 0 { + div = 1 << (div - 1) + } + if div >= 0x400 { + div = 0x3FF; + } + + let f_sel = div & 0xFF; + let upper_bits = (div >> 8) & 0x3; + + Ok((f_sel << 8) | (upper_bits << 6)) +} + +impl MmcInner { + fn power_on(&mut self) -> Result<(), Errno> { + machine::BCM_MBOX.set_power_state( + Bcm283xMailbox::POWER_SD_CARD, + Bcm283xMailbox::POWER_STATE_ON | Bcm283xMailbox::POWER_STATE_WAIT, + ) + } + + fn base_clock(&mut self) -> Result { + machine::BCM_MBOX.clock_rate(Bcm283xMailbox::CLOCK_EMMC) + } + + fn send_cmd_inner(&mut self, cmd: &mut SdCommand) -> Result { + debugln!("send_cmd {:?}", cmd.number); + + while self.regs.STATUS.get() & 1 != 0 { + cortex_a::asm::nop(); + } + + let response_type = cmd.response_type(); + if response_type.is_busy() { + // TODO check if this is an ABORT command + + while self.regs.STATUS.get() & 2 != 0 { + cortex_a::asm::nop(); + } + } + + let (block_count, block_size, io_flags) = match &cmd.transfer { + SdCommandTransfer::Write(buf, blk_size) => { + let sz = *blk_size as usize; + assert!(buf.len() % sz == 0); + ((buf.len() / sz) as u32, *blk_size, (1 << 21)) + } + SdCommandTransfer::Read(buf, blk_size) => { + let sz = *blk_size as usize; + assert!(buf.len() % sz == 0); + ((buf.len() / sz) as u32, *blk_size, (1 << 21) | (1 << 4)) + } + SdCommandTransfer::None => (0, 512, 0), + }; + + let (size_136, size_flags) = match response_type { + SdResponseType::R2 | SdResponseType::R4 => (true, 1 << 16), + SdResponseType::R1b | SdResponseType::R5b => (false, 3 << 16), + SdResponseType::None => (false, 0), + _ => (false, 2 << 16), + }; + + debugln!("size_flags: {:#x}, io_flags: {:#x}", size_flags, io_flags); + self.regs + .BLKSIZECNT + .set((block_size as u32) | (block_count << 16)); + self.regs.ARG1.set(cmd.argument); + self.regs + .CMDTM + .set((cmd.number() << 24) | size_flags | io_flags); + + while self.regs.INTERRUPT.get() & 0x8001 == 0 { + cortex_a::asm::nop(); + } + + let irq_status = self.regs.INTERRUPT.get(); + self.regs.INTERRUPT.set(0xFFFF0001); + + if irq_status & 0xFFFF0001 != 1 { + warnln!("SD error: irq_status={:#x}", irq_status); + return Err(Errno::InvalidArgument); + } + + let response = if size_136 { + SdResponse::Four([ + self.regs.RESP0.get(), + self.regs.RESP1.get(), + self.regs.RESP2.get(), + self.regs.RESP3.get(), + ]) + } else { + SdResponse::One(self.regs.RESP0.get()) + }; + + match &mut cmd.transfer { + SdCommandTransfer::Write(_, _) => { + todo!() + } + SdCommandTransfer::Read(buf, _) => { + debugln!("Reading {} data blocks", block_count); + for i in 0..block_count { + while self.regs.INTERRUPT.get() & (0x8000 | (1 << 5)) == 0 { + cortex_a::asm::nop(); + } + let irq_status = self.regs.INTERRUPT.get(); + self.regs.INTERRUPT.set(0xFFFF0000 | (1 << 5)); + + if irq_status & (0xFFFF0000 | (1 << 5)) != (1 << 5) { + todo!(); + } + + assert!(block_size % 4 == 0); + for j in (0..block_size).step_by(4) { + let word = self.regs.DATA.get(); + let base = (i * block_size as u32) as usize + j as usize; + buf[base + 0] = (word & 0xFF) as u8; + buf[base + 1] = ((word >> 8) & 0xFF) as u8; + buf[base + 2] = ((word >> 16) & 0xFF) as u8; + buf[base + 3] = (word >> 24) as u8; + } + } + } + SdCommandTransfer::None => {} + } + + Ok(response) + } + + fn phys_reset(&mut self) -> Result<(), Errno> { + self.status.phys_inserted = false; + + let mut tmp = self.regs.CONTROL1.get(); + tmp |= 1 << 24; + // Disable clock + tmp &= !(1 << 2); + tmp &= !(1 << 0); + self.regs.CONTROL1.set(tmp); + + debugln!("Checking for a card"); + let mut retry = 5; + let mut status = 0; + + while retry > 0 { + status = self.regs.STATUS.get(); + if status & 1 << 16 != 0 { + break; + } + + for _ in 0..100000 { + cortex_a::asm::nop(); + } + + retry -= 1; + } + + if retry == 0 { + warnln!("No card inserted"); + return Ok(()); + } + + self.status.phys_inserted = true; + debugln!("A card is present: status={:#x}", status); + + self.regs.CONTROL2.set(0); + + let mut f_base = self.base_clock()?; + if f_base == 0 { + f_base = 100000000; + } + + let div = clock_divider(f_base, 400000)?; + + debugln!("Switching to ID frequency"); + + tmp = self.regs.CONTROL1.get(); + tmp |= div; + tmp &= !(0xF << 16); + tmp |= 11 << 16; + tmp |= 4; + tmp |= 1; + self.regs.CONTROL1.set(tmp); + + while self.regs.CONTROL1.get() & 1 << 1 == 0 { + cortex_a::asm::nop(); + } + + self.regs.IRPT_EN.set(0); + self.regs.INTERRUPT.set(u32::MAX); + self.regs.IRPT_MASK.set(u32::MAX); + + Ok(()) + } + + fn send_cmd(&mut self, cmd: &mut SdCommand) -> Result { + if cmd.is_acmd() { + let arg = if let Some(rca) = self.status.address { + (rca as u32) << 16 + } else { + 0 + }; + self.send_cmd_inner(&mut SdCommand { + number: SdCommandNumber::Cmd55, + argument: arg, + transfer: SdCommandTransfer::None, + })?; + } + self.send_cmd_inner(cmd) + } +} + +impl BlockDevice for MassMediaController { + fn read(&self, pos: usize, data: &mut [u8]) -> Result<(), Errno> { + // TODO check card status + if data.len() % 512 != 0 || pos % 512 != 0 { + todo!() + } + + for i in 0..(data.len() / 512) { + let s = i * 512; + self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd17, + argument: (pos / 512 + i) as u32, + transfer: SdCommandTransfer::Read(&mut data[s..(s + 512)], 512), + })?; + } + Ok(()) + } + + fn write(&self, _pos: usize, _data: &[u8]) -> Result<(), Errno> { + todo!() + } +} + +impl SdHostController for MassMediaController { + fn send_cmd(&self, cmd: &mut SdCommand) -> Result { + self.inner.get().lock().send_cmd(cmd) + } + + fn phys_reset(&self) -> Result<(), Errno> { + let mut inner = self.inner.get().lock(); + inner.status.address = None; + inner.status.id = None; + inner.phys_reset() + } + + fn is_phys_inserted(&self) -> bool { + self.inner.get().lock().status.phys_inserted + } + + fn set_transfer_block_size(&self, blk: usize) -> Result<(), Errno> { + // TODO other block sizes? + assert_eq!(blk, 512); + let inner = self.inner.get().lock(); + let mut tmp = inner.regs.BLKSIZECNT.get(); + tmp &= !0xFFF; + tmp |= 0x200; + inner.regs.BLKSIZECNT.set(tmp); + Ok(()) + } + + fn set_card_address(&self, rca: u16) -> Result<(), Errno> { + self.inner.get().lock().status.address = Some(rca); + Ok(()) + } + + fn set_card_identification(&self, id: SdCardIdentification) -> Result<(), Errno> { + self.inner.get().lock().status.id = Some(id); + Ok(()) + } + + fn reset_card_identification(&self) -> Result<(), Errno> { + self.inner.get().lock().status.id = None; + Ok(()) + } +} + +impl Device for MassMediaController { + fn name(&self) -> &'static str { + "BCM283x External Mass Media Controller" + } + + unsafe fn enable(&self) -> Result<(), Errno> { + let mut inner = MmcInner { + regs: DeviceMemoryIo::map(self.name(), self.base, 1)?, + status: SdCardStatus::invalid(), + }; + inner.power_on()?; + self.inner.init(IrqSafeSpinLock::new(inner)); + + self.reset_card()?; + Ok(()) + } +} + +impl MassMediaController { + pub const unsafe fn new(base: usize) -> Self { + Self { + inner: InitOnce::new(), + base, + } + } +} diff --git a/kernel/src/arch/aarch64/mach_rpi3/irqchip.rs b/kernel/src/arch/aarch64/mach_rpi3/irqchip.rs new file mode 100644 index 0000000..0e77072 --- /dev/null +++ b/kernel/src/arch/aarch64/mach_rpi3/irqchip.rs @@ -0,0 +1,241 @@ +use crate::dev::{ + irq::{IntController, IntSource, IrqContext}, + Device, +}; +use crate::mem::virt::DeviceMemoryIo; +use crate::sync::IrqSafeSpinLock; +use crate::util::InitOnce; +use core::fmt; +use cortex_a::registers::MPIDR_EL1; +use error::Errno; + +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite}; + +register_structs! { + #[allow(non_snake_case)] + pub(super) BcmRegs { + (0x000 => _res0), + (0x200 => PENDING_BASIC: ReadOnly), + (0x204 => PENDING1: ReadOnly), + (0x208 => PENDING2: ReadOnly), + (0x20C => _res1), + (0x210 => ENABLE1: ReadWrite), + (0x214 => ENABLE2: ReadWrite), + (0x218 => ENABLE_BASIC: ReadWrite), + (0x21C => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + pub(super) Qa7Regs { + (0x000 => _res0), + (0x040 => CORE_IRQ_EN: [ReadWrite; 4]), + (0x050 => _res1), + (0x060 => CORE_IRQ_SRC: [ReadWrite; 4]), + (0x070 => @END), + } +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct IrqNumber(u32); + +impl IrqNumber { + pub const MAX: u32 = 64 + 32; + + pub const fn bcm_irq(n: u32) -> Self { + assert!(n < 64); + Self(n + 32) + } + + pub const fn qa7_irq(n: u32) -> Self { + assert!(n < 32); + Self(n) + } + + pub const fn is_bcm_irq(self) -> bool { + self.0 >= 32 + } + + pub const fn number(self) -> u32 { + if self.is_bcm_irq() { + self.0 - 32 + } else { + self.0 + } + } + + pub const fn index(self) -> usize { + self.0 as usize + } +} + +impl fmt::Debug for IrqNumber { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}_irq{}", + if self.is_bcm_irq() { "bcm" } else { "qa7" }, + self.number() + ) + } +} + +struct BcmIrqchipInner { + regs: DeviceMemoryIo, +} +struct Qa7IrqchipInner { + regs: DeviceMemoryIo, +} + +pub struct Bcm283xIrqchip { + bcm_inner: InitOnce>, + qa7_inner: InitOnce>, + table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; IrqNumber::MAX as usize]>, +} + +impl BcmIrqchipInner { + fn enable_irq(&self, n: u32) -> Result<(), Errno> { + let (reg, bit) = if n < 32 { + (&self.regs.ENABLE1, 1 << n) + } else if n < 64 { + (&self.regs.ENABLE2, 1 << (n - 32)) + } else { + todo!(); + }; + reg.set(reg.get() | bit); + Ok(()) + } + + fn pending_irq(&self) -> Option { + let status = self.regs.PENDING2.get(); + for bit in 0..32 { + if status & (1 << bit) != 0 { + return Some(bit + 32); + } + } + None + } +} + +impl Qa7IrqchipInner { + fn enable_irq(&self, n: u32) -> Result<(), Errno> { + // TODO check this code in SMP setup + let core_id = MPIDR_EL1.get() & 0x3; + // Timer IRQ control + let (reg, bit) = if n < 4 { + (&self.regs.CORE_IRQ_EN[core_id as usize], 1 << n) + } else { + todo!() + }; + reg.set(reg.get() | bit); + Ok(()) + } + + fn pending_irq(&self) -> Option { + let core_id = MPIDR_EL1.get() & 0x3; + let reg = &self.regs.CORE_IRQ_SRC[core_id as usize]; + let value = reg.get(); + for bit in 0..8 { + if value & (1 << bit) != 0 { + return Some(bit); + } + } + None + } + + fn clear_irq(&self) { + let core_id = MPIDR_EL1.get() & 0x3; + let reg = &self.regs.CORE_IRQ_SRC[core_id as usize]; + reg.set(0); + } +} + +impl Device for Bcm283xIrqchip { + fn name(&self) -> &'static str { + "BCM283x/QA7 Interrupt Controller" + } + + unsafe fn enable(&self) -> Result<(), Errno> { + self.bcm_inner.init(IrqSafeSpinLock::new(BcmIrqchipInner { + regs: DeviceMemoryIo::map("BCM283x Peripheral Interrupt Controller", 0x3F00B000, 1)?, + })); + self.qa7_inner.init(IrqSafeSpinLock::new(Qa7IrqchipInner { + regs: DeviceMemoryIo::map("QA7 Core Interrupt Controller", 0x40000000, 1)?, + })); + Ok(()) + } +} + +impl IntController for Bcm283xIrqchip { + type IrqNumber = IrqNumber; + + fn register_handler( + &self, + irq: IrqNumber, + handler: &'static (dyn IntSource + Sync), + ) -> Result<(), Errno> { + let mut table = self.table.lock(); + let irqi = irq.index(); + if table[irqi as usize].is_some() { + return Err(Errno::AlreadyExists); + } + + debugln!("Bound {:?} to {:?}", irq, Device::name(handler)); + table[irqi as usize] = Some(handler); + + Ok(()) + } + + fn enable_irq(&self, irq: IrqNumber) -> Result<(), Errno> { + if irq.is_bcm_irq() { + self.bcm_inner.get().lock().enable_irq(irq.number()) + } else { + self.qa7_inner.get().lock().enable_irq(irq.number()) + } + } + + fn handle_pending_irqs<'q>(&'q self, _ic: &IrqContext<'q>) { + let qa7 = self.qa7_inner.get().lock(); + let bcm = self.bcm_inner.get().lock(); + + let irq_number = if let Some(irq) = qa7.pending_irq() { + irq as usize + } else if let Some(irq) = bcm.pending_irq() { + irq as usize + 32 + } else { + panic!("No IRQ pending"); + }; + + drop(bcm); + + if irq_number < 32 { + qa7.clear_irq(); + drop(qa7); + } + + { + let table = self.table.lock(); + match table[irq_number] { + None => panic!("No handler registered for irq{}", irq_number), + Some(handler) => { + drop(table); + handler.handle_irq().expect("irq handler failed") + } + } + } + } +} + +impl Bcm283xIrqchip { + pub const fn new() -> Self { + Self { + bcm_inner: InitOnce::new(), + qa7_inner: InitOnce::new(), + table: IrqSafeSpinLock::new([None; IrqNumber::MAX as usize]), + } + } +} diff --git a/kernel/src/arch/aarch64/mach_rpi3/mailbox.rs b/kernel/src/arch/aarch64/mach_rpi3/mailbox.rs new file mode 100644 index 0000000..bdfa1e1 --- /dev/null +++ b/kernel/src/arch/aarch64/mach_rpi3/mailbox.rs @@ -0,0 +1,184 @@ +use crate::dev::Device; +use crate::mem::{self, virt::DeviceMemoryIo}; +use crate::sync::IrqSafeSpinLock; +use crate::util::InitOnce; +use error::Errno; + +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::registers::{ReadOnly, WriteOnly}; +use tock_registers::{register_bitfields, register_structs}; + +register_bitfields! { + u32, + STATUS [ + FULL OFFSET(31) NUMBITS(1) [], + EMPTY OFFSET(30) NUMBITS(1) [], + ], +} + +register_structs! { + #[allow(non_snake_case)] + Regs { + (0x00 => READ: ReadOnly), + (0x04 => _res0), + (0x18 => STATUS: ReadOnly), + (0x1C => _res1), + (0x20 => WRITE: WriteOnly), + (0x24 => @END), + } +} + +#[repr(C, align(16))] +struct MboxBuffer([u32; 36]); + +struct Inner { + regs: DeviceMemoryIo, + buf: MboxBuffer, +} + +pub struct Bcm283xMailbox { + inner: InitOnce>, + base: usize, +} + +impl Inner { + const RESPONSE: u32 = 1 << 31; + const REQUEST: u32 = 0; + + const PROP_ARM_MEMORY: u32 = 0x10005; + const PROP_SET_POWER_STATE: u32 = 0x28001; + const PROP_GET_CLOCK_RATE: u32 = 0x30002; + + fn call(&self, ch: u8) -> Result<(), Errno> { + let ptr_virt = &self.buf as *const _ as usize; + let ptr_phys = ptr_virt - mem::KERNEL_OFFSET; + assert!(ptr_phys < 0x100000000); + + while self.regs.STATUS.matches_all(STATUS::FULL::SET) { + cortex_a::asm::nop(); + } + + let val = (ptr_phys as u32) | (ch as u32); + self.regs.WRITE.set(val); + + loop { + while self.regs.STATUS.matches_all(STATUS::EMPTY::SET) { + cortex_a::asm::nop(); + } + + if self.regs.READ.get() == val { + return Ok(()); + } + } + } + + fn memory_split(&mut self) -> Result { + self.buf.0[0] = 8 * 4; + self.buf.0[1] = Self::REQUEST; + + self.buf.0[2] = Self::PROP_ARM_MEMORY; + self.buf.0[3] = 8; + self.buf.0[4] = 0; + self.buf.0[5] = 0x12345678; + self.buf.0[6] = 0x87654321; + self.buf.0[7] = 0; + + self.call(8)?; + + if self.buf.0[1] != Self::RESPONSE { + return Err(Errno::InvalidArgument); + } + + Ok(self.buf.0[6] as usize) + } + + fn set_power_state(&mut self, dev: u32, cmd: u32) -> Result<(), Errno> { + self.buf.0[0] = 8 * 4; + self.buf.0[1] = Self::REQUEST; + + self.buf.0[2] = Self::PROP_SET_POWER_STATE; + self.buf.0[3] = 8; + self.buf.0[4] = 0; + self.buf.0[5] = dev; + self.buf.0[6] = cmd; + self.buf.0[7] = 0; + + self.call(8)?; + + if self.buf.0[1] != Self::RESPONSE { + return Err(Errno::InvalidArgument); + } + + if self.buf.0[6] & 1 << 1 != 0 { + return Err(Errno::DoesNotExist); + } + + if self.buf.0[6] & 1 << 0 == 0 { + return Err(Errno::InvalidArgument); + } + + Ok(()) + } + + fn clock_rate(&mut self, clk: u32) -> Result { + self.buf.0[0] = 8 * 4; + self.buf.0[1] = Self::REQUEST; + + self.buf.0[2] = Self::PROP_GET_CLOCK_RATE; + self.buf.0[3] = 8; + self.buf.0[4] = 0; + self.buf.0[5] = clk; + self.buf.0[6] = 0; + self.buf.0[7] = 0; + + self.call(8)?; + + if self.buf.0[1] != Self::RESPONSE { + return Err(Errno::InvalidArgument); + } + + Ok(self.buf.0[6]) + } +} + +impl Device for Bcm283xMailbox { + fn name(&self) -> &'static str { + "BCM283x Mailbox" + } + + unsafe fn enable(&self) -> Result<(), Errno> { + self.inner.init(IrqSafeSpinLock::new(Inner { + regs: DeviceMemoryIo::map(self.name(), self.base, 1)?, + buf: MboxBuffer([0; 36]), + })); + + Ok(()) + } +} + +impl Bcm283xMailbox { + pub const POWER_STATE_ON: u32 = 1 << 0; + pub const POWER_STATE_WAIT: u32 = 1 << 1; + pub const POWER_SD_CARD: u32 = 0; + + pub const CLOCK_EMMC: u32 = 1; + + pub fn memory_split(&self) -> Result { + self.inner.get().lock().memory_split() + } + + pub fn set_power_state(&self, dev: u32, cmd: u32) -> Result<(), Errno> { + self.inner.get().lock().set_power_state(dev, cmd) + } + + pub fn clock_rate(&self, clk: u32) -> Result { + self.inner.get().lock().clock_rate(clk) + } + + pub const unsafe fn new(base: usize) -> Self { + Self { + inner: InitOnce::new(), + base, + } + } +} diff --git a/kernel/src/arch/aarch64/mach_rpi3/mod.rs b/kernel/src/arch/aarch64/mach_rpi3/mod.rs new file mode 100644 index 0000000..fccd42a --- /dev/null +++ b/kernel/src/arch/aarch64/mach_rpi3/mod.rs @@ -0,0 +1,65 @@ +#![allow(missing_docs)] + +use crate::arch::aarch64::timer::GenericTimer; +use crate::dev::{Device, serial::{SerialDevice, pl011::Pl011}, irq::IntSource}; +use crate::mem::phys; +use error::Errno; + +pub mod irqchip; +pub use irqchip::{IrqNumber, Bcm283xIrqchip}; +pub mod emmc; +pub use emmc::MassMediaController; +pub mod mailbox; +pub use mailbox::Bcm283xMailbox; + +const UART_BASE: usize = 0x3F201000; +const EMMC_BASE: usize = 0x3F300000; +const BCM_MBOX_BASE: usize = 0x3F00B880; +const UART_IRQ: IrqNumber = IrqNumber::bcm_irq(57); +const LOCAL_TIMER_IRQ: IrqNumber = IrqNumber::qa7_irq(1); + +pub fn init_board_early() -> Result<(), Errno> { + unsafe { + UART.enable()?; + BCM_MBOX.enable()?; + + let memory = BCM_MBOX.memory_split()?; + infoln!("Memory split: {:#x}", memory); + + phys::init_from_region(0, memory); + } + Ok(()) +} + +pub fn init_board() -> Result<(), Errno> { + unsafe { + IRQCHIP.enable()?; + UART.init_irqs()?; + + EMMC.enable()?; + } + Ok(()) +} + +#[inline] +pub fn intc() -> &'static Bcm283xIrqchip { + &IRQCHIP +} + +/// Returns primary console for this machine +#[inline] +pub fn console() -> &'static impl SerialDevice { + &UART +} + +/// Returns the timer used as CPU-local periodic IRQ source +#[inline] +pub fn local_timer() -> &'static GenericTimer { + &LOCAL_TIMER +} + +static IRQCHIP: Bcm283xIrqchip = Bcm283xIrqchip::new(); +static EMMC: MassMediaController = unsafe { MassMediaController::new(EMMC_BASE) }; +static UART: Pl011 = unsafe { Pl011::new(UART_BASE, UART_IRQ) }; +pub(self) static BCM_MBOX: Bcm283xMailbox = unsafe { Bcm283xMailbox::new(BCM_MBOX_BASE) }; +static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ); diff --git a/kernel/src/dev/mod.rs b/kernel/src/dev/mod.rs index e3446e5..79f2102 100644 --- a/kernel/src/dev/mod.rs +++ b/kernel/src/dev/mod.rs @@ -7,6 +7,8 @@ use error::Errno; pub mod fdt; pub mod gpio; pub mod irq; +#[allow(missing_docs)] +pub mod sd; pub mod pci; pub mod rtc; pub mod serial; diff --git a/kernel/src/dev/sd.rs b/kernel/src/dev/sd.rs new file mode 100644 index 0000000..577a040 --- /dev/null +++ b/kernel/src/dev/sd.rs @@ -0,0 +1,332 @@ +use crate::dev::Device; +use vfs::BlockDevice; +use error::Errno; + +pub trait SdHostController: Device + BlockDevice { + // Physical layer + fn send_cmd(&self, cmd: &mut SdCommand) -> Result; + fn phys_reset(&self) -> Result<(), Errno>; + fn is_phys_inserted(&self) -> bool; + fn set_transfer_block_size(&self, blk: usize) -> Result<(), Errno>; + + // Data layer + fn set_card_address(&self, rca: u16) -> Result<(), Errno>; + fn set_card_identification(&self, id: SdCardIdentification) -> Result<(), Errno>; + fn reset_card_identification(&self) -> Result<(), Errno>; + + fn reset_card_probe(&self) -> Result<(), Errno> { + self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd0, + argument: 0, + transfer: SdCommandTransfer::None, + })?; + + if self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd8, + argument: 0x1AA, + transfer: SdCommandTransfer::None, + })?.unwrap_one() != 0x1AA { + warnln!("Card did not respond to CMD8"); + return Err(Errno::InvalidArgument); + } + + // Set operating conditions + for _ in 0..10 { + let res = self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Acmd41, + argument: 0x00FF8000, + transfer: SdCommandTransfer::None + })?.unwrap_one(); + + if res & (1 << 31) != 0 { + return Ok(()); + } + + for _ in 0..1000000 { + cortex_a::asm::nop(); + } + } + + warnln!("Card did not respond to Acmd41"); + Err(Errno::InvalidArgument) + } + + fn reset_card(&self) -> Result<(), Errno> { + let mut buf = [0u8; 16]; + + self.reset_card_identification()?; + + // Reset physical interface + self.phys_reset()?; + + // Bail out if card is not physically present + if !self.is_phys_inserted() { + return Ok(()); + } + + // Probe for card + self.reset_card_probe()?; + + // Perform init sequence + let cmd2 = self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd2, + argument: 0, + transfer: SdCommandTransfer::None, + })?.unwrap_four(); + + let cmd3 = self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd3, + argument: 0, + transfer: SdCommandTransfer::None, + })?.unwrap_one(); + + let rca = (cmd3 >> 16) as u16; + + if cmd3 & (1 << 14) != 0 { + warnln!("Illegal command"); + return Err(Errno::InvalidArgument); + } + + if cmd3 & (1 << 13) != 0 { + warnln!("Card reported error"); + return Err(Errno::InvalidArgument); + } + + if cmd3 & (1 << 8) == 0 { + warnln!("Card is not ready for data mode"); + return Err(Errno::InvalidArgument); + } + let cmd9 = self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd9, + argument: cmd3 & 0xFFFF0000, + transfer: SdCommandTransfer::None, + })?.unwrap_four(); + + debugln!("cmd9 = {:#x?}", cmd9); + let csd_structure = (cmd9[3] >> 16) & 0x3; + let capacity = match csd_structure { + 0 => { + let c_size = ((cmd9[2] & 0x3) << 10) | ((cmd9[1] >> 22) & 0x3FF); + let c_size_mult = (cmd9[1] >> 7) & 0x7; + ((c_size + 1) as u64) << (c_size_mult + 9 /* Block size is 512 */ + 2) + }, + 1 => todo!(), + _ => { + warnln!("Invalid CSD version: {}", csd_structure); + return Err(Errno::InvalidArgument); + } + }; + + let cmd7 = self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd7, + argument: cmd3 & 0xFFFF0000, + transfer: SdCommandTransfer::None, + })?.unwrap_one(); + + let status = (cmd7 >> 9) & 0xF; + if status != 3 && status != 4 { + warnln!("Card reported invalid status: {}", status); + return Err(Errno::InvalidArgument); + } + + // Set block size + self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Cmd16, + argument: 512, + transfer: SdCommandTransfer::None, + })?; + + self.set_card_address(rca)?; + self.send_cmd(&mut SdCommand { + number: SdCommandNumber::Acmd51, + argument: 0, + transfer: SdCommandTransfer::Read(&mut buf[..8], 8) + })?; + + let sd_spec = buf[0] & 0xF; + + let version = match sd_spec { + 0 => SdCardVersion::Ver10, + 1 => SdCardVersion::Ver11, + 2 => { + // FIXME check for 3.0/4.0 + SdCardVersion::Ver20 + }, + _ => panic!("Invalid version: {:#x}", sd_spec) + }; + + let card_id = SdCardIdentification { + id: cmd2, + version, + capacity + }; + + self.set_card_identification(card_id)?; + infoln!("Found a valid SD card of capacity {}B", capacity); + + // TODO High speed support + // if version >= SdCardVersion::Ver11 { + // let mut buf = [0u8; 64]; + // self.send_cmd(&mut SdCommand { + // number: SdCommandNumber::Cmd6, + // argument: 0x00FFFFF0, + // transfer: SdCommandTransfer::Read(&mut buf, 64) + // })?; + + // // Check HS support + // let hs_support = buf[13] >> 1 != 0; + // if hs_support { + // debugln!("Switching to high speed mode"); + // self.send_cmd(&mut SdCommand { + // number: SdCommandNumber::Cmd6, + // argument: 0x80FFFFF1, + // transfer: SdCommandTransfer::None + // })?; + + // todo!(); + // } + // } + + // let dbus_widths = buf[0] & 0xF; + // TODO 4 bit mode support + + // TODO switch clock rate + + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SdResponseType { + None, + R1, + R1b, + R2, + R3, + R4, + R5, + R5b, + R6, + R7, +} + +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +pub enum SdCardVersion { + Ver10 = 0x10, + Ver11 = 0x11, + Ver20 = 0x20, + Ver30 = 0x30, + Ver40 = 0x40, +} + +pub struct SdCardIdentification { + pub id: [u32; 4], + pub version: SdCardVersion, + pub capacity: u64, +} + +pub struct SdCardStatus { + pub phys_inserted: bool, + pub address: Option, + pub id: Option, +} + +#[derive(Clone, Copy, Debug)] +pub enum SdResponse { + One(u32), + Four([u32; 4]), +} + +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(u32)] +pub enum SdCommandNumber { + Cmd0 = 0, + Cmd2 = 2, + Cmd3 = 3, + Cmd6 = 6, + Cmd7 = 7, + Cmd8 = 8, + Cmd9 = 9, + Cmd16 = 16, + Cmd17 = 17, + Acmd41 = 41, + Acmd51 = 51, + Cmd55 = 55, +} + +pub enum SdCommandTransfer<'a> { + None, + Read(&'a mut [u8], u16), + Write(&'a [u8], u16) +} + +pub struct SdCommand<'a> { + pub number: SdCommandNumber, + pub argument: u32, + pub transfer: SdCommandTransfer<'a>, +} + +impl SdCardStatus { + pub const fn invalid() -> Self { + Self { + phys_inserted: false, + address: None, + id: None, + } + } +} + +impl SdResponseType { + pub const fn is_busy(self) -> bool { + match self { + Self::R1b | Self::R5b => true, + _ => false + } + } +} + +impl SdResponse { + pub fn unwrap_one(&self) -> u32 { + match self { + &SdResponse::One(v) => v, + _ => panic!("Unexpected response type") + } + } + + pub fn unwrap_four(&self) -> [u32; 4] { + match self { + &SdResponse::Four(v) => v, + _ => panic!("Unexpected response type") + } + } +} + +impl SdCommand<'_> { + pub const fn response_type(&self) -> SdResponseType { + match self.number { + SdCommandNumber::Cmd0 => SdResponseType::None, + SdCommandNumber::Cmd2 => SdResponseType::R2, + SdCommandNumber::Cmd3 => SdResponseType::R6, + SdCommandNumber::Cmd6 => SdResponseType::R1, + SdCommandNumber::Cmd7 => SdResponseType::R1b, + SdCommandNumber::Cmd8 => SdResponseType::R7, + SdCommandNumber::Cmd9 => SdResponseType::R2, + SdCommandNumber::Cmd16 => SdResponseType::R1, + SdCommandNumber::Cmd17 => SdResponseType::R1, + SdCommandNumber::Acmd41 => SdResponseType::R3, + SdCommandNumber::Acmd51 => SdResponseType::R1, + SdCommandNumber::Cmd55 => SdResponseType::R1, + } + } + + pub const fn is_acmd(&self) -> bool { + match self.number { + SdCommandNumber::Acmd41 | SdCommandNumber::Acmd51 => true, + _ => false + } + } + + pub const fn number(&self) -> u32 { + self.number as u32 + } +} diff --git a/kernel/src/util.rs b/kernel/src/util.rs index 948c31c..cf8924d 100644 --- a/kernel/src/util.rs +++ b/kernel/src/util.rs @@ -51,3 +51,4 @@ impl InitOnce { } unsafe impl Sync for InitOnce {} +