#![no_std]
#![deny(unused_must_use)]
#![warn(clippy::pedantic)]
#![allow(
dead_code,
clippy::identity_op,
clippy::needless_range_loop,
clippy::cast_lossless
)]
use core::ptr;
#[cfg(feature = "blake2-standalone")]
pub mod blake2s;
pub const ROM_BASE: usize = 0x0000_0000;
pub const ROM_SIZE: usize = 3072 * 4;
pub const RAM_BASE: usize = 0x4000_0000;
pub const RAM_SIZE: usize = 0x2_0000;
const RESERVED_BASE: usize = 0x8000_0000;
const MMIO_BASE: usize = 0xc000_0000;
const MMIO_SIZE: usize = 0xffff_ffff - MMIO_BASE;
pub const TK1_CPU_FREQUENCY: u32 = 18_000_000;
pub const APP_MAX_SIZE: usize = RAM_SIZE;
const MMIO_TRNG_BASE: usize = MMIO_BASE | 0x0000_0000;
const MMIO_TIMER_BASE: usize = MMIO_BASE | 0x0100_0000;
const MMIO_UDS_BASE: usize = MMIO_BASE | 0x0200_0000;
const MMIO_TOUCH_BASE: usize = MMIO_BASE | 0x0400_0000;
const MMIO_FW_RAM_BASE: usize = MMIO_BASE | 0x1000_0000;
const MMIO_FW_RAM_SIZE: usize = 2048;
const MMIO_TK1_BASE: usize = MMIO_BASE | 0x3f00_0000;
pub const TK1_NAME0: *const u32 = (MMIO_TK1_BASE | 0x00) as *const u32;
pub const TK1_NAME1: *const u32 = (MMIO_TK1_BASE | 0x04) as *const u32;
pub const TK1_VERSION: *const u32 = (MMIO_TK1_BASE | 0x08) as *const u32;
pub const TK1_SWITCH_APP: *const u8 = (MMIO_TK1_BASE | 0x20) as *const u8;
pub const TK1_APP_ADDR: *const usize = (MMIO_TK1_BASE | 0x30) as *const usize;
pub const TK1_APP_SIZE: *const usize = (MMIO_TK1_BASE | 0x34) as *const usize;
pub const TK1_BLAKE2S_ADDR: *const usize = (MMIO_TK1_BASE | 0x40) as *const usize;
pub const TK1_CDI: *const u8 = (MMIO_TK1_BASE | 0x80) as *const u8;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error {
InvalidInput,
ProtocolViolation(&'static str),
Underrun,
Timeout,
}
pub mod io {
use core::ptr;
use crate::{timer, Error};
const MMIO_UART_BASE: usize = crate::MMIO_BASE | 0x0300_0000;
pub const UART_BITRATE: *mut u16 = (MMIO_UART_BASE | 0x40) as *mut u16;
pub const UART_DATA_BITS: *mut u8 = (MMIO_UART_BASE | 0x44) as *mut u8;
pub const UART_STOP_BITS: *mut u8 = (MMIO_UART_BASE | 0x48) as *mut u8;
pub const UART_RX_STATUS: *const u8 = (MMIO_UART_BASE | 0x80) as *const u8;
pub const UART_RX_DATA: *const u8 = (MMIO_UART_BASE | 0x84) as *const u8;
pub const UART_RX_BYTES: *const u32 = (MMIO_UART_BASE | 0x88) as *const u32;
pub const UART_TX_STATUS: *const u8 = (MMIO_UART_BASE | 0x100) as *const u8;
pub const UART_TX_DATA: *mut u8 = (MMIO_UART_BASE | 0x104) as *mut u8;
pub fn configuration() -> (u16, u8, u8) {
(
unsafe { ptr::read_volatile(UART_BITRATE) },
unsafe { ptr::read_volatile(UART_DATA_BITS) },
unsafe { ptr::read_volatile(UART_STOP_BITS) },
)
}
pub fn configure(bitrate: u16, data_bits: u8, stop_bits: u8) {
assert_eq!(0, crate::TK1_CPU_FREQUENCY % u32::from(bitrate));
assert!(data_bits < 16);
assert!(stop_bits < 4);
unsafe {
ptr::write_volatile(UART_BITRATE, bitrate);
ptr::write_volatile(UART_DATA_BITS, data_bits);
ptr::write_volatile(UART_STOP_BITS, stop_bits);
}
}
#[must_use]
pub fn available() -> usize {
unsafe { ptr::read_volatile(UART_RX_BYTES) as usize }
}
pub fn wait() {
while unsafe { ptr::read_volatile(UART_RX_STATUS) } == 0 {}
}
#[must_use]
pub fn read_u8() -> u8 {
loop {
unsafe {
if ptr::read_volatile(UART_RX_STATUS) != 0 {
return ptr::read_volatile(UART_RX_DATA);
}
}
}
}
pub fn read_into(buffer: &mut [u8]) {
for cell in buffer.iter_mut() {
*cell = read_u8();
}
}
pub fn read_available(buffer: &mut [u8]) -> usize {
let n = available();
if n == 0 {
return 0;
}
let n = n.min(buffer.len());
read_into(&mut buffer[0..n]);
n
}
pub fn read_into_checked(buffer: &mut [u8]) -> Result<(), Error> {
if buffer.len() > available() {
return Err(Error::Underrun);
}
read_into(buffer);
Ok(())
}
pub fn read_into_timed(buffer: &mut [u8], timeout: u32) -> Result<(), Error> {
assert!(!timer::running());
timer::set_prescaler(timer::PRESCALE_MILLISECONDS);
timer::initialize(timeout);
timer::start();
let mut i = 0usize;
while timer::running() {
i += read_available(&mut buffer[i..]);
if i == buffer.len() {
timer::stop();
return Ok(());
}
}
Err(Error::Timeout)
}
pub fn read_into_limited(buffer: &mut [u8], cycles: u32) -> usize {
let mut n = 0usize;
for _ in 0..cycles {
n += read_available(&mut buffer[n..]);
if n == buffer.len() {
break;
}
}
n
}
#[must_use]
pub fn read<const N: usize>() -> [u8; N] {
let mut buffer = [0u8; N];
read_into(&mut buffer);
buffer
}
pub fn read_checked<const N: usize>() -> Result<[u8; N], Error> {
if N > available() {
return Err(Error::Underrun);
}
Ok(read())
}
pub fn read_timed<const N: usize>(timeout: u32) -> Result<[u8; N], Error> {
let mut buffer = [0u8; N];
read_into_timed(&mut buffer, timeout)?;
Ok(buffer)
}
pub fn write_u8(b: u8) {
loop {
unsafe {
if ptr::read_volatile(UART_TX_STATUS) != 0 {
ptr::write_volatile(UART_TX_DATA, b);
return;
}
}
}
}
pub fn write(data: &[u8]) {
for b in data {
write_u8(*b);
}
}
}
pub mod frame {
use crate::{io, Error};
pub const LENGTH_MAX: usize = 128;
#[derive(Clone, Copy)]
pub enum Endpoint {
Firmware = 2,
Software = 3,
}
#[derive(Clone, Copy)]
pub enum CommandLength {
Length1 = 0,
Length4 = 1,
Length32 = 2,
Length128 = 3,
}
#[must_use]
pub fn command_length(length: CommandLength) -> usize {
match length {
CommandLength::Length1 => 1,
CommandLength::Length4 => 4,
CommandLength::Length32 => 32,
CommandLength::Length128 => 128,
}
}
#[must_use]
pub fn create_headerbyte(id: u8, endpoint: Endpoint, status: u8, length: CommandLength) -> u8 {
(id & 0b0000_0011) << 5 | (endpoint as u8) << 3 | (status & 0b0000_0001) << 2 | length as u8
}
#[must_use]
pub fn encode_header(header: &Header) -> u8 {
create_headerbyte(
header.id,
header.endpoint,
u8::from(header.error),
header.length,
)
}
#[derive(Clone, Copy)]
pub struct Header {
pub id: u8,
pub endpoint: Endpoint,
pub error: bool,
pub length: CommandLength,
}
pub fn parse_into(dst: &mut Header, headerbyte: u8) -> Result<(), Error> {
if headerbyte & 0b1000_0000 != 0 {
return Err(Error::ProtocolViolation(
"illegal value for reserved bit (protocol version)",
));
}
if headerbyte & 0b0000_0100 != 0 {
return Err(Error::ProtocolViolation(
"illegal value for unused bit (response status)",
));
}
dst.id = (headerbyte & 0b0110_0000) >> 5;
dst.endpoint = match (headerbyte & 0b0001_1000) >> 3 {
0 | 1 => return Err(Error::ProtocolViolation("illegal value for endpoint")),
2 => Endpoint::Firmware,
3 => Endpoint::Software,
_ => panic!("BUG: impossible value for id in frame-byte"),
};
dst.error = false;
dst.length = match headerbyte & 0b0000_0011 {
0 => CommandLength::Length1,
1 => CommandLength::Length4,
2 => CommandLength::Length32,
3 => CommandLength::Length128,
_ => panic!("BUG: impossible value for command length in frame-byte"),
};
Ok(())
}
pub fn parse(headerbyte: u8) -> Result<Header, Error> {
let mut header = Header {
id: 0,
endpoint: Endpoint::Firmware,
error: false,
length: CommandLength::Length1,
};
parse_into(&mut header, headerbyte)?;
Ok(header)
}
pub fn read_into(header: &mut Header, buffer: &mut [u8; LENGTH_MAX]) -> Result<(), Error> {
let headerbyte = io::read_u8();
parse_into(header, headerbyte)?;
let length = command_length(header.length);
io::read_into(&mut buffer[..length]);
Ok(())
}
pub fn write(header: &Header, data: &[u8]) {
let length = command_length(header.length);
assert!(data.len() >= length);
io::write_u8(encode_header(header));
io::write(&data[..length]);
}
}
pub mod gpio {
use crate::MMIO_TK1_BASE;
pub const TK1_GPIO: *mut u8 = (MMIO_TK1_BASE | 0x28) as *mut u8;
pub const TK1_GPIO_BIT_1: u8 = 0;
pub const TK1_GPIO_BIT_2: u8 = 1;
pub const TK1_GPIO_BIT_3: u8 = 2;
pub const TK1_GPIO_BIT_4: u8 = 3;
}
pub mod trng {
use core::ptr;
use crate::MMIO_TRNG_BASE;
pub const TRNG_STATUS: *const u8 = (MMIO_TRNG_BASE | 0x24) as *const u8;
pub const TRNG_BIT_READY: u8 = 0;
pub const TRNG_FLAG_READY: u8 = 1 << TRNG_BIT_READY;
pub const TRNG_ENTROPY: *const u32 = (MMIO_TRNG_BASE | 0x80) as *const u32;
#[must_use]
pub fn ready() -> bool {
unsafe { ptr::read_volatile(TRNG_STATUS) & TRNG_FLAG_READY != 0 }
}
pub fn wait() {
while !ready() {}
}
#[must_use]
pub fn read() -> u32 {
unsafe { ptr::read_volatile(TRNG_ENTROPY) }
}
#[must_use]
pub fn read_next() -> u32 {
wait();
read()
}
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn read_bytes() -> [u8; 4] {
let v = read();
[v as u8, (v >> 8) as u8, (v >> 16) as u8, (v >> 24) as u8]
}
#[must_use]
pub fn read_bytes_next() -> [u8; 4] {
wait();
read_bytes()
}
pub fn gather(buffer: &mut [u8]) {
for i in (0..buffer.len()).step_by(4) {
unsafe {
while ptr::read_volatile(TRNG_STATUS) & TRNG_FLAG_READY == 0 {}
ptr::copy_nonoverlapping(
TRNG_ENTROPY.cast::<u8>(),
buffer[i..].as_mut_ptr(),
4.min(buffer.len() - i),
);
}
}
}
}
pub mod led {
use core::ptr;
use crate::{sleep, MMIO_TK1_BASE};
pub const TK1_LED: *mut u8 = (MMIO_TK1_BASE | 0x24) as *mut u8;
pub const TK1_LED_BIT_RED: u8 = 2;
pub const TK1_LED_BIT_GREEN: u8 = 1;
pub const TK1_LED_BIT_BLUE: u8 = 0;
pub const LED_OFF: u8 = 0;
pub const LED_BLUE: u8 = 1 << TK1_LED_BIT_BLUE;
pub const LED_GREEN: u8 = 1 << TK1_LED_BIT_GREEN;
pub const LED_RED: u8 = 1 << TK1_LED_BIT_RED;
pub const LED_YELLOW: u8 = LED_RED | LED_GREEN;
pub const LED_PURPLE: u8 = LED_RED | LED_BLUE;
pub const LED_CYAN: u8 = LED_GREEN | LED_BLUE;
pub const LED_WHITE: u8 = LED_RED | LED_GREEN | LED_BLUE;
pub fn get() -> u8 {
unsafe { ptr::read_volatile(TK1_LED) & 0x7 }
}
pub fn set(color: u8) {
unsafe { ptr::write_volatile(TK1_LED, color & 0x7) }
}
#[must_use]
pub fn change(color: u8) -> u8 {
let prev = get();
set(color);
prev
}
pub fn signal(count: usize, color1: u8, color2: u8) {
let restore = get();
for _ in 0..count {
set(color1);
sleep(75000);
set(color2);
sleep(75000);
}
set(restore);
}
}
pub mod touch {
use core::ptr;
use crate::{led, sleep, MMIO_TOUCH_BASE};
pub const TOUCH_STATUS: *mut u8 = (MMIO_TOUCH_BASE | 0x24) as *mut u8;
pub const TOUCH_STATUS_BIT_EVENT: u8 = 0;
pub const TOUCH_STATUS_FLAG_EVENT: u8 = 1 << TOUCH_STATUS_BIT_EVENT;
pub fn reset() {
unsafe { ptr::write_volatile(TOUCH_STATUS, 0) };
}
pub fn touched() -> bool {
unsafe { ptr::read_volatile(TOUCH_STATUS) & TOUCH_STATUS_FLAG_EVENT != 0 }
}
#[must_use]
pub fn request(count: u16, color: u8) -> bool {
let restore = led::get();
reset();
for i in 0..usize::from(count) * 4 {
led::set(if i % 2 == 0 { led::LED_OFF } else { color });
if touched() {
led::set(restore);
return true;
}
sleep(if i % 4 == 0 { 200_000 } else { 100_000 });
}
led::set(restore);
false
}
}
pub mod timer {
use core::ptr;
use crate::{MMIO_TIMER_BASE, TK1_CPU_FREQUENCY};
pub const TIMER_CTRL: *mut u32 = (MMIO_TIMER_BASE | 0x20) as *mut u32;
pub const TIMER_CTRL_BIT_START: u32 = 0;
pub const TIMER_CTRL_FLAG_START: u32 = 1 << TIMER_CTRL_BIT_START;
pub const TIMER_CTRL_BIT_STOP: u32 = 1;
pub const TIMER_CTRL_FLAG_STOP: u32 = 1 << TIMER_CTRL_BIT_STOP;
pub const TIMER_STATUS: *const u32 = (MMIO_TIMER_BASE | 0x24) as *mut u32;
pub const TIMER_STATUS_BIT_RUNNING: u32 = 0;
pub const TIMER_STATUS_FLAG_RUNNING: u32 = 1 << TIMER_STATUS_BIT_RUNNING;
pub const TIMER_PRESCALER: *mut u32 = (MMIO_TIMER_BASE | 0x28) as *mut u32;
pub const TIMER_TIMER: *mut u32 = (MMIO_TIMER_BASE | 0x2c) as *mut u32;
pub const PRESCALE_SECONDS: u32 = TK1_CPU_FREQUENCY;
pub const PRESCALE_MILLISECONDS: u32 = TK1_CPU_FREQUENCY / 1000;
#[must_use]
pub fn running() -> bool {
unsafe { ptr::read_volatile(TIMER_STATUS) & TIMER_STATUS_FLAG_RUNNING != 0 }
}
pub fn set_prescaler(prescaler: u32) {
assert!(!running());
unsafe { ptr::write_volatile(TIMER_PRESCALER, prescaler) }
}
pub fn initialize(initial: u32) {
assert!(!running());
unsafe { ptr::write_volatile(TIMER_TIMER, initial) }
}
pub fn current() -> u32 {
unsafe { ptr::read_volatile(TIMER_TIMER) }
}
pub fn start() {
unsafe { ptr::write_volatile(TIMER_CTRL, TIMER_CTRL_FLAG_START) }
}
pub fn stop() {
unsafe { ptr::write_volatile(TIMER_CTRL, TIMER_CTRL_FLAG_STOP) }
}
pub fn wait() {
while running() {}
}
pub fn wait_until(value: u32) {
assert!(running());
while current() > value {}
}
pub fn wait_for(ticks: u32) {
wait_until(current() - ticks);
}
pub fn sleep(seconds: u32) {
assert!(!running());
set_prescaler(PRESCALE_SECONDS);
initialize(seconds);
start();
wait();
}
}
pub mod cpumonitor {
use core::ptr;
use crate::MMIO_TK1_BASE;
pub const TK1_CPU_MONITOR_CTRL: *mut u32 = (MMIO_TK1_BASE | 0x180) as *mut u32;
pub const TK1_CPU_MONITOR_FIRST: *mut usize = (MMIO_TK1_BASE | 0x184) as *mut usize;
pub const TK1_CPU_MONITOR_LAST: *mut usize = (MMIO_TK1_BASE | 0x188) as *mut usize;
pub fn monitor_range(first: usize, last: usize) {
static mut AVAILABLE: bool = true;
assert!(first <= last);
unsafe {
assert!(AVAILABLE);
ptr::write_volatile(TK1_CPU_MONITOR_FIRST, first);
ptr::write_volatile(TK1_CPU_MONITOR_LAST, last);
ptr::write_volatile(TK1_CPU_MONITOR_CTRL, 1);
AVAILABLE = false;
}
}
pub fn monitor_application_memory() {
monitor_range(
unsafe { *crate::TK1_APP_ADDR + *crate::TK1_APP_SIZE },
crate::RAM_BASE + crate::RAM_SIZE - 1,
);
}
}
#[must_use]
pub fn read_name_version() -> ([u8; 4], [u8; 4], u32) {
let mut name0 = [0u8; 4];
let mut name1 = [0u8; 4];
let version: u32;
unsafe {
ptr::copy_nonoverlapping(TK1_NAME0.cast::<u8>(), name0.as_mut_ptr(), 4);
ptr::copy_nonoverlapping(TK1_NAME1.cast::<u8>(), name1.as_mut_ptr(), 4);
version = ptr::read_volatile(TK1_VERSION);
}
(name0, name1, version)
}
#[must_use]
pub fn read_cdi() -> [u8; 32] {
let mut cdi = [0u8; 32];
unsafe {
ptr::copy_nonoverlapping(TK1_CDI, cdi.as_mut_ptr(), 32);
}
cdi
}
#[repr(C)]
#[derive(Copy, Clone)]
struct Blake2sContext {
pub b: [u8; 64],
pub h: [u32; 8],
pub t: [u32; 2],
pub c: usize,
pub outlen: usize,
}
#[cfg(feature = "blake2-firmware")]
pub fn blake2s(out: &mut [u8], key: &[u8], content: &[u8]) -> Result<(), Error> {
if out.is_empty() || out.len() > 32 || key.len() > 32 {
return Err(Error::InvalidInput);
}
let mut ctx = Blake2sContext {
b: [0; 64],
h: [0; 8],
t: [0; 2],
c: 0,
outlen: 0,
};
unsafe {
let blake2s_fw: unsafe extern "C" fn(
out: *mut u8,
outlen: usize,
key: *const u8,
keylen: usize,
content: *const u8,
contentlen: usize,
ctx: *mut Blake2sContext,
) -> i32 = core::mem::transmute(*TK1_BLAKE2S_ADDR);
let ret = blake2s_fw(
out.as_mut_ptr(),
out.len(),
key.as_ptr(),
key.len(),
content.as_ptr(),
content.len(),
&mut ctx,
);
assert_eq!(0, ret);
}
Ok(())
}
#[cfg(all(not(feature = "blake2-firmware"), feature = "blake2-standalone"))]
pub fn blake2s(out: &mut [u8], key: &[u8], content: &[u8]) -> Result<(), Error> {
blake2s::blake2s(out, key, content).map_err(|e| match e {
blake2s::Error::InvalidInput => Error::InvalidInput,
})
}
#[must_use = "Firmware ROM digest stored in the return value."]
pub fn hash_firmware_rom(key: &[u8]) -> Result<[u8; 32], Error> {
let mut out = [0u8; 32];
blake2s(&mut out, key, unsafe {
core::slice::from_raw_parts(ROM_BASE as *const u8, ROM_SIZE)
})?;
Ok(out)
}
#[allow(static_mut_refs)]
pub fn random(out: &mut [u8], seed: &[u8]) {
static mut BUFFER: [u8; 32] = [0u8; 32];
for i in (0..seed.len()).step_by(32) {
unsafe {
blake2s(
&mut BUFFER,
&seed[i..32.min(seed.len() - i)],
&*ptr::addr_of_mut!(BUFFER),
)
.unwrap();
};
}
if out.is_empty() {
return;
}
let mut key: [u8; 4] = trng::read_bytes();
for i in (0..out.len()).step_by(32) {
if trng::ready() {
key = trng::read_bytes();
} else {
key[0] ^= unsafe { BUFFER[key[0] as usize % BUFFER.len()] };
key[1] ^= unsafe { BUFFER[key[1] as usize % BUFFER.len()] };
key[2] ^= unsafe { BUFFER[key[2] as usize % BUFFER.len()] };
key[3] ^= unsafe { BUFFER[key[3] as usize % BUFFER.len()] };
}
unsafe {
blake2s(&mut BUFFER, &key, &*ptr::addr_of_mut!(BUFFER)).unwrap();
};
let size = 32.min(out.len() - i);
out[i..i + size].copy_from_slice(unsafe { &BUFFER[..size] });
}
unsafe {
blake2s(
&mut BUFFER,
"ByeByeOutputBytes".as_bytes(),
&*ptr::addr_of_mut!(BUFFER),
)
.unwrap();
};
}
pub fn sleep(n: usize) {
let mut i = 0usize;
while i < n {
unsafe { ptr::write_volatile(&mut i, i + 1) };
}
}
#[allow(clippy::empty_loop)]
pub fn done() -> ! {
loop {}
}
pub fn abort() -> ! {
loop {
led::set(led::LED_RED);
sleep(400_000);
led::set(led::LED_OFF);
sleep(400_000);
}
}