Skip to content

Commit

Permalink
xtask/starfive/visionfive2: rework header creation
Browse files Browse the repository at this point in the history
The struct is based on what the original vendor tool does.
No clue yet how the secure boot flow or setup really works.
Reference: starfive-tech/Tools#8

Add a Default impl.

Use CRC_32_ISO_HDLC from the crc crate as figured out by
https://github.com/jonirrings/vf2-header :)

Signed-off-by: Daniel Maslowski <[email protected]>
  • Loading branch information
orangecms committed Apr 7, 2024
1 parent 7fd0292 commit 20eece3
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 96 deletions.
7 changes: 3 additions & 4 deletions xtask/src/starfive/visionfive2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ use std::{fs, path::Path, process};
extern crate layoutflash;
use layoutflash::areas::{create_areas, Area};

use super::visionfive2_hdr::spl_create_hdr;
use super::visionfive2_hdr::{spl_create_hdr, HEADER_SIZE};

const HEADER_SIZE: usize = 0x400;
const SRAM_SIZE: usize = 0x2_8000;
const SRAM_SIZE: usize = 0x20_0000;

const ARCH: &str = "riscv64";
const TARGET: &str = "riscv64imac-unknown-none-elf";
Expand Down Expand Up @@ -124,7 +123,7 @@ fn xtask_build_image(env: &Env) {
// HACK: omit LinuxBoot etc so we fit in SRAM
let cut = core::cmp::min(SRAM_SIZE, dat.len());
trace!("image size {:08x} cut down to {cut:08x}", dat.len());
let out = spl_create_hdr(dat[HEADER_SIZE..cut].to_vec());
let out = spl_create_hdr(dat[HEADER_SIZE as usize..cut].to_vec());
trace!("final size {:08x}", out.len());
let out_path = dir.join(IMAGE);
fs::write(out_path.clone(), out).expect("writing final image");
Expand Down
214 changes: 122 additions & 92 deletions xtask/src/starfive/visionfive2_hdr.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1,136 @@
// It is not clear what CRC starfive had in mind, they wrote their own ...
fn crc32_reverse(x: u32) -> u32 {
let mut x = x;
x = ((x & 0x55555555) << 1) | ((x >> 1) & 0x55555555);
x = ((x & 0x33333333) << 2) | ((x >> 2) & 0x33333333);
x = ((x & 0x0F0F0F0F) << 4) | ((x >> 4) & 0x0F0F0F0F);
(x << 24) | ((x & 0xFF00) << 8) | ((x >> 8) & 0xFF00) | (x >> 24)
}

fn crc32(iv: u32, sv: u32, data: Vec<u8>) -> u32 {
let mut crc = iv;
for v in data {
let mut byte = crc32_reverse(v.into());
for _x in 0..8 {
crc = if ((crc ^ byte) & 0x80000000u32) != 0 {
(crc << 1) ^ sv
} else {
crc << 1
};
byte <<= 1;
}
}

crc
}

fn crc32_final(iv: u32) -> u32 {
crc32_reverse(iv ^ !0u32)
}

/* version: shall be 0x01010101
* (from https://doc-en.rvspace.org/VisionFive2/SWTRM/VisionFive2_SW_TRM/create_spl.html) */
const VERSION: u32 = 0x0101_0101;
// Offset of backup SBL from Flash info start (from input_sbl_normal.cfg)
const BACKUP: u32 = 0x20_0000;
// offset of spl header: 64+256+256 = 0x240
const HEADER_OFFSET: u32 = 0x0240;
// Offset of backup SBL from Flash info start (from input_sbl_normal.cfg)
const BACKUP_OFFSET: u32 = 0x20_0000;
/* Offset from HDR to SPL_IMAGE, 0x400 (00 04 00 00) currently */
const HEADER_SIZE: u32 = 0x0400;

const CRC_IV: u32 = !0;
const CRC_SV: u32 = 0x04c11db7;

// The use of a packed struct is kind of pointless. Just emit the proper things in the proper order.
// Push them into the output.
// Also, let's get real: there are no big-endian machines left. Assume LE.
// uint32_t sofs; /* offset of spl header: 64+256+256 = 0x240 */
// uint32_t bofs; /* SBL_BAK_OFFSET: Offset of backup SBL from Flash info start (from input_sbl_normal.cfg) */
// uint8_t zro2[636];
// uint32_t vers; /* version: shall be 0x01010101
// * (from https://doc-en.rvspace.org/VisionFive2/SWTRM/VisionFive2_SW_TRM/create_spl.html) */
// uint32_t fsiz; /* u-boot-spl.bin size in bytes */
// uint32_t res1; /* Offset from HDR to SPL_IMAGE, 0x400 (00 04 00 00) currently */
// uint32_t crcs; /* CRC32 of u-boot-spl.bin */
// uint8_t zro3[364];

pub fn spl_create_hdr(dat: Vec<u8>) -> Vec<u8> {
/*
// need to find out which one to use, but it's not this one.
// CRC-32-IEEE being the most commonly used one
let rc32 = crc::Crc::<u32>::new(&crc::CRC_32_ISCSI);
let mut digest = rc32.digest();
digest.update(&dat);
let crcout = digest.finalize();
}
*/
let v = crc32(CRC_IV, CRC_SV, dat.clone());
let fv = crc32_final(v);

let mut hdr = vec![];
pub const HEADER_SIZE: u32 = 0x0400;

let spl_header_offset: [u8; 4] = HEADER_OFFSET.to_le_bytes();
hdr.extend_from_slice(&spl_header_offset);

let backup: [u8; 4] = BACKUP.to_le_bytes();
hdr.extend_from_slice(&backup);
fn crc32(data: &Vec<u8>) -> u32 {
let c = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
let mut digest = c.digest();
digest.update(&data);
digest.finalize().to_le()
}

hdr.resize(hdr.len() + 636, 0);
fn zeroes<const N: usize>() -> [u8; N] {
[0u8; N]
}

let version: [u8; 4] = VERSION.to_le_bytes();
hdr.extend_from_slice(&version);
#[repr(C)]
#[derive(Default)]
struct Version {
major: u8,
minor: u8,
revision: u16,
}

let data_len: [u8; 4] = (dat.len() as u32).to_le_bytes();
hdr.extend_from_slice(&data_len); /* u-boot-spl.bin size in bytes */
#[repr(C)]
struct JH7110CommonHeader {
// the offset to the other header
size: u32,
backup_offset: u32,
_unknown1: u64,
_skip1: [u8; 48],
// 0x040
ec_param_p: [u8; 32],
ec_param_a: [u8; 32],
ec_param_b: [u8; 32],
ec_param_gx: [u8; 32],
ec_param_gy: [u8; 32],
ec_param_n: [u8; 32],
_skip2: [u8; 64],
// 0x140
ec_key_1: [u8; 64],
ec_key_2: [u8; 64],
ec_key_3: [u8; 64],
ec_key_4: [u8; 64],
}

println!("boot blob size: {}", dat.len());
impl Default for JH7110CommonHeader {
fn default() -> Self {
JH7110CommonHeader {
size: HEADER_OFFSET,
backup_offset: BACKUP_OFFSET,
_unknown1: 0,
_skip1: zeroes(),
ec_param_p: zeroes(),
ec_param_a: zeroes(),
ec_param_b: zeroes(),
ec_param_gx: zeroes(),
ec_param_gy: zeroes(),
ec_param_n: zeroes(),
_skip2: zeroes(),
ec_key_1: zeroes(),
ec_key_2: zeroes(),
ec_key_3: zeroes(),
ec_key_4: zeroes(),
}
}
}

let data_offset: [u8; 4] = HEADER_SIZE.to_le_bytes();
hdr.extend_from_slice(&data_offset);
type SigPadding = [u8; 0x40];

#[repr(C)]
struct JH7110SBLHeader {
ec_key_select: u32,
version: Version,
payload_size: u32,
header_size: u32,
checksum: u32,
aes_iv: [u8; 0x10],
ec_key_revoke: [u8; 0x20],
sbl_ver_cipher: [u8; 0x10],
// fill up to 0x180
_rest: [u8; 0x12c],
}

/* CRC32 of dat */
let l: [u8; 4] = fv.to_le_bytes();
hdr.extend_from_slice(&l);
// fill up to HEADER_SIZE
hdr.resize(hdr.len() + 364, 0);
impl Default for JH7110SBLHeader {
fn default() -> Self {
JH7110SBLHeader {
// TODO: This needs to be a parameter for "secure boot" setup.
// EC key to use; 0 means use none, 1 means first, 4 is maximum.
// Those refer to the ec_key_n in JH7110CommonHeader.
ec_key_select: 0,
// https://doc-en.rvspace.org/VisionFive2/SWTRM/VisionFive2_SW_TRM/create_spl.html
version: Version {
major: 1,
minor: 1,
revision: 0x101,
},
payload_size: 0,
header_size: HEADER_SIZE,
checksum: 0,
aes_iv: zeroes(),
ec_key_revoke: zeroes(),
sbl_ver_cipher: zeroes(),
_rest: zeroes(),
}
}
}

assert!(
hdr.len() == HEADER_SIZE as usize,
"hdr is {:x} bytes, not {HEADER_SIZE}",
hdr.len()
);
hdr.extend(&dat);
hdr
pub fn spl_create_hdr(payload: Vec<u8>) -> Vec<u8> {
let common_header = JH7110CommonHeader::default();
let sig_padding: SigPadding = zeroes();
let sbl_header = JH7110SBLHeader {
payload_size: payload.len() as u32,
checksum: crc32(&payload),
..Default::default()
};

// this should be easier
let ch_size = std::mem::size_of::<JH7110CommonHeader>();
let ch_ptr = &common_header as *const JH7110CommonHeader as *const u8;
let ch = unsafe { std::slice::from_raw_parts(ch_ptr, ch_size) };
let sh_size = std::mem::size_of::<JH7110SBLHeader>();
let sh_ptr = &sbl_header as *const JH7110SBLHeader as *const u8;
let sh = unsafe { std::slice::from_raw_parts(sh_ptr, sh_size) };

let mut res = vec![];
res.extend(ch);
res.extend(&sig_padding);
res.extend(sh);
res.extend(&payload);
res
}

#[test]
Expand Down

0 comments on commit 20eece3

Please sign in to comment.