big progress
Some checks failed
Rust/va416xx-rs/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2024-09-11 20:42:16 +02:00
parent daa181d1de
commit ac601d06cc
Signed by: muellerr
GPG Key ID: A649FB78196E3849
3 changed files with 126 additions and 40 deletions

View File

@ -115,6 +115,8 @@ fn main() -> ! {
let nvm = Nvm::new(&mut dp.sysconfig, dp.spi3, &clocks); let nvm = Nvm::new(&mut dp.sysconfig, dp.spi3, &clocks);
if FLASH_SELF { if FLASH_SELF {
let mut first_four_bytes: [u8; 4] = [0; 4];
read_four_bytes_at_addr_zero(&mut first_four_bytes);
let bootloader_data = { let bootloader_data = {
unsafe { unsafe {
&*core::ptr::slice_from_raw_parts( &*core::ptr::slice_from_raw_parts(
@ -123,15 +125,6 @@ fn main() -> ! {
) )
} }
}; };
let mut first_four_bytes: [u8; 4] = [0; 4];
unsafe {
core::arch::asm!(
"ldr r0, [{0}]", // Load 4 bytes from src into r0 register
"str r0, [{1}]", // Store r0 register into first_four_bytes
in(reg) BOOTLOADER_START_ADDR as *const u8, // Input: src pointer (0x0)
in(reg) &mut first_four_bytes as *mut [u8; 4], // Input: destination pointer
);
}
let mut digest = CRC_ALGO.digest(); let mut digest = CRC_ALGO.digest();
digest.update(&first_four_bytes); digest.update(&first_four_bytes);
digest.update(bootloader_data); digest.update(bootloader_data);
@ -173,17 +166,22 @@ fn main() -> ! {
} }
fn check_own_crc(wdt: &OptWdt, nvm: &Nvm, cp: &cortex_m::Peripherals) { fn check_own_crc(wdt: &OptWdt, nvm: &Nvm, cp: &cortex_m::Peripherals) {
let crc_exp = unsafe { *(BOOTLOADER_CRC_ADDR as *const u32) }; let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u32).read_unaligned().to_be() };
wdt.feed(); wdt.feed();
// I'd prefer to use [core::slice::from_raw_parts], but that is problematic // I'd prefer to use [core::slice::from_raw_parts], but that is problematic
// because the address of the bootloader is 0x0, so the NULL check fails and the functions // because the address of the bootloader is 0x0, so the NULL check fails and the functions
// panics. // panics.
let crc_calc = CRC_ALGO.checksum(unsafe { let mut first_four_bytes: [u8; 4] = [0; 4];
read_four_bytes_at_addr_zero(&mut first_four_bytes);
let mut digest = CRC_ALGO.digest();
digest.update(&first_four_bytes);
digest.update(unsafe {
&*core::ptr::slice_from_raw_parts( &*core::ptr::slice_from_raw_parts(
BOOTLOADER_START_ADDR as *const u8, (BOOTLOADER_START_ADDR + 4) as *const u8,
(BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 4) as usize, (BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 8) as usize,
) )
}); });
let crc_calc = digest.finalize();
wdt.feed(); wdt.feed();
if crc_exp == 0x0000 || crc_exp == 0xffff { if crc_exp == 0x0000 || crc_exp == 0xffff {
if DEBUG_PRINTOUTS { if DEBUG_PRINTOUTS {
@ -191,18 +189,33 @@ fn check_own_crc(wdt: &OptWdt, nvm: &Nvm, cp: &cortex_m::Peripherals) {
} }
// Blank CRC, write it to NVM. // Blank CRC, write it to NVM.
nvm.write_data(BOOTLOADER_CRC_ADDR, &crc_calc.to_be_bytes()); nvm.write_data(BOOTLOADER_CRC_ADDR, &crc_calc.to_be_bytes());
// The Vorago bootloader resets here. I am not sure why this is done, just continue with // The Vorago bootloader resets here. I am not sure why this is done but I think it is
// the regular boot process.. // necessary because somehow the boot will not work if we just continue as usual.
// cortex_m::peripheral::SCB::sys_reset();
} else if crc_exp != crc_calc { } else if crc_exp != crc_calc {
// Bootloader is corrupted. Try to run App A. // Bootloader is corrupted. Try to run App A.
if DEBUG_PRINTOUTS { if DEBUG_PRINTOUTS {
rprintln!("bootloader CRC corrupt. booting image A immediately"); rprintln!(
"bootloader CRC corrupt, read {} and expected {}. booting image A immediately",
crc_calc,
crc_exp
);
} }
// TODO: Shift out minimal CCSDS frame to notify about bootloader corruption. // TODO: Shift out minimal CCSDS frame to notify about bootloader corruption.
boot_app(AppSel::A, cp); boot_app(AppSel::A, cp);
} }
} }
fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) {
unsafe {
core::arch::asm!(
"ldr r0, [{0}]", // Load 4 bytes from src into r0 register
"str r0, [{1}]", // Store r0 register into first_four_bytes
in(reg) BOOTLOADER_START_ADDR as *const u8, // Input: src pointer (0x0)
in(reg) buf as *mut [u8; 4], // Input: destination pointer
);
}
}
fn check_app_crc(app_sel: AppSel, wdt: &OptWdt) -> bool { fn check_app_crc(app_sel: AppSel, wdt: &OptWdt) -> bool {
if DEBUG_PRINTOUTS { if DEBUG_PRINTOUTS {
rprintln!("Checking image {:?}", app_sel); rprintln!("Checking image {:?}", app_sel);
@ -220,8 +233,13 @@ fn check_app_given_addr(
image_size_addr: u32, image_size_addr: u32,
wdt: &OptWdt, wdt: &OptWdt,
) -> bool { ) -> bool {
let crc_exp = unsafe { *(crc_addr as *const u32) }; let crc_exp = unsafe { (crc_addr as *const u32).read_unaligned().to_be() };
let image_size = unsafe { *(image_size_addr as *const u32) }; let image_size = unsafe { (image_size_addr as *const u32).read_unaligned().to_be() };
// Sanity check.
if image_size > APP_A_END_ADDR - APP_A_START_ADDR - 8 {
rprintln!("detected invalid app size {}", image_size);
return false;
}
wdt.feed(); wdt.feed();
let crc_calc = CRC_ALGO.checksum(unsafe { let crc_calc = CRC_ALGO.checksum(unsafe {
core::slice::from_raw_parts(start_addr as *const u8, image_size as usize) core::slice::from_raw_parts(start_addr as *const u8, image_size as usize)
@ -234,6 +252,9 @@ fn check_app_given_addr(
} }
fn boot_app(app_sel: AppSel, cp: &cortex_m::Peripherals) -> ! { fn boot_app(app_sel: AppSel, cp: &cortex_m::Peripherals) -> ! {
if DEBUG_PRINTOUTS {
rprintln!("booting app {:?}", app_sel);
}
let clkgen = unsafe { pac::Clkgen::steal() }; let clkgen = unsafe { pac::Clkgen::steal() };
clkgen clkgen
.ctrl0() .ctrl0()

View File

@ -5,6 +5,7 @@ import struct
import logging import logging
import argparse import argparse
import time import time
import enum
from tmtccmd.com.serial_base import SerialCfg from tmtccmd.com.serial_base import SerialCfg
from tmtccmd.com.serial_cobs import SerialCobsComIF from tmtccmd.com.serial_cobs import SerialCobsComIF
from tmtccmd.com.ser_utils import prompt_com_port from tmtccmd.com.ser_utils import prompt_com_port
@ -34,9 +35,17 @@ APP_IMG_SZ = 0x1E000
CHUNK_SIZE = 896 CHUNK_SIZE = 896
MEMORY_SERVICE = 6 MEMORY_SERVICE = 6
ACTION_SERVICE = 8
RAW_MEMORY_WRITE_SUBSERVICE = 2 RAW_MEMORY_WRITE_SUBSERVICE = 2
BOOT_NVM_MEMORY_ID = 1 BOOT_NVM_MEMORY_ID = 1
class ActionId(enum.IntEnum):
CORRUPT_APP_A = 128
CORRUPT_APP_B = 129
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -57,11 +66,12 @@ def main() -> int:
prog="image-loader", description="Python VA416XX Image Loader Application" prog="image-loader", description="Python VA416XX Image Loader Application"
) )
parser.add_argument("-p", "--ping", action="store_true", help="Send ping command") parser.add_argument("-p", "--ping", action="store_true", help="Send ping command")
parser.add_argument("-c", "--corrupt", action="store_true", help="Corrupt a target")
parser.add_argument( parser.add_argument(
"-f", "-t",
"--flash", "--target",
choices=["bl", "a", "b"], choices=["bl", "a", "b"],
help="Flash target (Bootloader or slot A or B)", help="Target (Bootloader or slot A or B)",
) )
parser.add_argument( parser.add_argument(
"path", nargs="?", default=None, help="Path to the App to flash" "path", nargs="?", default=None, help="Path to the App to flash"
@ -84,19 +94,40 @@ def main() -> int:
com_if = SerialCobsComIF(serial_cfg) com_if = SerialCobsComIF(serial_cfg)
com_if.open() com_if.open()
file_path = None file_path = None
if args.flash: if args.target:
if not args.path: if not args.corrupt:
_LOGGER.error("App Path needs to be specified for the flash process") if not args.path:
return -1 _LOGGER.error("App Path needs to be specified for the flash process")
file_path = Path(args.path) return -1
if not file_path.exists(): file_path = Path(args.path)
_LOGGER.error("File does not exist") if not file_path.exists():
return -1 _LOGGER.error("File does not exist")
ping_tc = PusTc(apid=0x00, service=PusService.S17_TEST, subservice=1) return -1
if args.ping: if args.ping:
_LOGGER.info("Sending ping command") _LOGGER.info("Sending ping command")
ping_tc = PusTc(apid=0x00, service=PusService.S17_TEST, subservice=1)
com_if.send(ping_tc.pack()) com_if.send(ping_tc.pack())
if args.flash: if args.corrupt:
if not args.target:
_LOGGER.error("target for corruption command required")
return -1
if args.target == "bl":
_LOGGER.error("can not corrupt bootloader")
if args.target == "a":
packet = PusTc(
apid=0,
service=ACTION_SERVICE,
subservice=ActionId.CORRUPT_APP_A,
)
com_if.send(packet.pack())
if args.target == "b":
packet = PusTc(
apid=0,
service=ACTION_SERVICE,
subservice=ActionId.CORRUPT_APP_B,
)
com_if.send(packet.pack())
else:
assert file_path is not None assert file_path is not None
loadable_segments = [] loadable_segments = []
_LOGGER.info("Parsing ELF file for loadable sections") _LOGGER.info("Parsing ELF file for loadable sections")
@ -110,19 +141,19 @@ def main() -> int:
# Basic validity checks of the base addresses. # Basic validity checks of the base addresses.
if idx == 0: if idx == 0:
if ( if (
args.flash == "bl" args.target == "bl"
and segment.header.p_paddr != BOOTLOADER_START_ADDR and segment.header.p_paddr != BOOTLOADER_START_ADDR
): ):
raise ValueError( raise ValueError(
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for " f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
f"bootloader, expected {BOOTLOADER_START_ADDR}" f"bootloader, expected {BOOTLOADER_START_ADDR}"
) )
if args.flash == "a" and segment.header.p_paddr != APP_A_START_ADDR: if args.target == "a" and segment.header.p_paddr != APP_A_START_ADDR:
raise ValueError( raise ValueError(
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for " f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
f"App A, expected {APP_A_START_ADDR}" f"App A, expected {APP_A_START_ADDR}"
) )
if args.flash == "b" and segment.header.p_paddr != APP_B_START_ADDR: if args.target == "b" and segment.header.p_paddr != APP_B_START_ADDR:
raise ValueError( raise ValueError(
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for " f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
f"App B, expected {APP_B_START_ADDR}" f"App B, expected {APP_B_START_ADDR}"
@ -150,11 +181,11 @@ def main() -> int:
) )
total_size += segment.header.p_filesz total_size += segment.header.p_filesz
context_str = None context_str = None
if args.flash == "bl": if args.target == "bl":
context_str = "Bootloader" context_str = "Bootloader"
elif args.flash == "a": elif args.target == "a":
context_str = "App Slot A" context_str = "App Slot A"
elif args.flash == "b": elif args.target == "b":
context_str = "App Slot B" context_str = "App Slot B"
_LOGGER.info( _LOGGER.info(
f"Flashing {context_str} with image {file_path} (size {total_size})" f"Flashing {context_str} with image {file_path} (size {total_size})"
@ -181,7 +212,7 @@ def main() -> int:
current_addr += next_chunk_size current_addr += next_chunk_size
pos_in_segment += next_chunk_size pos_in_segment += next_chunk_size
time.sleep(0.2) time.sleep(0.2)
if args.flash == "bl": if args.target == "bl":
_LOGGER.info("Blanking the bootloader checksum") _LOGGER.info("Blanking the bootloader checksum")
# Blank the checksum. For the bootloader, the bootloader will calculate the # Blank the checksum. For the bootloader, the bootloader will calculate the
# checksum itself on the initial run. # checksum itself on the initial run.
@ -192,10 +223,10 @@ def main() -> int:
else: else:
crc_addr = None crc_addr = None
size_addr = None size_addr = None
if args.flash == "a": if args.target == "a":
crc_addr = APP_A_CRC_ADDR crc_addr = APP_A_CRC_ADDR
size_addr = APP_A_SIZE_ADDR size_addr = APP_A_SIZE_ADDR
elif args.flash == "b": elif args.target == "b":
crc_addr = APP_B_CRC_ADDR crc_addr = APP_B_CRC_ADDR
size_addr = APP_B_SIZE_ADDR size_addr = APP_B_SIZE_ADDR
assert crc_addr is not None assert crc_addr is not None

View File

@ -33,6 +33,10 @@ const COBS_RX_DEBUGGING: bool = false;
const BOOT_NVM_MEMORY_ID: u8 = 1; const BOOT_NVM_MEMORY_ID: u8 = 1;
pub enum ActionId {
CorruptImageA = 128,
CorruptImageB = 129,
}
pub trait WdtInterface { pub trait WdtInterface {
fn feed(&self); fn feed(&self);
} }
@ -49,6 +53,11 @@ impl WdtInterface for OptWdt {
static CLOCKS: OnceCell<Clocks> = OnceCell::new(); static CLOCKS: OnceCell<Clocks> = OnceCell::new();
pub const APP_A_START_ADDR: u32 = 0x4000;
pub const APP_A_END_ADDR: u32 = 0x22000;
pub const APP_B_START_ADDR: u32 = 0x22000;
pub const APP_B_END_ADDR: u32 = 0x40000;
#[rtic::app(device = pac, dispatchers = [U1, U2, U3])] #[rtic::app(device = pac, dispatchers = [U1, U2, U3])]
mod app { mod app {
use super::*; use super::*;
@ -303,6 +312,31 @@ mod app {
cx.shared.decode_buffer_busy.lock(|busy| *busy = false); cx.shared.decode_buffer_busy.lock(|busy| *busy = false);
match PusTcReader::new(cx.local.read_buf) { match PusTcReader::new(cx.local.read_buf) {
Ok((pus_tc, _)) => { Ok((pus_tc, _)) => {
if pus_tc.service() == PusServiceId::Action as u8 {
let mut corrupt_image = |base_addr: u32| {
// Safety: We only use this for NVM handling and we only do NVM
// handling here.
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
let nvm = Nvm::new(
&mut sys_cfg,
cx.local.rom_spi.take().unwrap(),
CLOCKS.get().as_ref().unwrap(),
);
let mut buf = [0u8; 4];
nvm.read_data(base_addr + 32, &mut buf);
buf[0] += 1;
nvm.write_data(base_addr + 32, &buf);
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
};
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
rprintln!("corrupting App Image A");
corrupt_image(APP_A_START_ADDR);
}
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
rprintln!("corrupting App Image B");
corrupt_image(APP_B_START_ADDR);
}
}
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 { if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
log::info!(target: "TC Handler", "received ping TC"); log::info!(target: "TC Handler", "received ping TC");
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 { } else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {