|
| 1 | +use core::mem::{size_of, size_of_val}; |
| 2 | +use crc::{Crc, CRC_16_XMODEM}; |
| 3 | + |
| 4 | +#[derive(Debug)] |
| 5 | +pub enum Error { |
| 6 | + HeadLength { |
| 7 | + wrong_length: usize, |
| 8 | + }, |
| 9 | + MagicNumber { |
| 10 | + wrong_magic: u32, |
| 11 | + }, |
| 12 | + RawBlobMagic { |
| 13 | + wrong_magic: [u8; 32], |
| 14 | + }, |
| 15 | + ImageContentLength { |
| 16 | + wrong_content_length: usize, |
| 17 | + wrong_full_length: usize, |
| 18 | + }, |
| 19 | + OutputBufferLength { |
| 20 | + wrong_length: usize, |
| 21 | + }, |
| 22 | +} |
| 23 | + |
| 24 | +pub type Result<T> = core::result::Result<T, Error>; |
| 25 | + |
| 26 | +pub struct Operations<'a> { |
| 27 | + pub refill_header: Option<HeaderInfo>, |
| 28 | + pub set_image_content: Option<&'a [u8]>, |
| 29 | + pub resize_image_full_length: usize, |
| 30 | +} |
| 31 | + |
| 32 | +pub struct HeaderInfo { |
| 33 | + pub blcp_image_checksum: u32, |
| 34 | + pub bl2_image_checksum: u32, |
| 35 | + pub bl2_image_size: u32, |
| 36 | +} |
| 37 | + |
| 38 | +// TODO: supports: 1. blob with magic 2. full fip.bin 3. ELF file |
| 39 | +// 1. blob with blob magic |
| 40 | +// - returns additional header content and padding size |
| 41 | +// 2. full fip.bin (BL2 FIP magic): returns |
| 42 | +// - repaired header if wrong checksum |
| 43 | +// - error if image truncated (image length + image offset > file length) |
| 44 | +// 3. ELF containing blob (ELF magic): |
| 45 | +// returns header and image content |
| 46 | +pub fn check(buf: &[u8]) -> Result<Operations> { |
| 47 | + if buf.len() < size_of::<u32>() { |
| 48 | + return Err(Error::HeadLength { |
| 49 | + wrong_length: buf.len(), |
| 50 | + }); |
| 51 | + } |
| 52 | + let u32_magic = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]); |
| 53 | + match u32_magic { |
| 54 | + 0x0200006F => check_raw_blob(buf), |
| 55 | + 0x4C425643 => check_cvbl_fip(buf), |
| 56 | + 0x7F454C46 => check_elf(buf), |
| 57 | + wrong_magic => Err(Error::MagicNumber { wrong_magic }), |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +const HEADER_LENGTH: usize = 0x1000; |
| 62 | +const BLOB_MAGIC: [u8; 32] = [ |
| 63 | + 0x6F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 64 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 65 | +]; |
| 66 | + |
| 67 | +fn check_raw_blob(buf: &[u8]) -> Result<Operations> { |
| 68 | + if buf.len() < size_of_val(&BLOB_MAGIC) || buf.len() >= (u32::MAX - 512) as usize { |
| 69 | + return Err(Error::HeadLength { |
| 70 | + wrong_length: buf.len(), |
| 71 | + }); |
| 72 | + } |
| 73 | + if buf[..BLOB_MAGIC.len()] != BLOB_MAGIC { |
| 74 | + return Err(Error::RawBlobMagic { |
| 75 | + wrong_magic: buf[..BLOB_MAGIC.len()].try_into().unwrap(), |
| 76 | + }); |
| 77 | + } |
| 78 | + let crc = Crc::<u16>::new(&CRC_16_XMODEM); |
| 79 | + let padding_len = 512 - (buf.len() % 512); |
| 80 | + // TODO no allocations required |
| 81 | + let mut padded_buf = Vec::new(); |
| 82 | + padded_buf.extend(buf); |
| 83 | + padded_buf.extend(core::iter::repeat(0).take(padding_len)); |
| 84 | + let bl2_checksum_part = crc.checksum(&padded_buf); |
| 85 | + let bl2_checksum = 0xCAFE0000u32 + bl2_checksum_part as u32; |
| 86 | + // TODO no allocations required |
| 87 | + let bl2_padded_size = buf.len() + padding_len; |
| 88 | + Ok(Operations { |
| 89 | + refill_header: Some(HeaderInfo { |
| 90 | + blcp_image_checksum: 0xCAFE0000, |
| 91 | + bl2_image_checksum: bl2_checksum, |
| 92 | + bl2_image_size: bl2_padded_size as u32, |
| 93 | + }), |
| 94 | + set_image_content: Some(buf), |
| 95 | + resize_image_full_length: HEADER_LENGTH + bl2_padded_size, |
| 96 | + }) |
| 97 | +} |
| 98 | + |
| 99 | +const CVBL01_MAGIC: [u8; 12] = [ |
| 100 | + 0x43, 0x56, 0x42, 0x4C, 0x30, 0x31, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 101 | +]; // CVBL01 |
| 102 | + |
| 103 | +fn check_cvbl_fip(buf: &[u8]) -> Result<Operations> { |
| 104 | + todo!("{:?}", buf) |
| 105 | +} |
| 106 | + |
| 107 | +fn check_elf(buf: &[u8]) -> Result<Operations> { |
| 108 | + todo!("{:?}", buf) |
| 109 | +} |
| 110 | + |
| 111 | +pub fn process(buf: &mut [u8], ops: &Operations) -> Result<()> { |
| 112 | + if buf.len() < ops.resize_image_full_length { |
| 113 | + return Err(Error::OutputBufferLength { |
| 114 | + wrong_length: buf.len(), |
| 115 | + }); |
| 116 | + } |
| 117 | + if let Some(header) = &ops.refill_header { |
| 118 | + buf[..CVBL01_MAGIC.len()].copy_from_slice(&CVBL01_MAGIC); |
| 119 | + buf[0xBC..0xC0].copy_from_slice(&0x2F8u32.to_le_bytes()); // chip_conf_size |
| 120 | + buf[0xC0..0xC4].copy_from_slice(&header.blcp_image_checksum.to_le_bytes()); |
| 121 | + buf[0xD4..0xD8].copy_from_slice(&header.bl2_image_checksum.to_le_bytes()); |
| 122 | + buf[0xD8..0xDC].copy_from_slice(&header.bl2_image_size.to_le_bytes()); |
| 123 | + let crc = Crc::<u16>::new(&CRC_16_XMODEM); |
| 124 | + let param_checksum = 0xCAFE0000u32 + crc.checksum(&buf[0x10..0x800]) as u32; |
| 125 | + buf[0xC..0x10].copy_from_slice(¶m_checksum.to_le_bytes()) |
| 126 | + } |
| 127 | + if let Some(image) = &ops.set_image_content { |
| 128 | + if image.len() > u32::MAX as usize |
| 129 | + || ops.resize_image_full_length > u32::MAX as usize |
| 130 | + || ops.resize_image_full_length < HEADER_LENGTH |
| 131 | + || image.len() + HEADER_LENGTH > ops.resize_image_full_length |
| 132 | + { |
| 133 | + return Err(Error::ImageContentLength { |
| 134 | + wrong_content_length: image.len(), |
| 135 | + wrong_full_length: ops.resize_image_full_length, |
| 136 | + }); |
| 137 | + } |
| 138 | + buf[HEADER_LENGTH..][..image.len()].copy_from_slice(image); |
| 139 | + } |
| 140 | + Ok(()) |
| 141 | +} |
0 commit comments