blob: bd7282898001a374c56349273f953ad2b0f8c5e4 [file] [log] [blame]
/**
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include "pico/stdlib.h"
#include "boot/picobin.h"
#include "boot/picoboot.h"
#include "pico/bootrom.h"
#include "hardware/structs/otp.h"
#include "hardware/structs/trng.h"
#include "hardware/structs/sha256.h"
#include "pico/binary_info.h"
#include "hardware/clocks.h"
#include "hardware/xosc.h"
#include "hardware/structs/rosc.h"
#include "config.h"
extern void decrypt(uint8_t* key4way, uint8_t* IV_OTPsalt, uint8_t* IV_public, uint8_t(*buf)[16], int nblk);
// These just have to be higher than the actual frequency, to prevent overclocking unused peripherals
#define ROSC_HZ 300*MHZ
#define OTHER_CLK_DIV 30
// Enable calibration of the ROSC using the XOSC - if disabled it runs with the fixed ~110MHz ROSC configuration
#define XOSC_CALIBRATION 0
#if CK_JITTER
void runtime_init_clocks(void) {
// Disable resus that may be enabled from previous software
clocks_hw->resus.ctrl = 0;
bi_decl(bi_ptr_int32(0, 0, rosc_div, 2)); // default divider 2
bi_decl(bi_ptr_int32(0, 0, rosc_drive, 0x7777)); // default drives of 0b111 (0x7)
// Bump up ROSC speed to ~110MHz
rosc_hw->freqa = 0; // reset the drive strengths
rosc_hw->div = rosc_div | ROSC_DIV_VALUE_PASS; // set divider
// Increment the freqency range one step at a time - this is safe provided the current config is not TOOHIGH
// because ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM | ROSC_CTRL_FREQ_RANGE_VALUE_HIGH == ROSC_CTRL_FREQ_RANGE_VALUE_HIGH
static_assert(ROSC_CTRL_FREQ_RANGE_VALUE_LOW | ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM == ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM);
static_assert(ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM | ROSC_CTRL_FREQ_RANGE_VALUE_HIGH == ROSC_CTRL_FREQ_RANGE_VALUE_HIGH);
hw_set_bits(&rosc_hw->ctrl, ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM);
hw_set_bits(&rosc_hw->ctrl, ROSC_CTRL_FREQ_RANGE_VALUE_HIGH);
// Enable rosc randomisation
rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) |
rosc_drive | ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation
// Not used with FREQ_RANGE_VALUE_HIGH, but should still be set to the maximum drive
rosc_hw->freqb = (ROSC_FREQB_PASSWD_VALUE_PASS << ROSC_FREQB_PASSWD_LSB) |
ROSC_FREQB_DS7_LSB | ROSC_FREQB_DS6_LSB | ROSC_FREQB_DS5_LSB | ROSC_FREQB_DS4_LSB;
#if XOSC_CALIBRATION
// Calibrate ROSC frequency if XOSC present - otherwise just configure
bi_decl(bi_ptr_int32(0, 0, xosc_hz, 12000000)); // xosc freq in Hz
bi_decl(bi_ptr_int32(0, 0, clk_khz, 150 * KHZ)); // maximum clk_sys freq in KHz
if (xosc_hz) {
xosc_init();
// Switch away from ROSC to avoid overclocking
// CLK_REF = XOSC
clock_configure_int_divider(clk_ref,
CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC,
0,
xosc_hz,
1);
// CLK_SYS = CLK_REF
clock_configure_int_divider(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC, // leave the aux source on ROSC to prevent glitches when we switch back to it
xosc_hz,
1);
// Go through configurations until you get below 150MHz
uint8_t div = 1;
uint32_t drives = 0x7777;
while (div < 4) {
rosc_hw->div = div | ROSC_DIV_VALUE_PASS;
rosc_hw->freqa = (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) |
drives |
ROSC_FREQA_DS1_RANDOM_BITS | ROSC_FREQA_DS0_RANDOM_BITS; // enable randomisation
// Wait for ROSC to be stable
while(!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)) {
tight_loop_contents();
}
if (frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC_PH) < clk_khz) {
break;
}
if (!drives) div++;
drives = drives ? 0x0000 : 0x7777;
}
}
#endif // XOSC_CALIBRATION
// CLK SYS = ROSC directly, as it's running slowly enough
clock_configure_int_divider(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC,
ROSC_HZ, // this doesn't have to be accurate
1);
// Configure other clocks - none of these need to be accurate
// CLK_REF = ROSC / OTHER_CLK_DIV - this and other clocks aren't really used, so just need to be set to a low enough frequency
clock_configure_int_divider(clk_ref,
CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH,
0,
ROSC_HZ,
OTHER_CLK_DIV);
// CLK USB (not used)
clock_configure_int_divider(clk_usb,
0, // No GLMUX
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH,
ROSC_HZ,
OTHER_CLK_DIV);
// CLK ADC (not used)
clock_configure_int_divider(clk_adc,
0, // No GLMUX
CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH,
ROSC_HZ,
OTHER_CLK_DIV);
// CLK PERI Used as reference clock for UART and SPI serial. (not used)
clock_configure_int_divider(clk_peri,
0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
ROSC_HZ,
OTHER_CLK_DIV);
// CLK_HSTX Transmit bit clock for the HSTX peripheral. (not used)
clock_configure_int_divider(clk_hstx,
0,
CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLK_SYS,
ROSC_HZ,
OTHER_CLK_DIV);
}
#endif
bi_decl(bi_ptr_int32(0, 0, otp_key_page, 29));
// The function lock_key() is called from decrypt() after key initialisation is complete and before decryption begins.
// That is a suitable point to lock the OTP area where key information is stored.
void lock_key() {
otp_hw->sw_lock[otp_key_page] = 0xf;
otp_hw->sw_lock[otp_key_page + 1] = 0xf;
}
int main() {
bi_decl(bi_ptr_int32(0, 0, data_start_addr, 0x20000000));
bi_decl(bi_ptr_int32(0, 0, data_size, 0x78000));
bi_decl(bi_ptr_string(0, 0, iv, "0123456789abcdef", 17));
// Read key directly from OTP - guarded reads will throw a bus fault if there are any errors
uint16_t* otp_data = (uint16_t*)OTP_DATA_GUARDED_BASE;
decrypt(
(uint8_t*)&(otp_data[otp_key_page * 0x40]),
(uint8_t*)&(otp_data[(otp_key_page + 2) * 0x40]),
(uint8_t*)iv,
(void*)data_start_addr,
data_size/16
);
// Lock the IV salt
otp_hw->sw_lock[otp_key_page + 2] = 0xf;
// Increase stack limit by 0x100
pico_default_asm_volatile(
"mrs r0, msplim\n"
"subs r0, 0x100\n"
"msr msplim, r0"
:::"r0");
// Chain into decrypted image
rom_chain_image(
(uint8_t*)ROM_CHAIN_WORKSPACE,
4 * 1024,
data_start_addr,
data_size
);
__breakpoint();
}