blob: 77c566a76c23601ec6f01c87e4a59f5814457917 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! 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_capsules::pinmux_capsule::PinmuxCapsule;
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::pinmux_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<
'static,
matcha_capsules::nexus_spiflash::NexusSpiflash<
'static,
capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>,
>,
>,
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>,
>,
pinmux_capsule: &'static PinmuxCapsule,
}
/// 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)),
CAPSULE_PINMUX => f(Some(self.pinmux_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<
'static,
matcha_capsules::nexus_spiflash::NexusSpiflash<
'static,
capsules::virtual_spi::VirtualSpiMasterDevice<'static, spi_host_hal::SpiHw>,
>>,
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 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()
);
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 pinmux_capsule = static_init!(
matcha_capsules::pinmux_capsule::PinmuxCapsule,
matcha_capsules::pinmux_capsule::PinmuxCapsule::new(
board_kernel.create_grant(&memory_allocation_cap),
)
);
pinmux_capsule.set_pinmux(&pinmux_hal::PINMUX);
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);
// Storage access reads (only) from flash.
let storage_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 storage_read_page: &'static mut matcha_capsules::nexus_spiflash::NexusSpiflashPage = static_init!(
matcha_capsules::nexus_spiflash::NexusSpiflashPage,
matcha_capsules::nexus_spiflash::NexusSpiflashPage::default()
);
storage_capsule.set_flash(storage_virtual_flash, storage_read_page);
storage_virtual_flash.set_client(storage_capsule);
// Elfloader reads artifacts from flash & boots the SMC.
elfloader_capsule.set_smc_ctrl(&smc_ctrl_hal::SMC_CTRL);
let elfloader_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 elfloader_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(elfloader_virtual_flash, elfloader_read_page);
elfloader_virtual_flash.set_client(elfloader_capsule);
// NB: main app triggers the Elfloader bootstrap of 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,
pinmux_capsule: pinmux_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);
}