forked from M-Labs/artiq
562 lines
18 KiB
Rust
562 lines
18 KiB
Rust
#![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};
|
|
#[cfg(has_ethmac)]
|
|
use smoltcp::iface::{Routes, SocketStorage};
|
|
#[cfg(has_ethmac)]
|
|
use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address, Ipv6Address};
|
|
|
|
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<usize, ()> {
|
|
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),
|
|
net_addresses.ipv6_ll_addr,
|
|
IpCidr::new(IpAddress::Ipv6(Ipv6Address::UNSPECIFIED), 0)
|
|
];
|
|
if let net_settings::Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
|
|
ip_addrs[0] = IpCidr::Ipv4(ipv4);
|
|
}
|
|
if let Some(ipv6) = net_addresses.ipv6_addr {
|
|
ip_addrs[2] = IpCidr::Ipv6(ipv6);
|
|
};
|
|
let mut routes = [None; 2];
|
|
let mut interface = 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)
|
|
.routes(Routes::new(&mut routes[..]))
|
|
.finalize();
|
|
|
|
if let Some(default_route) = net_addresses.ipv4_default_route {
|
|
interface.routes_mut().add_default_ipv4_route(default_route).unwrap();
|
|
}
|
|
if let Some(default_route) = net_addresses.ipv6_default_route {
|
|
interface.routes_mut().add_default_ipv6_route(default_route).unwrap();
|
|
}
|
|
|
|
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::<smoltcp::socket::TcpSocket>(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 {}
|
|
}
|