blob: c1dcffb485b324de6d22abd96068108c3c037f11 [file] [log] [blame]
//! Board file for LowRISC OpenTitan Matcha RISC-V development platform.
//!
//! - <https://opentitan.org/>
#![no_std]
// Disable this attribute when documenting, as a workaround for
// https://github.com/rust-lang/rust/issues/62184.
#![cfg_attr(not(doc), no_main)]
#![feature(const_in_array_repeat_expressions)]
use matcha_capsules::debug_uart::DebugUart;
use capsules::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
use capsules::virtual_hmac::VirtualMuxHmac;
use kernel::capabilities;
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
use kernel::common::registers::{ReadOnly, WriteOnly};
use kernel::common::StaticRef;
use kernel::component::Component;
use kernel::hil;
use kernel::hil::i2c::I2CMaster;
use kernel::hil::time::Alarm;
use kernel::Chip;
use kernel::Platform;
use kernel::{create_capability, debug, static_init};
use rv32i::csr;
pub mod io;
//
// 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 matcha::chip::Matcha<VirtualMuxAlarm<'static, matcha::timer::RvTimer>>,
> = None;
// How should the kernel respond when a process faults.
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
/// 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 OpenTitan {
led: &'static capsules::led::LED<'static, matcha::gpio::GpioPin<'static>>,
gpio: &'static capsules::gpio::GPIO<'static, matcha::gpio::GpioPin<'static>>,
console: &'static capsules::console::Console<'static>,
alarm: &'static capsules::alarm::AlarmDriver<
'static,
VirtualMuxAlarm<'static, matcha::timer::RvTimer<'static>>,
>,
hmac: &'static capsules::hmac::HmacDriver<
'static,
VirtualMuxHmac<'static, lowrisc::hmac::Hmac<'static>, [u8; 32]>,
[u8; 32],
>,
lldb: &'static capsules::low_level_debug::LowLevelDebug<
'static,
capsules::virtual_uart::UartDevice<'static>,
>,
i2c_master: &'static capsules::i2c_master::I2CMasterDriver<lowrisc::i2c::I2c<'static>>,
debug_uart: &'static DebugUart,
storage_manager: &'static capsules::storage_manager::StorageManager,
}
/// Mapping of integer syscalls to objects that implement syscalls.
impl Platform for OpenTitan {
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
where
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
{
match driver_num {
capsules::led::DRIVER_NUM => f(Some(self.led)),
capsules::hmac::DRIVER_NUM => f(Some(self.hmac)),
capsules::gpio::DRIVER_NUM => f(Some(self.gpio)),
capsules::console::DRIVER_NUM => f(Some(self.console)),
capsules::alarm::DRIVER_NUM => f(Some(self.alarm)),
capsules::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
capsules::i2c_master::DRIVER_NUM => f(Some(self.i2c_master)),
matcha_capsules::debug_uart::DRIVER_NUM => f(Some(self.debug_uart)),
capsules::storage_manager::DRIVER_NUM => f(Some(self.storage_manager)),
_ => 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
matcha::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);
// Configure kernel debug gpios as early as possible
kernel::debug::assign_gpios(
Some(&matcha::gpio::PORT[7]), // First LED
None,
None,
);
// Create a shared UART channel for the console and for kernel debug.
let uart_mux = components::console::UartMuxComponent::new(
&matcha::uart::UART0,
matcha::uart::UART0_BAUDRATE,
dynamic_deferred_caller,
)
.finalize(());
// LEDs
// Start with half on and half off
let led = components::led::LedsComponent::new(components::led_component_helper!(
matcha::gpio::GpioPin,
(
&matcha::gpio::PORT[8],
kernel::hil::gpio::ActivationMode::ActiveHigh
),
(
&matcha::gpio::PORT[9],
kernel::hil::gpio::ActivationMode::ActiveHigh
),
(
&matcha::gpio::PORT[10],
kernel::hil::gpio::ActivationMode::ActiveHigh
),
(
&matcha::gpio::PORT[11],
kernel::hil::gpio::ActivationMode::ActiveHigh
),
(
&matcha::gpio::PORT[12],
kernel::hil::gpio::ActivationMode::ActiveHigh
),
(
&matcha::gpio::PORT[13],
kernel::hil::gpio::ActivationMode::ActiveHigh
),
(
&matcha::gpio::PORT[14],
kernel::hil::gpio::ActivationMode::ActiveHigh
),
(
&matcha::gpio::PORT[15],
kernel::hil::gpio::ActivationMode::ActiveHigh
)
))
.finalize(components::led_component_buf!(matcha::gpio::GpioPin));
let gpio = components::gpio::GpioComponent::new(
board_kernel,
components::gpio_component_helper!(
matcha::gpio::GpioPin,
0 => &matcha::gpio::PORT[0],
1 => &matcha::gpio::PORT[1],
2 => &matcha::gpio::PORT[2],
3 => &matcha::gpio::PORT[3],
4 => &matcha::gpio::PORT[4],
5 => &matcha::gpio::PORT[5],
6 => &matcha::gpio::PORT[6],
7 => &matcha::gpio::PORT[15]
),
)
.finalize(components::gpio_component_buf!(matcha::gpio::GpioPin));
let alarm = &matcha::timer::TIMER;
alarm.setup();
// Create a shared virtualization mux layer on top of a single hardware
// alarm.
let mux_alarm = static_init!(
MuxAlarm<'static, matcha::timer::RvTimer>,
MuxAlarm::new(alarm)
);
hil::time::Alarm::set_alarm_client(&matcha::timer::TIMER, mux_alarm);
// Alarm
let virtual_alarm_user = static_init!(
VirtualMuxAlarm<'static, matcha::timer::RvTimer>,
VirtualMuxAlarm::new(mux_alarm)
);
let scheduler_timer_virtual_alarm = static_init!(
VirtualMuxAlarm<'static, matcha::timer::RvTimer>,
VirtualMuxAlarm::new(mux_alarm)
);
let alarm = static_init!(
capsules::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, matcha::timer::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);
let chip = static_init!(
matcha::chip::Matcha<VirtualMuxAlarm<'static, matcha::timer::RvTimer>>,
matcha::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 = 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 = components::lldb::LowLevelDebugComponent::new(board_kernel, uart_mux).finalize(());
let hmac_data_buffer = static_init!([u8; 64], [0; 64]);
let hmac_dest_buffer = static_init!([u8; 32], [0; 32]);
let mux_hmac = components::hmac::HmacMuxComponent::new(&matcha::hmac::HMAC).finalize(
components::hmac_mux_component_helper!(lowrisc::hmac::Hmac, [u8; 32]),
);
let hmac = components::hmac::HmacComponent::new(
board_kernel,
&mux_hmac,
hmac_data_buffer,
hmac_dest_buffer,
)
.finalize(components::hmac_component_helper!(
lowrisc::hmac::Hmac,
[u8; 32]
));
let i2c_master = static_init!(
capsules::i2c_master::I2CMasterDriver<lowrisc::i2c::I2c<'static>>,
capsules::i2c_master::I2CMasterDriver::new(
&matcha::i2c::I2C,
&mut capsules::i2c_master::BUF,
board_kernel.create_grant(&memory_allocation_cap)
)
);
matcha::i2c::I2C.set_master_client(i2c_master);
let debug_uart = static_init!(
DebugUart,
DebugUart {
tx_busy: StaticRef::new(0x4000_0010 as *const ReadOnly<u32>),
tx_port: StaticRef::new(0x4000_0018 as *const WriteOnly<u32>),
app_data_grant: board_kernel.create_grant(&memory_allocation_cap)
}
);
/// 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;
}
let storage_manager = static_init!(
capsules::storage_manager::StorageManager,
capsules::storage_manager::StorageManager::new(board_kernel.create_grant(&memory_allocation_cap))
);
let opentitan = OpenTitan {
gpio: gpio,
led: led,
console: console,
alarm: alarm,
hmac,
lldb: lldb,
i2c_master,
debug_uart: debug_uart,
storage_manager: storage_manager,
};
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,
FAULT_RESPONSE,
&process_mgmt_cap,
)
.unwrap_or_else(|err| {
debug!("Error loading processes!");
debug!("{:?}", err);
});
debug!("OpenTitan initialisation complete. Entering main loop");
debug!("Woo Tock!");
let scheduler = components::sched::priority::PriorityComponent::new(board_kernel).finalize(());
board_kernel.kernel_loop(&opentitan, chip, None, scheduler, &main_loop_cap);
}