| //! Board file for Shodan's "Matcha" RISC-V development platform. | |
| #![no_std] | |
| #![no_main] | |
| #![feature(llvm_asm)] | |
| #![feature(const_fn)] | |
| #![feature(naked_functions)] | |
| use capsules::virtual_alarm::{MuxAlarm, VirtualMuxAlarm}; | |
| use core::fmt::Write; | |
| use core::panic::PanicInfo; | |
| use kernel::capabilities; | |
| use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState}; | |
| use kernel::component::Component; | |
| use kernel::hil; | |
| use kernel::hil::flash::HasClient; | |
| use kernel::hil::time::Alarm; | |
| use kernel::Chip; | |
| use kernel::Platform; | |
| use kernel::{create_capability, debug, static_init}; | |
| use kernel::debug::IoWrite; | |
| use matcha_capsules::dprintf_capsule::DprintfCapsule; | |
| use matcha_capsules::elfloader_capsule::ElfLoaderCapsule; | |
| use matcha_capsules::mailbox_capsule::MailboxCapsule; | |
| use matcha_capsules::storage_capsule::StorageCapsule; | |
| use matcha_config::*; | |
| use matcha_hal::spi_host_hal; | |
| use matcha_hal::timer_hal; | |
| use matcha_hal::uart_hal; | |
| use matcha_hal::smc_ctrl_hal; | |
| use matcha_hal::rv_core_ibex_hal; | |
| use rv32i::csr; | |
| pub mod chip; | |
| // Writer struct for panic handler. | |
| struct Writer {} | |
| static mut WRITER: Writer = Writer {}; | |
| impl Write for Writer { | |
| fn write_str(&mut self, s: &str) -> ::core::fmt::Result { | |
| self.write(s.as_bytes()); | |
| Ok(()) | |
| } | |
| } | |
| impl IoWrite for Writer { | |
| fn write(&mut self, buf: &[u8]) { | |
| unsafe { | |
| uart_hal::UART0.transmit_sync(buf); | |
| } | |
| } | |
| } | |
| // Stubbed LED for panic handler. | |
| pub struct MatchaLed { | |
| } | |
| impl MatchaLed { | |
| pub fn new() -> Self { | |
| Self {} | |
| } | |
| } | |
| impl kernel::hil::led::Led for MatchaLed { | |
| fn init(&mut self) { } | |
| fn on(&mut self) { } | |
| fn off(&mut self) { } | |
| fn toggle(&mut self) { } | |
| fn read(&self) -> bool { true } | |
| } | |
| /// Panic handler. | |
| #[cfg(not(test))] | |
| #[no_mangle] | |
| #[panic_handler] | |
| pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! { | |
| let first_led = &mut MatchaLed::new(); | |
| let writer = &mut WRITER; | |
| debug::panic( | |
| &mut [first_led], | |
| writer, | |
| pi, | |
| &rv32i::support::nop, | |
| &PROCESSES, | |
| &CHIP, | |
| ) | |
| } | |
| /// These symbols are defined in the linker script. | |
| extern "C" { | |
| /// Beginning of the ROM region containing app images. | |
| static _sapps: u8; | |
| /// End of the ROM region containing app images. | |
| static _eapps: u8; | |
| /// Beginning of the RAM region for app memory. | |
| static mut _sappmem: u8; | |
| /// End of the RAM region for app memory. | |
| static _eappmem: u8; | |
| } | |
| // Actual memory for holding the active process structures. Need an empty list | |
| // at least. | |
| static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; 4] = | |
| [None, None, None, None]; | |
| static mut CHIP: Option< | |
| &'static crate::chip::Matcha<VirtualMuxAlarm<'static, timer_hal::RvTimer>>, | |
| > = None; | |
| /// Dummy buffer that causes the linker to reserve enough space for the stack. | |
| /// Must be at least 16k in debug builds (@aappleby - not sure why, what's so large?) | |
| #[no_mangle] | |
| #[link_section = ".stack_buffer"] | |
| pub static mut STACK_MEMORY: [u8; 0x4000] = [0; 0x4000]; | |
| /// A structure representing this platform that holds references to all | |
| /// capsules for this platform. We've included an alarm and console. | |
| struct MatchaPlatform { | |
| console_capsule: &'static capsules::console::Console<'static>, | |
| alarm_capsule: &'static capsules::alarm::AlarmDriver< | |
| 'static, | |
| VirtualMuxAlarm<'static, timer_hal::RvTimer<'static>>, | |
| >, | |
| dprintf_capsule: &'static DprintfCapsule, | |
| storage_capsule: &'static StorageCapsule, | |
| elfloader_capsule: &'static ElfLoaderCapsule< | |
| 'static, | |
| matcha_capsules::nexus_spiflash::NexusSpiflash< | |
| 'static, | |
| capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>, | |
| >, | |
| >, | |
| mailbox_capsule: &'static MailboxCapsule, | |
| lldb_capsule: &'static capsules::low_level_debug::LowLevelDebug< | |
| 'static, | |
| capsules::virtual_uart::UartDevice<'static>, | |
| >, | |
| } | |
| /// Mapping of integer syscalls to objects that implement syscalls. | |
| impl Platform for MatchaPlatform { | |
| fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R | |
| where | |
| F: FnOnce(Option<&dyn kernel::Driver>) -> R, | |
| { | |
| match driver_num { | |
| CAPSULE_CONSOLE => f(Some(self.console_capsule)), | |
| CAPSULE_ALARM => f(Some(self.alarm_capsule)), | |
| CAPSULE_DPRINTF => f(Some(self.dprintf_capsule)), | |
| CAPSULE_STORAGE => f(Some(self.storage_capsule)), | |
| CAPSULE_ELFLOADER => f(Some(self.elfloader_capsule)), | |
| CAPSULE_MAILBOX => f(Some(self.mailbox_capsule)), | |
| CAPSULE_LLDB => f(Some(self.lldb_capsule)), | |
| _ => f(None), | |
| } | |
| } | |
| } | |
| /// Reset Handler. | |
| /// | |
| /// This function is called from the arch crate after some very basic RISC-V | |
| /// setup. | |
| #[no_mangle] | |
| pub unsafe fn reset_handler() { | |
| // Basic setup of the platform. | |
| rv32i::init_memory(); | |
| // Ibex-specific handler | |
| crate::chip::configure_trap_handler(); | |
| // initialize capabilities | |
| let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability); | |
| let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability); | |
| let main_loop_cap = create_capability!(capabilities::MainLoopCapability); | |
| let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&PROCESSES)); | |
| let dynamic_deferred_call_clients = | |
| static_init!([DynamicDeferredCallClientState; 1], Default::default()); | |
| let dynamic_deferred_caller = static_init!( | |
| DynamicDeferredCall, | |
| DynamicDeferredCall::new(dynamic_deferred_call_clients) | |
| ); | |
| DynamicDeferredCall::set_global_instance(dynamic_deferred_caller); | |
| // Create a shared UART channel for the console and for kernel debug. | |
| let uart_mux = components::console::UartMuxComponent::new( | |
| &uart_hal::UART0, | |
| matcha_config::UART0_BAUDRATE, | |
| dynamic_deferred_caller, | |
| ) | |
| .finalize(()); | |
| let alarm = &timer_hal::TIMER; | |
| alarm.setup(); | |
| // Create a shared virtualization mux layer on top of a single hardware | |
| // alarm. | |
| let mux_alarm = static_init!(MuxAlarm<'static, timer_hal::RvTimer>, MuxAlarm::new(alarm)); | |
| hil::time::Alarm::set_alarm_client(&timer_hal::TIMER, mux_alarm); | |
| // Alarm | |
| let virtual_alarm_user = static_init!( | |
| VirtualMuxAlarm<'static, timer_hal::RvTimer>, | |
| VirtualMuxAlarm::new(mux_alarm) | |
| ); | |
| let scheduler_timer_virtual_alarm = static_init!( | |
| VirtualMuxAlarm<'static, timer_hal::RvTimer>, | |
| VirtualMuxAlarm::new(mux_alarm) | |
| ); | |
| let alarm_capsule = static_init!( | |
| capsules::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, timer_hal::RvTimer>>, | |
| capsules::alarm::AlarmDriver::new( | |
| virtual_alarm_user, | |
| board_kernel.create_grant(&memory_allocation_cap) | |
| ) | |
| ); | |
| hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm_capsule); | |
| let chip = static_init!( | |
| crate::chip::Matcha<VirtualMuxAlarm<'static, timer_hal::RvTimer>>, | |
| crate::chip::Matcha::new(scheduler_timer_virtual_alarm) | |
| ); | |
| scheduler_timer_virtual_alarm.set_alarm_client(chip.scheduler_timer()); | |
| CHIP = Some(chip); | |
| // Need to enable all interrupts for Tock Kernel | |
| chip.enable_plic_interrupts(); | |
| // enable interrupts globally | |
| csr::CSR | |
| .mie | |
| .modify(csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET + csr::mie::mie::mext::SET); | |
| csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET); | |
| // Setup the console. | |
| let console_capsule = | |
| components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(()); | |
| // Create the debugger object that handles calls to `debug!()`. | |
| components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(()); | |
| let lldb_capsule = | |
| components::lldb::LowLevelDebugComponent::new(board_kernel, uart_mux).finalize(()); | |
| let dprintf_capsule = static_init!( | |
| DprintfCapsule, | |
| DprintfCapsule { | |
| app_data_grant: board_kernel.create_grant(&memory_allocation_cap) | |
| } | |
| ); | |
| let storage_capsule = static_init!( | |
| matcha_capsules::storage_capsule::StorageCapsule, | |
| matcha_capsules::storage_capsule::StorageCapsule::new( | |
| board_kernel.create_grant(&memory_allocation_cap) | |
| ) | |
| ); | |
| // Construct a mailbox that points to the platform-specific base address and | |
| // PLIC lines | |
| let mailbox_hal: &'static mut matcha_hal::mailbox_hal::MailboxImpl = static_init!( | |
| matcha_hal::mailbox_hal::MailboxImpl, | |
| matcha_hal::mailbox_hal::MailboxImpl::new( | |
| MAILBOX_BASE, | |
| MAILBOX_WTIRQ, | |
| MAILBOX_RTIRQ, | |
| MAILBOX_EIRQ, | |
| ) | |
| ); | |
| let fpga_version = rv_core_ibex_hal::RV_CORE_IBEX_SEC.fpga_version(); | |
| let elfloader_capsule = static_init!( | |
| matcha_capsules::elfloader_capsule::ElfLoaderCapsule< | |
| 'static, | |
| matcha_capsules::nexus_spiflash::NexusSpiflash< | |
| 'static, | |
| capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>, | |
| >>, | |
| matcha_capsules::elfloader_capsule::ElfLoaderCapsule::new(fpga_version != 0) | |
| ); | |
| let mailbox_capsule = static_init!( | |
| matcha_capsules::mailbox_capsule::MailboxCapsule, | |
| matcha_capsules::mailbox_capsule::MailboxCapsule::new( | |
| board_kernel.create_grant(&memory_allocation_cap), | |
| ) | |
| ); | |
| // Mailbox interrupts are delegated to the capsule, except for the | |
| // bottom-level bit twiddling. | |
| mailbox_hal.set_client_isr(mailbox_capsule); | |
| // Our other capsules and our "chip" hold references to the mailbox. | |
| mailbox_capsule.set_mailbox(mailbox_hal); | |
| elfloader_capsule.set_mailbox(mailbox_hal); | |
| chip.set_mailbox_isr(mailbox_hal); | |
| let mux_spi = components::spi::SpiMuxComponent::new( | |
| &spi_host_hal::SPI_HOST0 | |
| ).finalize(components::spi_mux_component_helper!(spi_host_hal::SpiHw)); | |
| let nexus_spiflash_spi = static_init!( | |
| capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>, | |
| capsules::virtual_spi::VirtualSpiMasterDevice::new(mux_spi, 0) | |
| ); | |
| let nexus_spiflash_virtual_alarm = static_init!( | |
| VirtualMuxAlarm<'static, timer_hal::RvTimer>, | |
| VirtualMuxAlarm::new(mux_alarm) | |
| ); | |
| let nexus_spiflash = static_init!( | |
| matcha_capsules::nexus_spiflash::NexusSpiflash< | |
| 'static, | |
| capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>, | |
| >, | |
| matcha_capsules::nexus_spiflash::NexusSpiflash::new( | |
| nexus_spiflash_spi, | |
| &mut matcha_capsules::nexus_spiflash::TXBUFFER, | |
| &mut matcha_capsules::nexus_spiflash::RXBUFFER, | |
| ) | |
| ); | |
| nexus_spiflash_spi.set_client(nexus_spiflash); | |
| nexus_spiflash_virtual_alarm.set_alarm_client(nexus_spiflash); | |
| let mux_flash = static_init!( | |
| capsules::virtual_flash::MuxFlash<'static, matcha_capsules::nexus_spiflash::NexusSpiflash< | |
| 'static, | |
| capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>, | |
| >>, | |
| capsules::virtual_flash::MuxFlash::new(nexus_spiflash) | |
| ); | |
| hil::flash::HasClient::set_client(nexus_spiflash, mux_flash); | |
| let virtual_flash = static_init!( | |
| capsules::virtual_flash::FlashUser<'static, matcha_capsules::nexus_spiflash::NexusSpiflash< | |
| 'static, | |
| capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>, | |
| >>, | |
| capsules::virtual_flash::FlashUser::new(mux_flash) | |
| ); | |
| let flash_read_page: &'static mut matcha_capsules::nexus_spiflash::NexusSpiflashPage = static_init!( | |
| matcha_capsules::nexus_spiflash::NexusSpiflashPage, | |
| matcha_capsules::nexus_spiflash::NexusSpiflashPage::default() | |
| ); | |
| elfloader_capsule.set_flash(virtual_flash, flash_read_page); | |
| elfloader_capsule.set_smc_ctrl(&smc_ctrl_hal::SMC_CTRL); | |
| virtual_flash.set_client(elfloader_capsule); | |
| // elfloader_capsule.load_sel4(); | |
| let platform = MatchaPlatform { | |
| console_capsule: console_capsule, | |
| alarm_capsule: alarm_capsule, | |
| dprintf_capsule: dprintf_capsule, | |
| storage_capsule: storage_capsule, | |
| elfloader_capsule: elfloader_capsule, | |
| mailbox_capsule: mailbox_capsule, | |
| lldb_capsule: lldb_capsule, | |
| }; | |
| kernel::procs::load_processes( | |
| board_kernel, | |
| chip, | |
| core::slice::from_raw_parts( | |
| &_sapps as *const u8, | |
| &_eapps as *const u8 as usize - &_sapps as *const u8 as usize, | |
| ), | |
| core::slice::from_raw_parts_mut( | |
| &mut _sappmem as *mut u8, | |
| &_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize, | |
| ), | |
| &mut PROCESSES, | |
| kernel::procs::FaultResponse::Panic, | |
| &process_mgmt_cap, | |
| ) | |
| .unwrap_or_else(|err| { | |
| debug!("Error loading processes!"); | |
| debug!("{:?}", err); | |
| }); | |
| debug!("MatchaPlatform initialisation complete. Entering main loop"); | |
| let scheduler = components::sched::priority::PriorityComponent::new(board_kernel).finalize(()); | |
| board_kernel.kernel_loop(&platform, chip, None, scheduler, &main_loop_cap); | |
| } |