#![no_std] #![feature(panic_info_message)] extern crate crc; extern crate byteorder; extern crate smoltcp; #[macro_use] extern crate board_misoc; extern crate riscv; use core::{ptr, slice, convert::TryFrom}; use crc::crc32; use byteorder::{ByteOrder, LittleEndian}; use board_misoc::{ident, cache, sdram, config, boot, mem as board_mem}; #[cfg(has_slave_fpga_cfg)] use board_misoc::slave_fpga; #[cfg(has_ethmac)] use board_misoc::{clock, ethmac, net_settings}; use board_misoc::uart_console::Console; use riscv::register::{mcause, mepc, mtval}; use smoltcp::iface::SocketStorage; use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address}; fn check_integrity() -> bool { extern { static _begin: u8; static _end: u8; static _crc: u32; } unsafe { let length = &_end as *const u8 as usize - &_begin as *const u8 as usize; let bootloader = slice::from_raw_parts(&_begin as *const u8, length); crc32::checksum_ieee(bootloader) == _crc } } fn memory_test(total: &mut usize, wrong: &mut usize) -> bool { const MEMORY: *mut u32 = board_mem::MAIN_RAM_BASE as *mut u32; *total = 0; *wrong = 0; macro_rules! test { ( $prepare:stmt; for $i:ident in ($range:expr) { MEMORY[$index:expr] = $data:expr } ) => ({ $prepare for $i in $range { unsafe { ptr::write_volatile(MEMORY.offset($index as isize), $data) }; *total += 1; } cache::flush_cpu_dcache(); cache::flush_l2_cache(); $prepare for $i in $range { if unsafe { ptr::read_volatile(MEMORY.offset($index as isize)) } != $data { *wrong += 1; } } }) } fn prng32(seed: &mut u32) -> u32 { *seed = u32::wrapping_add(u32::wrapping_mul(1664525, *seed), 1013904223); *seed } fn prng16(seed: &mut u16) -> u16 { *seed = u16::wrapping_add(u16::wrapping_mul(25173, *seed), 13849); *seed } for _ in 0..4 { // Test data bus test!((); for i in (0..0x100) { MEMORY[i] = 0xAAAAAAAA }); test!((); for i in (0..0x100) { MEMORY[i] = 0x55555555 }); // Test counter addressing with random data test!(let mut seed = 0; for i in (0..0x100000) { MEMORY[i] = prng32(&mut seed) }); // Test random addressing with counter data test!(let mut seed = 0; for i in (0..0x10000) { MEMORY[prng16(&mut seed)] = i }); } *wrong == 0 } fn startup() -> bool { if check_integrity() { println!("Bootloader CRC passed"); } else { println!("Bootloader CRC failed"); return false } println!("Gateware ident {}", ident::read(&mut [0; 64])); println!("Initializing SDRAM..."); if unsafe { sdram::init(Some(&mut Console)) } { println!("SDRAM initialized"); } else { println!("SDRAM initialization failed"); return false } let (mut total, mut wrong) = (0, 0); if memory_test(&mut total, &mut wrong) { println!("Memory test passed"); } else { println!("Memory test failed ({}/{} words incorrect)", wrong, total); return false } true } #[cfg(has_slave_fpga_cfg)] fn load_slave_fpga() { println!("Loading slave FPGA gateware..."); const GATEWARE: *mut u8 = board_misoc::csr::CONFIG_SLAVE_FPGA_GATEWARE as *mut u8; let header = unsafe { slice::from_raw_parts(GATEWARE, 8) }; let magic = LittleEndian::read_u32(&header[0..]); let length = LittleEndian::read_u32(&header[4..]) as usize; println!(" magic: 0x{:08x}, length: 0x{:08x}", magic, length); if magic != 0x5352544d { println!(" ...Error: bad magic"); return } if length > 0x220000 { println!(" ...Error: too long (corrupted?)"); return } let payload = unsafe { slice::from_raw_parts(GATEWARE.offset(8), length) }; if let Err(e) = slave_fpga::prepare() { println!(" ...Error during preparation: {}", e); return } if let Err(e) = slave_fpga::input(payload) { println!(" ...Error during loading: {}", e); return } if let Err(e) = slave_fpga::startup() { println!(" ...Error during startup: {}", e); return } println!(" ...done"); } fn flash_boot() { const FIRMWARE: *mut u8 = board_mem::FLASH_BOOT_ADDRESS as *mut u8; const MAIN_RAM: *mut u8 = board_mem::MAIN_RAM_BASE as *mut u8; println!("Booting from flash..."); let header = unsafe { slice::from_raw_parts(FIRMWARE, 8) }; let length = LittleEndian::read_u32(&header[0..]) as usize; let expected_crc = LittleEndian::read_u32(&header[4..]); if length == 0 || length == 0xffffffff { println!("No firmware present"); return } else if length > 4 * 1024 * 1024 { println!("Firmware too large (is it corrupted?)"); return } let firmware_in_flash = unsafe { slice::from_raw_parts(FIRMWARE.offset(8), length) }; let actual_crc_flash = crc32::checksum_ieee(firmware_in_flash); if actual_crc_flash == expected_crc { let firmware_in_sdram = unsafe { slice::from_raw_parts_mut(MAIN_RAM, length) }; firmware_in_sdram.copy_from_slice(firmware_in_flash); let actual_crc_sdram = crc32::checksum_ieee(firmware_in_sdram); if actual_crc_sdram == expected_crc { println!("Starting firmware."); unsafe { boot::jump(MAIN_RAM as usize) } } else { println!("Firmware CRC failed in SDRAM (actual {:08x}, expected {:08x})", actual_crc_sdram, expected_crc); } } else { println!("Firmware CRC failed in flash (actual {:08x}, expected {:08x})", actual_crc_flash, expected_crc); } } #[cfg(has_ethmac)] enum NetConnState { WaitCommand, FirmwareLength(usize, u8), FirmwareDownload(usize, usize), FirmwareWaitO, FirmwareWaitK, #[cfg(has_slave_fpga_cfg)] GatewareLength(usize, u8), #[cfg(has_slave_fpga_cfg)] GatewareDownload(usize, usize), #[cfg(has_slave_fpga_cfg)] GatewareWaitO, #[cfg(has_slave_fpga_cfg)] GatewareWaitK } #[cfg(has_ethmac)] struct NetConn { state: NetConnState, firmware_downloaded: bool } #[cfg(has_ethmac)] impl NetConn { pub fn new() -> NetConn { NetConn { state: NetConnState::WaitCommand, firmware_downloaded: false } } pub fn reset(&mut self) { self.state = NetConnState::WaitCommand; self.firmware_downloaded = false; } // buf must contain at least one byte // this function must consume at least one byte fn input_partial(&mut self, buf: &[u8], mut boot_callback: impl FnMut()) -> Result { match self.state { NetConnState::WaitCommand => { match buf[0] { b'F' => { println!("Received firmware load command"); self.state = NetConnState::FirmwareLength(0, 0); Ok(1) }, #[cfg(has_slave_fpga_cfg)] b'G' => { println!("Received gateware load command"); self.state = NetConnState::GatewareLength(0, 0); Ok(1) } b'B' => { if self.firmware_downloaded { println!("Received boot command"); boot_callback(); self.state = NetConnState::WaitCommand; Ok(1) } else { println!("Received boot command, but no firmware downloaded"); Err(()) } }, _ => { println!("Received unknown netboot command: 0x{:02x}", buf[0]); Err(()) } } }, NetConnState::FirmwareLength(firmware_length, recv_bytes) => { let firmware_length = (firmware_length << 8) | (buf[0] as usize); let recv_bytes = recv_bytes + 1; if recv_bytes == 4 { self.state = NetConnState::FirmwareDownload(firmware_length, 0); } else { self.state = NetConnState::FirmwareLength(firmware_length, recv_bytes); } Ok(1) }, NetConnState::FirmwareDownload(firmware_length, recv_bytes) => { let max_length = firmware_length - recv_bytes; let buf = if buf.len() > max_length { &buf[..max_length] } else { &buf[..] }; let length = buf.len(); let firmware_in_sdram = unsafe { slice::from_raw_parts_mut((board_mem::MAIN_RAM_BASE + recv_bytes) as *mut u8, length) }; firmware_in_sdram.copy_from_slice(buf); let recv_bytes = recv_bytes + length; if recv_bytes == firmware_length { self.state = NetConnState::FirmwareWaitO; Ok(length) } else { self.state = NetConnState::FirmwareDownload(firmware_length, recv_bytes); Ok(length) } }, NetConnState::FirmwareWaitO => { if buf[0] == b'O' { self.state = NetConnState::FirmwareWaitK; Ok(1) } else { println!("End-of-firmware confirmation failed"); Err(()) } }, NetConnState::FirmwareWaitK => { if buf[0] == b'K' { println!("Firmware successfully downloaded"); self.state = NetConnState::WaitCommand; self.firmware_downloaded = true; Ok(1) } else { println!("End-of-firmware confirmation failed"); Err(()) } } #[cfg(has_slave_fpga_cfg)] NetConnState::GatewareLength(gateware_length, recv_bytes) => { let gateware_length = (gateware_length << 8) | (buf[0] as usize); let recv_bytes = recv_bytes + 1; if recv_bytes == 4 { if let Err(e) = slave_fpga::prepare() { println!(" Error during slave FPGA preparation: {}", e); return Err(()) } self.state = NetConnState::GatewareDownload(gateware_length, 0); } else { self.state = NetConnState::GatewareLength(gateware_length, recv_bytes); } Ok(1) }, #[cfg(has_slave_fpga_cfg)] NetConnState::GatewareDownload(gateware_length, recv_bytes) => { let max_length = gateware_length - recv_bytes; let buf = if buf.len() > max_length { &buf[..max_length] } else { &buf[..] }; let length = buf.len(); if let Err(e) = slave_fpga::input(buf) { println!("Error during slave FPGA loading: {}", e); return Err(()) } let recv_bytes = recv_bytes + length; if recv_bytes == gateware_length { self.state = NetConnState::GatewareWaitO; Ok(length) } else { self.state = NetConnState::GatewareDownload(gateware_length, recv_bytes); Ok(length) } }, #[cfg(has_slave_fpga_cfg)] NetConnState::GatewareWaitO => { if buf[0] == b'O' { self.state = NetConnState::GatewareWaitK; Ok(1) } else { println!("End-of-gateware confirmation failed"); Err(()) } }, #[cfg(has_slave_fpga_cfg)] NetConnState::GatewareWaitK => { if buf[0] == b'K' { if let Err(e) = slave_fpga::startup() { println!("Error during slave FPGA startup: {}", e); return Err(()) } println!("Gateware successfully downloaded"); self.state = NetConnState::WaitCommand; Ok(1) } else { println!("End-of-gateware confirmation failed"); Err(()) } } } } fn input(&mut self, buf: &[u8], mut boot_callback: impl FnMut()) -> Result<(), ()> { let mut remaining = &buf[..]; while !remaining.is_empty() { let read_cnt = self.input_partial(remaining, &mut boot_callback)?; remaining = &remaining[read_cnt..]; } Ok(()) } } #[cfg(has_ethmac)] fn network_boot() { use smoltcp::wire::IpCidr; println!("Initializing network..."); // Assuming only one socket is ever needed by the bootloader. // The smoltcp reuses the listening socket when the connection is established. let mut sockets = [SocketStorage::EMPTY]; let mut net_device = unsafe { ethmac::EthernetDevice::new() }; net_device.reset_phy_if_any(); let mut neighbor_map = [None; 2]; let neighbor_cache = smoltcp::iface::NeighborCache::new(&mut neighbor_map[..]); let net_addresses = net_settings::get_adresses(); println!("Network addresses: {}", net_addresses); let mut ip_addrs = [ IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0), IpCidr::new(net_addresses.ipv6_ll_addr, 0), IpCidr::new(net_addresses.ipv6_ll_addr, 0) ]; if let net_settings::Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr { ip_addrs[0] = IpCidr::new(IpAddress::Ipv4(ipv4), 0); } let mut interface = match net_addresses.ipv6_addr { Some(addr) => { ip_addrs[2] = IpCidr::new(addr, 0); smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..]) .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) .ip_addrs(&mut ip_addrs[..]) .neighbor_cache(neighbor_cache) .finalize() } None => smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..]) .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) .ip_addrs(&mut ip_addrs[..2]) .neighbor_cache(neighbor_cache) .finalize() }; let mut rx_storage = [0; 4096]; let mut tx_storage = [0; 128]; let tcp_rx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]); let tcp_tx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]); let tcp_socket = smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); let tcp_handle = interface.add_socket(tcp_socket); let mut net_conn = NetConn::new(); let mut boot_time = None; println!("Waiting for connections..."); loop { let timestamp = clock::get_ms() as i64; { let socket = &mut *interface.get_socket::(tcp_handle); match boot_time { None => { if !socket.is_open() { socket.listen(4269).unwrap() // 0x10ad } if socket.may_recv() { if socket.recv(|data| { (data.len(), net_conn.input(data, || { boot_time = Some(timestamp + 20); }).is_err()) }).unwrap() { net_conn.reset(); socket.close(); } } else if socket.may_send() { net_conn.reset(); socket.close(); } }, Some(boot_time) => { if timestamp > boot_time { println!("Starting firmware."); unsafe { boot::jump(board_mem::MAIN_RAM_BASE) } } } } } match interface.poll(smoltcp::time::Instant::from_millis(timestamp)) { Ok(_) => (), Err(smoltcp::Error::Unrecognized) => (), Err(err) => println!("Network error: {}", err) } } } #[no_mangle] pub extern fn main() -> i32 { println!(""); println!(r" __ __ _ ____ ____ "); println!(r"| \/ (_) ___| ___ / ___|"); println!(r"| |\/| | \___ \ / _ \| | "); println!(r"| | | | |___) | (_) | |___ "); println!(r"|_| |_|_|____/ \___/ \____|"); println!(""); println!("MiSoC Bootloader"); println!("Copyright (c) 2017-2023 M-Labs Limited"); println!(""); #[cfg(has_ethmac)] clock::init(); if startup() { println!(""); if !config::read_str("no_flash_boot", |r| r == Ok("1")) { #[cfg(has_slave_fpga_cfg)] load_slave_fpga(); flash_boot(); } else { println!("Flash booting has been disabled."); } #[cfg(has_ethmac)] network_boot(); } else { println!("Halting."); } println!("No boot medium."); loop {} } #[no_mangle] pub extern fn exception(_regs: *const u32) { let pc = mepc::read(); let cause = mcause::read().cause(); let mtval = mtval::read(); panic!("{:?} at PC {:#08x}, trap value {:#08x}", cause, u32::try_from(pc).unwrap(), mtval); } #[no_mangle] pub extern fn abort() { println!("aborted"); loop {} } #[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647} #[panic_handler] pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! { #[cfg(has_error_led)] unsafe { board_misoc::csr::error_led::out_write(1); } if let Some(location) = info.location() { print!("panic at {}:{}:{}", location.file(), location.line(), location.column()); } else { print!("panic at unknown location"); } if let Some(message) = info.message() { println!(": {}", message); } else { println!(""); } loop {} }