mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-11 07:40:10 +00:00
584 lines
23 KiB
Rust
584 lines
23 KiB
Rust
#![cfg_attr(not(test), no_main)]
|
|
#![cfg_attr(not(test), no_std)]
|
|
|
|
#[macro_use]
|
|
mod args;
|
|
use args::{KernelArgument, KernelArguments};
|
|
|
|
#[cfg_attr(feature = "atsama5d27", path = "platform/atsama5d27/debug.rs")]
|
|
mod debug;
|
|
mod fonts;
|
|
#[cfg(feature = "secboot")]
|
|
mod secboot;
|
|
|
|
#[cfg_attr(feature = "atsama5d27", path = "platform/atsama5d27/asm.rs")]
|
|
mod asm;
|
|
mod bootconfig;
|
|
#[cfg_attr(feature = "atsama5d27", path = "platform/atsama5d27/consts.rs")]
|
|
mod consts;
|
|
mod minielf;
|
|
#[cfg(feature = "resume")]
|
|
mod murmur3;
|
|
mod phase1;
|
|
mod phase2;
|
|
mod platform;
|
|
#[cfg(feature = "swap")]
|
|
pub mod swap;
|
|
|
|
use core::{mem, ptr, slice};
|
|
|
|
use asm::*;
|
|
use bootconfig::BootConfig;
|
|
use consts::*;
|
|
pub use loader::*;
|
|
use minielf::*;
|
|
use phase1::{InitialProcess, phase_1};
|
|
use phase2::{ProgramDescription, phase_2};
|
|
#[cfg(feature = "swap")]
|
|
use platform::SwapHal;
|
|
|
|
const WORD_SIZE: usize = mem::size_of::<usize>();
|
|
pub const SIGBLOCK_SIZE: usize = 0x1000;
|
|
const STACK_PAGE_COUNT: usize = 8;
|
|
|
|
const VDBG: bool = false; // verbose debug
|
|
const VVDBG: bool = false; // very verbose debug
|
|
const SDBG: bool = false; // swap debug
|
|
|
|
#[cfg(test)]
|
|
mod test;
|
|
|
|
#[repr(C)]
|
|
pub struct MemoryRegionExtra {
|
|
start: u32,
|
|
length: u32,
|
|
name: u32,
|
|
padding: u32,
|
|
}
|
|
|
|
/// A single RISC-V page table entry. In order to resolve an address,
|
|
/// we need two entries: the top level, followed by the lower level.
|
|
#[repr(C)]
|
|
pub struct PageTable {
|
|
entries: [usize; PAGE_SIZE / WORD_SIZE],
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
pub enum IniType {
|
|
// RAM
|
|
IniE,
|
|
// XIP
|
|
IniF,
|
|
// Swap
|
|
IniS,
|
|
}
|
|
|
|
/// Entrypoint
|
|
/// This makes the program self-sufficient by setting up memory page assignment
|
|
/// and copying the arguments to RAM.
|
|
/// Assume the bootloader has already set up the stack to point to the end of RAM.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// This function is safe to call exactly once.
|
|
#[export_name = "rust_entry"]
|
|
pub unsafe extern "C" fn rust_entry(signed_buffer: *const usize, signature: u32) -> ! {
|
|
#[cfg(all(feature = "cramium-soc", not(feature = "verilator-only")))]
|
|
let perclk_freq = crate::platform::early_init(); // sets up PLLs so we're not running at 16MHz...
|
|
// need to make this "official" for NTO, the feature flag combo below works around some simulation config
|
|
// conflicts.
|
|
#[cfg(all(feature = "verilator-only", not(feature = "cramium-mpw")))]
|
|
platform::coreuser_config();
|
|
|
|
#[cfg(not(all(feature = "cramium-soc", not(feature = "verilator-only"))))]
|
|
let perclk_freq = 0;
|
|
|
|
#[cfg(all(feature = "cramium-soc", feature = "updates"))]
|
|
crate::platform::process_update(perclk_freq);
|
|
|
|
// initially validate the whole image on disk (including kernel args)
|
|
// kernel args must be validated because tampering with them can change critical assumptions about
|
|
// how data is loaded into memory
|
|
#[cfg(feature = "secboot")]
|
|
let mut fs_prehash = [0u8; 64];
|
|
#[cfg(not(feature = "secboot"))]
|
|
let fs_prehash = [0u8; 64];
|
|
#[cfg(feature = "secboot")]
|
|
if !secboot::validate_xous_img(signed_buffer as *const u32, &mut fs_prehash) {
|
|
loop {}
|
|
};
|
|
// the kernel arg buffer is SIG_BLOCK_SIZE into the signed region
|
|
let arg_buffer = (signed_buffer as u32 + SIGBLOCK_SIZE as u32) as *const usize;
|
|
|
|
// perhaps later on in these sequences, individual sub-images may be validated
|
|
// against sub-signatures; or the images may need to be re-validated after loading
|
|
// into RAM, if we have concerns about RAM glitching as an attack surface (I don't think we do...).
|
|
// But for now, the basic "validate everything as a blob" is perhaps good enough to
|
|
// armor code-at-rest against front-line patching attacks.
|
|
let kab = KernelArguments::new(arg_buffer);
|
|
boot_sequence(kab, signature, fs_prehash, perclk_freq);
|
|
}
|
|
|
|
fn boot_sequence(args: KernelArguments, _signature: u32, fs_prehash: [u8; 64], _perclk_freq: u32) -> ! {
|
|
// Store the initial boot config on the stack. We don't know
|
|
// where in heap this memory will go.
|
|
#[allow(clippy::cast_ptr_alignment)] // This test only works on 32-bit systems
|
|
#[cfg(feature = "platform-tests")]
|
|
platform::platform_tests();
|
|
|
|
let mut cfg = BootConfig { base_addr: args.base as *const usize, args, ..Default::default() };
|
|
#[cfg(feature = "swap")]
|
|
println!("Size of BootConfig: {:x}", core::mem::size_of::<BootConfig>());
|
|
read_initial_config(&mut cfg);
|
|
|
|
#[cfg(feature = "swap")]
|
|
{
|
|
cfg.swap_hal = SwapHal::new(&cfg, _perclk_freq);
|
|
read_swap_config(&mut cfg);
|
|
}
|
|
|
|
// check to see if we are recovering from a clean suspend or not
|
|
#[cfg(feature = "resume")]
|
|
let (clean, was_forced_suspend, susres_pid) = check_resume(&mut cfg);
|
|
#[cfg(not(feature = "resume"))]
|
|
let clean = {
|
|
// cold boot path
|
|
println!("No suspend marker found, doing a cold boot!");
|
|
#[cfg(feature = "simulation-only")]
|
|
println!("Configured for simulation. Skipping RAM clear!");
|
|
#[cfg(not(any(feature = "cramium-soc", feature = "cramium-fpga")))]
|
|
// cramium target clears RAM with assembly routine on boot
|
|
clear_ram(&mut cfg);
|
|
phase_1(&mut cfg);
|
|
phase_2(&mut cfg, &fs_prehash);
|
|
#[cfg(any(feature = "debug-print", feature = "swap"))]
|
|
if VDBG || SDBG {
|
|
check_load(&mut cfg);
|
|
}
|
|
println!("done initializing for cold boot.");
|
|
false
|
|
};
|
|
#[cfg(feature = "resume")]
|
|
if !clean {
|
|
// cold boot path
|
|
println!("No suspend marker found, doing a cold boot!");
|
|
clear_ram(&mut cfg);
|
|
phase_1(&mut cfg);
|
|
phase_2(&mut cfg, &fs_prehash);
|
|
#[cfg(any(feature = "debug-print", feature = "swap"))]
|
|
if VDBG || SDBG {
|
|
check_load(&mut cfg);
|
|
}
|
|
println!("done initializing for cold boot.");
|
|
} else {
|
|
// resume path
|
|
use utralib::generated::*;
|
|
// flip my self-power-on switch: otherwise, I might turn off before the whole sequence is finished.
|
|
let mut power_csr = CSR::new(utra::power::HW_POWER_BASE as *mut u32);
|
|
power_csr.rmwf(utra::power::POWER_STATE, 1);
|
|
power_csr.rmwf(utra::power::POWER_SELF, 1);
|
|
|
|
// TRNG virtual memory mapping already set up, but we pump values out just to make sure
|
|
// the pipeline is fresh. Simulations show this isn't necessary, but I feel paranoid;
|
|
// I worry a subtle bug in the reset logic could leave deterministic values in the pipeline.
|
|
let trng_csr = CSR::new(utra::trng_kernel::HW_TRNG_KERNEL_BASE as *mut u32);
|
|
for _ in 0..4 {
|
|
while trng_csr.rf(utra::trng_kernel::URANDOM_VALID_URANDOM_VALID) == 0 {}
|
|
trng_csr.rf(utra::trng_kernel::URANDOM_URANDOM);
|
|
}
|
|
// turn on the kernel UART.
|
|
let mut uart_csr = CSR::new(utra::uart::HW_UART_BASE as *mut u32);
|
|
uart_csr.rmwf(utra::uart::EV_ENABLE_RX, 1);
|
|
|
|
// setup the `susres` register for a resume
|
|
let mut resume_csr = CSR::new(utra::susres::HW_SUSRES_BASE as *mut u32);
|
|
// set the resume marker for the SUSRES server, noting the forced suspend status
|
|
if was_forced_suspend {
|
|
resume_csr.wo(
|
|
utra::susres::STATE,
|
|
resume_csr.ms(utra::susres::STATE_RESUME, 1)
|
|
| resume_csr.ms(utra::susres::STATE_WAS_FORCED, 1),
|
|
);
|
|
} else {
|
|
resume_csr.wfo(utra::susres::STATE_RESUME, 1);
|
|
}
|
|
resume_csr.wfo(utra::susres::CONTROL_PAUSE, 1); // ensure that the ticktimer is paused before resuming
|
|
resume_csr.wfo(utra::susres::EV_ENABLE_SOFT_INT, 1); // ensure that the soft interrupt is enabled for the kernel to kick
|
|
println!("clean suspend marker found, doing a resume!");
|
|
|
|
// trigger the interrupt; it's not immediately handled, but rather checked later on by the kernel on
|
|
// clean resume
|
|
resume_csr.wfo(utra::susres::INTERRUPT_INTERRUPT, 1);
|
|
}
|
|
|
|
// condense debug and resume arguments into a single register, so we have space for XPT
|
|
let debug_resume = if cfg.debug { 0x1 } else { 0x0 } | if clean { 0x2 } else { 0x0 };
|
|
if !clean {
|
|
// The MMU should be set up now, and memory pages assigned to their
|
|
// respective processes.
|
|
let krn_struct_start = cfg.sram_start as usize + cfg.sram_size - cfg.init_size + cfg.swap_offset;
|
|
#[cfg(feature = "swap")]
|
|
if SDBG && VDBG {
|
|
const CHUNK_SIZE: usize = 2;
|
|
// activate to debug stack smashes. RPT should be 0's here (or at least valid PIDs) if stack did
|
|
// not overflow.
|
|
for (_i, _r) in cfg.runtime_page_tracker[cfg.runtime_page_tracker.len() - 512..]
|
|
.chunks(CHUNK_SIZE)
|
|
.enumerate()
|
|
{
|
|
println!(" rpt {:08x}: {:02x?}", cfg.runtime_page_tracker.len() - 512 + _i * CHUNK_SIZE, _r);
|
|
}
|
|
}
|
|
// Add a static check for stack overflow, using a heuristic that the last 64 entries of the RPT
|
|
// ought to be a valid PID. A stack smash is likely to write something that does not obey
|
|
// this heuristic within that range (any stack-stored pointer, for example, will break this).
|
|
for &check in cfg.runtime_page_tracker[cfg.runtime_page_tracker.len() - 64..].iter() {
|
|
assert!(
|
|
// use .to_le() to access the structure because SwapAlloc can either be a u8 or a composite
|
|
// type, and .to_le() can do the right thing for both cases.
|
|
check.to_le() <= cfg.processes.len() as u8,
|
|
"RPT looks corrupted, suspect stack overflow in loader. Increase GUARD_MEMORY_BYTES!"
|
|
);
|
|
}
|
|
// compute the virtual addresses of all of these "manually"
|
|
let arg_offset = cfg.args.base as usize - krn_struct_start + KERNEL_ARGUMENT_OFFSET;
|
|
let ip_offset = cfg.processes.as_ptr() as usize - krn_struct_start + KERNEL_ARGUMENT_OFFSET;
|
|
let rpt_offset =
|
|
cfg.runtime_page_tracker.as_ptr() as usize - krn_struct_start + KERNEL_ARGUMENT_OFFSET;
|
|
let xpt_offset = cfg.extra_page_tracker.as_ptr() as usize - krn_struct_start + KERNEL_ARGUMENT_OFFSET;
|
|
#[cfg(not(feature = "atsama5d27"))]
|
|
let _tt_addr = { cfg.processes[0].satp };
|
|
#[cfg(feature = "atsama5d27")]
|
|
let _tt_addr = { cfg.processes[0].ttbr0 };
|
|
println!(
|
|
"Jumping to kernel @ {:08x} with map @ {:08x} and stack @ {:08x} (kargs: {:08x}, ip: {:08x}, rpt: {:08x}, xpt: {:08x})",
|
|
cfg.processes[0].entrypoint,
|
|
_tt_addr,
|
|
cfg.processes[0].sp,
|
|
arg_offset,
|
|
ip_offset,
|
|
rpt_offset,
|
|
xpt_offset,
|
|
);
|
|
|
|
// save a copy of the computed kernel registers at the bottom of the page reserved
|
|
// for the bootloader stack. Note there is no stack-smash protection for these arguments,
|
|
// so we're definitely vulnerable to e.g. buffer overrun attacks in the early bootloader.
|
|
//
|
|
// Probably the right long-term solution to this is to turn all the bootloader "loading"
|
|
// actions into "verify" actions during a clean resume (right now we just don't run them),
|
|
// so that attempts to mess with the args during a resume can't lead to overwriting
|
|
// critical parameters like these kernel arguments.
|
|
#[cfg(not(feature = "atsama5d27"))]
|
|
unsafe {
|
|
let backup_args: *mut [u32; 8] = BACKUP_ARGS_ADDR as *mut [u32; 8];
|
|
(*backup_args)[0] = arg_offset as u32;
|
|
(*backup_args)[1] = ip_offset as u32;
|
|
(*backup_args)[2] = rpt_offset as u32;
|
|
(*backup_args)[3] = cfg.processes[0].satp as u32;
|
|
(*backup_args)[4] = cfg.processes[0].entrypoint as u32;
|
|
(*backup_args)[5] = cfg.processes[0].sp as u32;
|
|
(*backup_args)[6] = if cfg.debug { 1 } else { 0 };
|
|
(*backup_args)[7] = xpt_offset as u32;
|
|
#[cfg(feature = "debug-print")]
|
|
{
|
|
if VDBG {
|
|
println!("Backup kernel args:");
|
|
for &arg in (*backup_args).iter() {
|
|
println!("0x{:08x}", arg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "atsama5d27"))]
|
|
{
|
|
#[cfg(not(any(feature = "cramium-soc", feature = "cramium-fpga")))]
|
|
{
|
|
// uart mux only exists on the FPGA variant
|
|
use utralib::generated::*;
|
|
let mut gpio_csr = CSR::new(utra::gpio::HW_GPIO_BASE as *mut u32);
|
|
gpio_csr.wfo(utra::gpio::UARTSEL_UARTSEL, 1); // patch us over to a different UART for debug (1=LOG 2=APP, 0=KERNEL(hw reset default))
|
|
}
|
|
|
|
start_kernel(
|
|
arg_offset,
|
|
ip_offset,
|
|
rpt_offset,
|
|
xpt_offset,
|
|
cfg.processes[0].satp,
|
|
cfg.processes[0].entrypoint,
|
|
cfg.processes[0].sp,
|
|
debug_resume,
|
|
);
|
|
}
|
|
|
|
#[cfg(feature = "atsama5d27")]
|
|
unsafe {
|
|
start_kernel(
|
|
cfg.processes[0].sp,
|
|
cfg.processes[0].ttbr0,
|
|
cfg.processes[0].entrypoint,
|
|
arg_offset,
|
|
ip_offset,
|
|
rpt_offset,
|
|
xpt_offset,
|
|
debug_resume,
|
|
)
|
|
}
|
|
} else {
|
|
#[cfg(feature = "resume")]
|
|
unsafe {
|
|
let backup_args: *mut [u32; 8] = BACKUP_ARGS_ADDR as *mut [u32; 8];
|
|
#[cfg(feature = "debug-print")]
|
|
{
|
|
println!("Using backed up kernel args:");
|
|
for &arg in (*backup_args).iter() {
|
|
println!("0x{:08x}", arg);
|
|
}
|
|
}
|
|
let satp = ((*backup_args)[3] as usize) & 0x803F_FFFF | (((susres_pid as usize) & 0x1FF) << 22);
|
|
//let satp = (*backup_args)[3];
|
|
println!(
|
|
"Adjusting SATP to the sures process. Was: 0x{:08x} now: 0x{:08x}",
|
|
(*backup_args)[3],
|
|
satp
|
|
);
|
|
|
|
#[cfg(not(feature = "renode-bypass"))]
|
|
{
|
|
use utralib::generated::*;
|
|
let mut gpio_csr = CSR::new(utra::gpio::HW_GPIO_BASE as *mut u32);
|
|
gpio_csr.wfo(utra::gpio::UARTSEL_UARTSEL, 0); // patch us over to a different UART for debug (1=LOG 2=APP, 0=KERNEL(default))
|
|
}
|
|
|
|
start_kernel(
|
|
(*backup_args)[0] as usize,
|
|
(*backup_args)[1] as usize,
|
|
(*backup_args)[2] as usize,
|
|
(*backup_args)[7] as usize,
|
|
satp as usize,
|
|
(*backup_args)[4] as usize,
|
|
(*backup_args)[5] as usize,
|
|
debug_resume,
|
|
);
|
|
}
|
|
#[cfg(not(feature = "resume"))]
|
|
panic!("Unreachable code executed");
|
|
}
|
|
}
|
|
|
|
pub fn read_initial_config(cfg: &mut BootConfig) {
|
|
let args = cfg.args;
|
|
let mut i = args.iter();
|
|
let xarg = i.next().expect("couldn't read initial tag");
|
|
if xarg.name != u32::from_le_bytes(*b"XArg") || xarg.size != 20 {
|
|
panic!("XArg wasn't first tag, or was invalid size");
|
|
}
|
|
cfg.sram_start = xarg.data[2] as *mut usize;
|
|
cfg.sram_size = xarg.data[3] as usize;
|
|
#[cfg(feature = "swap")]
|
|
if SDBG {
|
|
println!("XAarg // sram_start: {:x}, sram_size: {:x}", cfg.sram_start as usize, cfg.sram_size);
|
|
}
|
|
|
|
let mut kernel_seen = false;
|
|
let mut init_seen = false;
|
|
|
|
for tag in i {
|
|
if tag.name == u32::from_le_bytes(*b"MREx") {
|
|
cfg.regions = unsafe {
|
|
slice::from_raw_parts(
|
|
tag.data.as_ptr() as *const MemoryRegionExtra,
|
|
tag.size as usize / mem::size_of::<MemoryRegionExtra>(),
|
|
)
|
|
};
|
|
} else if tag.name == u32::from_le_bytes(*b"Bflg") {
|
|
let boot_flags = tag.data[0];
|
|
if boot_flags & 1 != 0 {
|
|
cfg.no_copy = true;
|
|
}
|
|
if boot_flags & (1 << 1) != 0 {
|
|
cfg.base_addr = core::ptr::null::<usize>();
|
|
}
|
|
if boot_flags & (1 << 2) != 0 {
|
|
cfg.debug = true;
|
|
}
|
|
} else if tag.name == u32::from_le_bytes(*b"XKrn") {
|
|
assert!(!kernel_seen, "kernel appears twice");
|
|
assert!(tag.size as usize == mem::size_of::<ProgramDescription>(), "invalid XKrn size");
|
|
kernel_seen = true;
|
|
} else if tag.name == u32::from_le_bytes(*b"IniE") || tag.name == u32::from_le_bytes(*b"IniF") {
|
|
assert!(tag.size >= 4, "invalid Init size");
|
|
init_seen = true;
|
|
cfg.init_process_count += 1;
|
|
}
|
|
#[cfg(feature = "swap")]
|
|
if tag.name == u32::from_le_bytes(*b"Swap") {
|
|
// safety: the image creator guarantees this data is aligned and initialized properly
|
|
cfg.swap = Some(unsafe { &*(tag.data.as_ptr() as *const crate::swap::SwapDescriptor) });
|
|
}
|
|
}
|
|
|
|
assert!(kernel_seen, "no kernel definition");
|
|
assert!(init_seen, "no initial programs found");
|
|
}
|
|
|
|
#[cfg(feature = "swap")]
|
|
pub fn read_swap_config(cfg: &mut BootConfig) {
|
|
// Read in the swap arguments: should be located at beginning of the encrypted image in swap.
|
|
let page0 = cfg.swap_hal.as_mut().unwrap().decrypt_src_page_at(0x0);
|
|
let swap_args = KernelArguments::new(page0.as_ptr() as *const usize);
|
|
for tag in swap_args.iter() {
|
|
if tag.name == u32::from_le_bytes(*b"IniS") {
|
|
assert!(tag.size >= 4, "invalid Init size");
|
|
cfg.init_process_count += 1;
|
|
} else if tag.name == u32::from_le_bytes(*b"XArg") {
|
|
// these are actually specified inside the `Swap` arg, this is just
|
|
// a mirror because this argument is added by default by the image creator
|
|
if SDBG {
|
|
println!("Swap start: {:x}", tag.data[2]);
|
|
println!("Swap size: {:x}", tag.data[3]);
|
|
}
|
|
} else {
|
|
println!("Unhandled argument in swap: {:x}", tag.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Checks a reserved area of RAM for a pattern with a pre-defined mathematical
|
|
/// relationship. The purpose is to detect if we have a "clean suspend", or if
|
|
/// we've rebooted from a corrupt/cold RAM state.
|
|
#[cfg(feature = "resume")]
|
|
fn check_resume(cfg: &mut BootConfig) -> (bool, bool, u32) {
|
|
use utralib::generated::*;
|
|
const WORDS_PER_SECTOR: usize = 128;
|
|
const NUM_SECTORS: usize = 8;
|
|
const WORDS_PER_PAGE: usize = PAGE_SIZE / 4;
|
|
|
|
let suspend_marker = cfg.sram_start as usize + cfg.sram_size - GUARD_MEMORY_BYTES;
|
|
let marker: *mut [u32; WORDS_PER_PAGE] = suspend_marker as *mut [u32; WORDS_PER_PAGE];
|
|
|
|
let boot_seed = CSR::new(utra::seed::HW_SEED_BASE as *mut u32);
|
|
let seed0 = boot_seed.r(utra::seed::SEED0);
|
|
let seed1 = boot_seed.r(utra::seed::SEED1);
|
|
let was_forced_suspend: bool = if unsafe { (*marker)[0] } != 0 { true } else { false };
|
|
|
|
let mut clean = true;
|
|
let mut hashbuf: [u32; WORDS_PER_SECTOR - 1] = [0; WORDS_PER_SECTOR - 1];
|
|
let mut index: usize = 0;
|
|
let mut pid: u32 = 0;
|
|
for sector in 0..NUM_SECTORS {
|
|
for i in 0..hashbuf.len() {
|
|
hashbuf[i] = unsafe { (*marker)[index * WORDS_PER_SECTOR + i] };
|
|
}
|
|
// sector 0 contains the boot seeds, which we replace with our own as read out from our FPGA before
|
|
// computing the hash it also contains the PID of the suspend/resume process manager, which we
|
|
// need to inject into the SATP
|
|
if sector == 0 {
|
|
hashbuf[1] = seed0;
|
|
hashbuf[2] = seed1;
|
|
pid = hashbuf[3];
|
|
}
|
|
let hash = crate::murmur3::murmur3_32(&hashbuf, 0);
|
|
if hash != unsafe { (*marker)[(index + 1) * WORDS_PER_SECTOR - 1] } {
|
|
println!("* computed 0x{:08x} - stored 0x{:08x}", hash, unsafe {
|
|
(*marker)[(index + 1) * (WORDS_PER_SECTOR) - 1]
|
|
});
|
|
clean = false;
|
|
} else {
|
|
println!(" computed 0x{:08x} - match", hash);
|
|
}
|
|
index += 1;
|
|
}
|
|
// zero out the clean suspend marker, so if something goes wrong during resume we don't try to resume
|
|
// again
|
|
for i in 0..WORDS_PER_PAGE {
|
|
unsafe {
|
|
(*marker)[i] = 0;
|
|
}
|
|
}
|
|
|
|
(clean, was_forced_suspend, pid)
|
|
}
|
|
|
|
/// Clears all of RAM. This is a must for systems that have suspend-to-RAM for security.
|
|
/// It is configured to be skipped in simulation only, to accelerate the simulation times
|
|
/// since we can initialize the RAM to zero in simulation.
|
|
#[cfg(all(not(feature = "simulation-only"), not(feature = "cramium-soc")))]
|
|
fn clear_ram(cfg: &mut BootConfig) {
|
|
// clear RAM on a cold boot.
|
|
// RAM is persistent and battery-backed. This means secret material could potentially
|
|
// stay there forever, if not explicitly cleared. This clear adds a couple seconds
|
|
// to a cold boot, but it's probably worth it. Note that it doesn't happen on a suspend/resume.
|
|
let ram: *mut u32 = cfg.sram_start as *mut u32;
|
|
#[cfg(feature = "swap")]
|
|
let clear_limit = ((2 * 4096 + core::mem::size_of::<BootConfig>()) + 4095) & !4095;
|
|
#[cfg(not(feature = "swap"))]
|
|
let clear_limit = ((4096 + core::mem::size_of::<BootConfig>()) + 4095) & !4095;
|
|
if VDBG {
|
|
println!("Stack clearing limit: {:x}", clear_limit);
|
|
}
|
|
unsafe {
|
|
for addr in 0..(cfg.sram_size - clear_limit) / 4 {
|
|
// 8k is reserved for our own stack
|
|
ram.add(addr).write_volatile(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub unsafe fn bzero<T>(mut sbss: *mut T, ebss: *mut T) {
|
|
if VDBG {
|
|
println!("ZERO: {:08x} - {:08x}", sbss as usize, ebss as usize);
|
|
}
|
|
while sbss < ebss {
|
|
// NOTE(volatile) to prevent this from being transformed into `memclr`
|
|
// which can create an accidental dependency on libc.
|
|
ptr::write_volatile(sbss, mem::zeroed());
|
|
sbss = sbss.offset(1);
|
|
}
|
|
}
|
|
|
|
/// This function allows us to check the final loader results
|
|
/// It will print to the console the first 32 bytes of each loaded
|
|
/// region top/bottom, based upon extractions from the page table.
|
|
#[cfg(any(feature = "debug-print", feature = "swap"))]
|
|
fn check_load(cfg: &mut BootConfig) {
|
|
let args = cfg.args;
|
|
|
|
// This is the offset in RAM where programs are loaded from.
|
|
println!("\n\nCHECKING LOAD");
|
|
|
|
// Go through all Init processes and the kernel, setting up their
|
|
// page tables and mapping memory to them.
|
|
let mut pid = 2;
|
|
for tag in args.iter() {
|
|
if tag.name == u32::from_le_bytes(*b"IniE") {
|
|
let inie = MiniElf::new(&tag);
|
|
println!("\n\nChecking IniE region");
|
|
inie.check(cfg, inie.load_offset as usize, pid, IniType::IniE);
|
|
pid += 1;
|
|
} else if tag.name == u32::from_le_bytes(*b"IniF") {
|
|
let inif = MiniElf::new(&tag);
|
|
println!("\n\nChecking IniF region");
|
|
inif.check(cfg, inif.load_offset as usize, pid, IniType::IniF);
|
|
pid += 1;
|
|
} else if tag.name == u32::from_le_bytes(*b"IniS") {
|
|
let inis = MiniElf::new(&tag);
|
|
println!("\n\nChecking IniS region");
|
|
inis.check(cfg, inis.load_offset as usize, pid, IniType::IniS);
|
|
pid += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Install a panic handler when not running tests.
|
|
#[cfg(all(not(test), not(feature = "atsama5d27")))]
|
|
mod panic_handler {
|
|
use core::panic::PanicInfo;
|
|
#[panic_handler]
|
|
fn handle_panic(_arg: &PanicInfo) -> ! {
|
|
crate::println!("{}", _arg);
|
|
loop {}
|
|
}
|
|
}
|