| /* |
| * Copyright (C) 2015 Freescale Semiconductor, Inc. |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <asm/arch/clock.h> |
| #include <asm/arch/iomux.h> |
| #include <asm/arch/imx-regs.h> |
| #include <asm/arch/crm_regs.h> |
| #include <asm/arch/mx6-pins.h> |
| #include <asm/arch/sys_proto.h> |
| #include <asm/gpio.h> |
| #include <asm/imx-common/iomux-v3.h> |
| #include <asm/imx-common/boot_mode.h> |
| #include <asm/imx-common/mxc_i2c.h> |
| #include <asm/io.h> |
| #include <common.h> |
| #include <environment.h> |
| #include <fdtdec.h> |
| #include <fsl_esdhc.h> |
| #include <i2c.h> |
| #include <linux/sizes.h> |
| #include <linux/fb.h> |
| #include <malloc.h> |
| #include <miiphy.h> |
| #include <mmc.h> |
| #include <mxsfb.h> |
| #include <netdev.h> |
| #include <usb.h> |
| #include <usb/ehci-fsl.h> |
| #include <fuse.h> |
| #include <rsa-imx.h> |
| #include "../fs/ubifs/ubifs.h" |
| |
| #include <power/pmic.h> |
| #include <power/pfuze100_pmic.h> |
| |
| #ifdef CONFIG_FASTBOOT |
| #include <fastboot.h> |
| #ifdef CONFIG_ANDROID_RECOVERY |
| #include <recovery.h> |
| #endif |
| #endif /*CONFIG_FASTBOOT*/ |
| |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| #define UART_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ |
| PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ |
| PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) |
| |
| #define GPMI_PAD_CTRL0 (PAD_CTL_PKE | PAD_CTL_PUE | PAD_CTL_PUS_100K_UP) |
| #define GPMI_PAD_CTRL1 (PAD_CTL_DSE_40ohm | PAD_CTL_SPEED_MED | \ |
| PAD_CTL_SRE_FAST) |
| #define GPMI_PAD_CTRL2 (GPMI_PAD_CTRL0 | GPMI_PAD_CTRL1) |
| |
| |
| int dram_init(void) |
| { |
| extern void dram_fixup(void); |
| extern char _dram_fixup_start, _dram_fixup_end; |
| |
| char *src, *dst; |
| size_t len, start, end; |
| void (*func)(void); |
| |
| dst = (void*)DRAM_FIXUP_IRAM_ADDR; |
| src = &_dram_fixup_start; |
| len = &_dram_fixup_end - &_dram_fixup_start; |
| memcpy(dst, src, len); |
| |
| start = (size_t)dst; |
| end = roundup(start + len, CONFIG_SYS_CACHELINE_SIZE); |
| invalidate_icache_all(); |
| invalidate_dcache_range(start, end); |
| |
| func = (void*)(dst - src + (size_t)dram_fixup); |
| func(); |
| |
| gd->ram_size = get_ram_size((void *)PHYS_SDRAM, PHYS_SDRAM_SIZE); |
| |
| return 0; |
| } |
| |
| static iomux_v3_cfg_t const wdog_pads[] = { |
| MX6_PAD_GPIO1_IO08__WDOG1_WDOG_B | MUX_PAD_CTRL(NO_PAD_CTRL), |
| }; |
| |
| // Debug UART |
| static iomux_v3_cfg_t const uart1_pads[] = { |
| MX6_PAD_UART1_TX_DATA__UART1_DCE_TX | MUX_PAD_CTRL(UART_PAD_CTRL), |
| MX6_PAD_UART1_RX_DATA__UART1_DCE_RX | MUX_PAD_CTRL(UART_PAD_CTRL), |
| }; |
| |
| static iomux_v3_cfg_t const ok2boot_pads[] = { |
| MX6_PAD_ENET1_TX_EN__GPIO2_IO05 | MUX_PAD_CTRL(NO_PAD_CTRL) |
| }; |
| |
| static iomux_v3_cfg_t const ram_pads[] = { |
| MX6_PAD_ENET2_RX_EN__GPIO2_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL) |
| }; |
| |
| #ifdef CONFIG_SYS_USE_NAND |
| static iomux_v3_cfg_t const nand_pads[] = { |
| MX6_PAD_NAND_DATA00__RAWNAND_DATA00 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DATA01__RAWNAND_DATA01 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DATA02__RAWNAND_DATA02 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DATA03__RAWNAND_DATA03 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DATA04__RAWNAND_DATA04 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DATA05__RAWNAND_DATA05 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DATA06__RAWNAND_DATA06 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DATA07__RAWNAND_DATA07 | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_CLE__RAWNAND_CLE | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_ALE__RAWNAND_ALE | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_CE0_B__RAWNAND_CE0_B | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_RE_B__RAWNAND_RE_B | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_WE_B__RAWNAND_WE_B | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_WP_B__RAWNAND_WP_B | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_READY_B__RAWNAND_READY_B | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| MX6_PAD_NAND_DQS__RAWNAND_DQS | MUX_PAD_CTRL(GPMI_PAD_CTRL2), |
| }; |
| |
| /* Mux out the UART for security */ |
| static iomux_v3_cfg_t const uart1_pads_disabled[] = { |
| MX6_PAD_UART1_TX_DATA__GPIO1_IO16 | MUX_PAD_CTRL(NO_PAD_CTRL), |
| MX6_PAD_UART1_RX_DATA__GPIO1_IO17 | MUX_PAD_CTRL(NO_PAD_CTRL), |
| }; |
| |
| void disable_iomux_uart(void) |
| { |
| imx_iomux_v3_setup_multiple_pads(uart1_pads_disabled, ARRAY_SIZE(uart1_pads_disabled)); |
| } |
| |
| static void setup_gpmi_nand(void) |
| { |
| struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; |
| |
| /* config gpmi nand iomux */ |
| imx_iomux_v3_setup_multiple_pads(nand_pads, ARRAY_SIZE(nand_pads)); |
| |
| clrbits_le32(&mxc_ccm->CCGR4, |
| MXC_CCM_CCGR4_RAWNAND_U_BCH_INPUT_APB_MASK | |
| MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_BCH_MASK | |
| MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK | |
| MXC_CCM_CCGR4_RAWNAND_U_GPMI_INPUT_APB_MASK | |
| MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_MASK); |
| |
| /* |
| * config gpmi and bch clock to 100 MHz |
| * bch/gpmi select PLL2 PFD2 400M |
| * 100M = 400M / 4 |
| */ |
| clrbits_le32(&mxc_ccm->cscmr1, |
| MXC_CCM_CSCMR1_BCH_CLK_SEL | |
| MXC_CCM_CSCMR1_GPMI_CLK_SEL); |
| clrsetbits_le32(&mxc_ccm->cscdr1, |
| MXC_CCM_CSCDR1_BCH_PODF_MASK | |
| MXC_CCM_CSCDR1_GPMI_PODF_MASK, |
| (3 << MXC_CCM_CSCDR1_BCH_PODF_OFFSET) | |
| (3 << MXC_CCM_CSCDR1_GPMI_PODF_OFFSET)); |
| |
| /* enable gpmi and bch clock gating */ |
| setbits_le32(&mxc_ccm->CCGR4, |
| MXC_CCM_CCGR4_RAWNAND_U_BCH_INPUT_APB_MASK | |
| MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_BCH_MASK | |
| MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK | |
| MXC_CCM_CCGR4_RAWNAND_U_GPMI_INPUT_APB_MASK | |
| MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_MASK); |
| |
| /* enable apbh clock gating */ |
| setbits_le32(&mxc_ccm->CCGR0, MXC_CCM_CCGR0_APBHDMA_MASK); |
| } |
| #endif |
| |
| static void setup_iomux_wdog(void) |
| { |
| imx_iomux_v3_setup_multiple_pads(wdog_pads, ARRAY_SIZE(wdog_pads)); |
| } |
| |
| static void setup_iomux_uart(void) |
| { |
| imx_iomux_v3_setup_multiple_pads(uart1_pads, ARRAY_SIZE(uart1_pads)); |
| } |
| |
| static void setup_piezo_enable_gpio(void) |
| { |
| imx_iomux_v3_setup_pad(MX6_PAD_ENET1_TX_CLK__GPIO2_IO06 | MUX_PAD_CTRL(NO_PAD_CTRL)); |
| gpio_direction_output(IMX_GPIO_NR(2, 6), 1); |
| } |
| |
| static void setup_iomux_6lo(void) |
| { |
| /* Hold 6Lo in reset. (Proto and later. No impact on other revisions.) */ |
| imx_iomux_v3_setup_pad(MX6_PAD_SD1_DATA1__GPIO2_IO19 | MUX_PAD_CTRL(NO_PAD_CTRL)); |
| gpio_direction_output(IMX_GPIO_NR(2, 19), 0); |
| } |
| |
| static void setup_iomux_ok2boot(void) |
| { |
| imx_iomux_v3_setup_multiple_pads(ok2boot_pads, ARRAY_SIZE(ok2boot_pads)); |
| } |
| |
| static void setup_iomux_ram(void) |
| { |
| imx_iomux_v3_setup_multiple_pads(ram_pads, ARRAY_SIZE(ram_pads)); |
| gpio_direction_output(GPIO_RAM_PWR_EN, 0); |
| } |
| |
| static void battery_trap(void) |
| { |
| extern void suspend(void); |
| extern char _suspend_start, _suspend_end; |
| |
| struct gpc *gpc = (struct gpc *)GPC_BASE_ADDR; |
| struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; |
| struct pgc *pgc_pcie_phy = (struct pgc *)PGC_PCIE_PHY_BASE_ADDR; |
| struct pgc *pgc_mega = (struct pgc *)PGC_MEGA_BASE_ADDR; |
| struct pgc *pgc_cpu = (struct pgc *)PGC_CPU_BASE_ADDR; |
| struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; |
| |
| u32 val; |
| char *src, *dst; |
| size_t len, start, end; |
| void (*func)(void); |
| |
| printf("Entering Battery Trap...\n"); |
| |
| setup_iomux_ram(); |
| |
| /* Disable All Interrupts */ |
| disable_interrupts(); |
| __raw_writel(~0, &gpc->imr1); |
| __raw_writel(~0, &gpc->imr2); |
| __raw_writel(~0, &gpc->imr3); |
| __raw_writel(~0, &gpc->imr4); |
| |
| val = __raw_readl(&mxc_ccm->clpcr); |
| /* Enable STOP Mode on DSM */ |
| val &= ~MXC_CCM_CLPCR_LPM_MASK; |
| val |= (0x2 << MXC_CCM_CLPCR_LPM_OFFSET) & MXC_CCM_CLPCR_LPM_MASK; |
| /* Enable Standby Voltage in STOP Mode */ |
| val |= MXC_CCM_CLPCR_VSTBY; |
| /* Disable On-Chip Oscillator in STOP Mode */ |
| val |= MXC_CCM_CLPCR_SBYOS; |
| /* Bypass MMDC Handshake for STOP Mode Entry */ |
| val |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS; |
| __raw_writel(val, &mxc_ccm->clpcr); |
| |
| /* Disable Memory Clock Regardless of Pending Interrups on WFI */ |
| clrbits_le32(&mxc_ccm->cgpr, MXC_CCM_CGPR_INT_MEM_CLK_LPM); |
| |
| /* Enable Regulator Bypass After Standby Voltage */ |
| setbits_le32(&mxc_ccm->ccr, MXC_CCM_CCR_RBC_EN); |
| |
| /* Power-Down All Optional Domains in Low-Power Mode */ |
| setbits_le32(&pgc_pcie_phy->ctrl, PGC_CTRL_PDN); |
| setbits_le32(&pgc_mega->ctrl, PGC_CTRL_PDN); |
| setbits_le32(&pgc_cpu->ctrl, PGC_CTRL_PDN); |
| |
| /* Turn Off All Regulators */ |
| __raw_writel(BM_ANADIG_REG_1P1_ENABLE_PULLDOWN, &anatop->reg_1p1_set); |
| __raw_writel(BM_ANADIG_REG_2P5_ENABLE_PULLDOWN, &anatop->reg_2p5_set); |
| __raw_writel(BM_ANADIG_REG_CORE_FET_ODRIVE, &anatop->reg_core_set); |
| __raw_writel(BM_ANADIG_ANA_MISC0_V2_DISCON_HIGH_SNVS, &anatop->ana_misc0_set); |
| __raw_writel(BM_ANADIG_ANA_MISC0_V3_STOP_MODE_CONFIG, &anatop->ana_misc0_set); |
| |
| dst = (void*)SUSPEND_IRAM_ADDR; |
| src = &_suspend_start; |
| len = &_suspend_end - &_suspend_start; |
| memcpy(dst, src, len); |
| |
| start = (size_t)dst; |
| end = roundup(start + len, CONFIG_SYS_CACHELINE_SIZE); |
| invalidate_icache_all(); |
| invalidate_dcache_range(start, end); |
| |
| func = (void*)(dst - src + (size_t)suspend); |
| func(); |
| } |
| |
| void battery_check(void) |
| { |
| if (gpio_get_value(GPIO_OK2BOOT)) { |
| battery_trap(); |
| } |
| } |
| |
| static void setup_clocks(void) |
| { |
| struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; |
| |
| u32 val; |
| |
| val = __raw_readl(&mxc_ccm->cacrr); |
| val &= ~MXC_CCM_CACRR_ARM_PODF_MASK; |
| val |= CLOCK_PODF_ARM << MXC_CCM_CACRR_ARM_PODF_OFFSET; |
| __raw_writel(val, &mxc_ccm->cacrr); |
| |
| val = __raw_readl(&mxc_ccm->cbcdr); |
| val &= ~MXC_CCM_CBCDR_MMDC_CH1_PODF_MASK; |
| val |= CLOCK_PODF_DDR << MXC_CCM_CBCDR_MMDC_CH1_PODF_OFFSET; |
| __raw_writel(val, &mxc_ccm->cbcdr); |
| } |
| |
| /* RSA unlock token check ******************************************************/ |
| #define OTP_BANK_UID 0 |
| #define OTP_WORD_UID_L 1 |
| #define OTP_WORD_UID_H 2 |
| #define OTP_BANK_SEC_CONFIG 0 |
| #define OTP_WORD_SEC_CONFIG 6 |
| #define SEC_CONFIG_CLOSED 0x00000002 |
| #define UNLOCK_TOKEN_SIZE_BYTES 256 |
| #define UNLOCK_TOKEN_BASE64_LEN 344 // The 256-Byte unlock token should ALWAYS be 344 Bytes long when base64 encoded |
| #define UNLOCK_TOKEN_ADDRESS 0x82FFF000 // This is the address where the DFU tool needs to stick the unlock token when doing DFU |
| #define UNLOCK_TOKEN_PARTITION "ubi0:system-config" |
| #define SHA256_CHUNK_SZ 64 |
| |
| // If CONFIG_UNLOCK_TOKEN_PREFIX is defined (basically all but the D3 legacy devices), |
| // check that all unlock tokens are prefixed with it. |
| // This disambiguates tokens accidentally generated for the wrong build product. |
| #ifndef CONFIG_UNLOCK_TOKEN_PREFIX |
| #error CONFIG_UNLOCK_TOKEN_PREFIX not defined! |
| #endif |
| #define UNLOCK_TOKEN_PREFIX_LEN (strlen(CONFIG_UNLOCK_TOKEN_PREFIX)) |
| |
| /* Lookup the 6-bit value for a base64-encoded character. |
| * Invalid characters will return 0x7f and '=' end padding characters will always return zero. |
| */ |
| static char base64_lookup(char in) { |
| uint8_t ret = 0x7f; |
| |
| if ((in >= 'A') && (in <= 'Z')) { |
| ret = in - 'A'; |
| } else if ((in >= 'a') && (in <= 'z')) { |
| ret = in - 'a' + 26; |
| } else if ((in >= '0') && (in <= '9')) { |
| ret = in - '0' + 52; |
| } else if (in == '+') { |
| ret = 62; |
| } else if (in == '/') { |
| ret = 63; |
| } else if (in == '=') { |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| /* Decode a base64-encoded string. |
| * This implementation assumes the lengths have been checked, such that the encoded data will always fit |
| * The source and destination buffers may be the same, but should not otherwise overlap. |
| * The destination buffer must be large enough hold the output if the expected input has two '=' padding |
| * characters and those characters are replaced with other valid base64 words. |
| */ |
| static int base64_decode(char *encoded_data, char *decoded_data, bool silent) { |
| int idx_in, idx_out, |
| len = strlen(encoded_data), |
| ret = 0; |
| |
| if (((len % 4) != 0) || (len == 0)) { |
| printf("%s: Invalid length: %d\n", __func__, len); |
| decoded_data[0] = 0; |
| ret = -1; |
| } else { |
| idx_in = idx_out = 0; |
| while ((!ret) && (idx_in < len)) { |
| char a = base64_lookup(encoded_data[idx_in++]), |
| b = base64_lookup(encoded_data[idx_in++]), |
| c = base64_lookup(encoded_data[idx_in++]), |
| d = base64_lookup(encoded_data[idx_in++]); |
| |
| if ((a > 0x3f) || (b > 0x3f) || (c > 0x3f) || (d > 0x3f)) { |
| if (!silent) { |
| printf("%s: Invalid character near Byte %i\n", __func__, (idx_in - 3)); |
| } |
| ret = -1; |
| } else { |
| decoded_data[idx_out++] = ((a & 0x3f) << 2) | ((b & 0x30) >> 4); |
| decoded_data[idx_out++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); |
| decoded_data[idx_out++] = ((c & 0x03) << 6) | (d & 0x3f); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* Check for a valid unlock token, either in RAM or in the filesystem. |
| * The unlock token is the UID string (ASCII hex encoded, with no CR/LF appended), |
| * signed with the private unlocking key and base64 encoded. |
| * |
| * Tokens can be generated as follows (for UIDs '2a1429d4de730bfc' and '102551d4df669959'): |
| * > device_UIDs=("2a1429d4de730bfc" "102551d4df669959") |
| * > unlock_key=/dev/shm/diamond3-unlock.key |
| * > unlock_token_folder=${BuildRoot}/keys/ |
| * > for device_UID in "${device_UIDs[@]}"; do |
| * > echo -n "${device_UID}" | openssl dgst -sha256 -passin env:passcode -sign ${unlock_key} | base64 --wrap=0 > ${unlock_token_folder}/${device_UID} |
| * > done |
| * |
| * The token can be loaded at boot time when booting via DFU by loading it to UNLOCK_TOKEN_ADDRESS |
| * The token can be permanently stored by copying the token file (filename is the UID) to the root of UNLOCK_TOKEN_PARTITION |
| */ |
| static bool board_unlock_token_check(char *UID_str) |
| { |
| bool unlocked = false; |
| void *blob = ((void *)gd_fdt_blob()); |
| int sig_node, unlock_node; |
| uint8_t hash[SHA256_SUM_LEN]; |
| sha256_context ctx; |
| struct imx_rsa_public_key key; |
| char *unlock_token_source; |
| char *unlock_data = ((char *)UNLOCK_TOKEN_ADDRESS); |
| int ret = 0; |
| |
| // Stretch the UID ASCII hex string into a SHA-256 hash so that we can |
| // pad it in a standard way |
| sha256_starts(&ctx); |
| sha256_update(&ctx, (uint8_t *)UID_str, strlen(UID_str)); |
| sha256_finish(&ctx, hash); |
| |
| // Locate the signature DT node |
| sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME); |
| if (sig_node < 0) { |
| printf("%s: No signature node found\n", __func__); |
| ret = -1; |
| } |
| if (!ret) { |
| // Locate the public unlocking key DT node |
| unlock_node = fdt_subnode_offset(blob, sig_node, "unlock"); |
| if (unlock_node < 0) { |
| printf("%s: No public unlocking key node found\n", __func__); |
| ret = -1; |
| } |
| } |
| if (!ret) { |
| // Read the public key from the DT unlock_node into memory |
| ret = rsa_imx_get_key(blob, unlock_node, &key); |
| if (ret) { |
| printf("%s: RSA failed to load public unlocking key: %d\n", __func__, ret); |
| } |
| } |
| if (!ret) { |
| // Use the public key to check if there is a valid signature in memory |
| unlock_data[UNLOCK_TOKEN_PREFIX_LEN + UNLOCK_TOKEN_BASE64_LEN] = 0; |
| if (strlen(unlock_data) != (UNLOCK_TOKEN_PREFIX_LEN + UNLOCK_TOKEN_BASE64_LEN)) { |
| ret = -1; |
| } else if (strncmp(unlock_data, CONFIG_UNLOCK_TOKEN_PREFIX, UNLOCK_TOKEN_PREFIX_LEN)) { |
| ret = -1; |
| } else if (base64_decode(&unlock_data[UNLOCK_TOKEN_PREFIX_LEN], unlock_data, true) != 0) { |
| ret = -1; |
| } else { |
| ret = rsa_imx_verify_key(&key, (uint8_t *)unlock_data, UNLOCK_TOKEN_SIZE_BYTES, hash); |
| unlock_token_source = "RAM"; |
| } |
| |
| if (ret) { |
| unlock_data[UNLOCK_TOKEN_PREFIX_LEN + UNLOCK_TOKEN_BASE64_LEN] = 0; |
| |
| // Init UBIFS |
| ubifs_init(); |
| |
| // Mount the partition containing the token |
| if (uboot_ubifs_mount(UNLOCK_TOKEN_PARTITION)) { |
| printf("Could not mount " UNLOCK_TOKEN_PARTITION "\n"); |
| // Attempt to read a file with a name that matches the UID. This will fail silently if the file is absent. |
| } else if (ubifs_load(UID_str, (u32) unlock_data, (UNLOCK_TOKEN_PREFIX_LEN + UNLOCK_TOKEN_BASE64_LEN))) { |
| // Do nothing |
| } else if (strlen(unlock_data) != (UNLOCK_TOKEN_PREFIX_LEN + UNLOCK_TOKEN_BASE64_LEN)) { |
| printf("%s: Unlock token length incorrect: %d\n", __func__, strlen(unlock_data)); |
| } else if (strncmp(unlock_data, CONFIG_UNLOCK_TOKEN_PREFIX, UNLOCK_TOKEN_PREFIX_LEN)) { |
| printf("%s: Unlock token prefix invalid\n", __func__); |
| } else if (base64_decode(&unlock_data[UNLOCK_TOKEN_PREFIX_LEN], unlock_data, false) != 0) { |
| printf("%s: Unlock token encoding invalid\n", __func__); |
| } else { |
| ret = rsa_imx_verify_key(&key, (uint8_t *)unlock_data, UNLOCK_TOKEN_SIZE_BYTES, hash); |
| unlock_token_source = "filesystem"; |
| } |
| } |
| |
| if (ret) { |
| printf("Unlocking token not present or invalid (%d)\n", ret); |
| } else { |
| printf("Unlocking token verified from %s\n", unlock_token_source); |
| unlocked = true; |
| } |
| } |
| |
| return unlocked; |
| } |
| |
| /* Get the 64-bit unique silicon ID and convert it to ASCII hex so that it can be used to generate an unlock token |
| * Also get the SOC SEC_CONFIG value burned into OCOTP so that we can determine the SOC's security state |
| */ |
| int board_get_UID_sec_config(char *UID_str, int maxlen, uint32_t *sec_config) { |
| uint32_t UID[2]; // The 64-bit UID |
| int ret; |
| |
| memset(UID_str, 0x00, maxlen); |
| |
| ret = fuse_read(OTP_BANK_UID, OTP_WORD_UID_L, &UID[0]); |
| if (!ret) |
| ret = fuse_read(OTP_BANK_UID, OTP_WORD_UID_H, &UID[1]); |
| if (!ret) |
| ret = fuse_read(OTP_BANK_SEC_CONFIG, OTP_WORD_SEC_CONFIG, sec_config); |
| if (!ret) |
| snprintf(UID_str, maxlen, "%08x%08x", UID[1], UID[0]); |
| |
| return ret; |
| } |
| |
| /* Switch the system into either the fully locked or fully unlocked state */ |
| static void board_update_unlocked(bool unlocked) |
| { |
| void *blob = ((void *)gd_fdt_blob()); |
| int sig_node; |
| int ret = 0; |
| |
| // Update the system state based on locked/unlocked status |
| if (unlocked) { |
| // Update the control FDT's boot config to disable ITB signature checking, by finding all |
| // subnodes in the control FDT that contain a "required" property and set the value to "none". |
| sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME); |
| if (sig_node < 0) { |
| // This will happen on systems that don't have the public |
| // key tree, so we don't make any noise about it. |
| debug("%s: Unable to get sig node: %d\n", __func__, ret); |
| } else { |
| int noffset = fdt_first_subnode(blob, sig_node); |
| while ((noffset >= 0) && (!ret)) { |
| if (fdt_getprop(blob, noffset, "required", NULL)) { |
| ret = fdt_setprop(blob, noffset, "required", "none", 4); |
| } |
| noffset = fdt_next_subnode(blob, noffset); |
| } |
| } |
| |
| // Use the boot delay defined in the environment (via CONFIG_BOOTDELAY) |
| |
| } else { |
| // Give any outgoing characters time to clear the UART before we mux it out |
| udelay(100); |
| |
| // Override the default env variable 'console', which gets passed to kernel cmdline and point it to /dev/null |
| setenv("console", "/dev/null"); |
| |
| // Always autoboot with no delay and don't check for an abort |
| setenv("bootdelay", "-2"); |
| |
| // Mux out the UART |
| disable_iomux_uart(); |
| |
| // Flush out any data received before the UART was muxed out |
| while (tstc()) { |
| getc(); |
| } |
| } |
| |
| if (ret) { |
| printf("%s: Failed updating security config: %d\n", __func__, ret); |
| } |
| } |
| |
| int board_early_init_f(void) |
| { |
| setup_iomux_wdog(); |
| |
| setup_iomux_uart(); |
| |
| setup_piezo_enable_gpio(); |
| |
| setup_iomux_6lo(); |
| |
| setup_iomux_ok2boot(); |
| |
| battery_check(); |
| |
| setup_clocks(); |
| |
| return 0; |
| } |
| |
| int board_init(void) |
| { |
| /* Address of boot parameters */ |
| gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; |
| |
| #ifdef CONFIG_SYS_USE_NAND |
| setup_gpmi_nand(); |
| #endif |
| |
| return 0; |
| } |
| |
| void env_sanitize(void) |
| { |
| ALLOC_CACHE_ALIGN_BUFFER(env_t, export, 1); |
| char *import = (char *)export; |
| char *whitelist[] = |
| { |
| CONFIG_ENV_WHITELIST |
| }; |
| char *ptr = NULL; |
| ssize_t size; |
| bool sanitize; |
| |
| #ifdef CONFIG_CMD_SAVEENV |
| /* env can't be sanitized because saving would clear stored data */ |
| sanitize = false |
| #else |
| /* env can't be sanitized unless loading was deferred */ |
| sanitize = !fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1); |
| #endif |
| |
| if (sanitize) { |
| env_export(export); |
| env_relocate(); |
| size = hexport_r(&env_htab, '\0', H_MATCH_KEY | H_MATCH_IDENT, |
| &ptr, 0, ARRAY_SIZE(whitelist), whitelist); |
| env_import(import, 1); |
| if (ptr != NULL) { |
| himport_r(&env_htab, ptr, size, '\0', H_NOCLEAR, false, 0, NULL); |
| free(ptr); |
| } |
| } else { |
| printf("*** Warning - cannot sanitize env!\n"); |
| } |
| } |
| |
| int board_late_init(void) |
| { |
| char UID_str[SHA256_CHUNK_SZ]; // The UID's ASCII hex string representation. Size needs to be multiple of the SHA-256 chunk size. |
| uint32_t sec_config; |
| bool unlocked = false; |
| |
| set_wdog_reset((struct wdog_regs *)WDOG1_BASE_ADDR); |
| |
| env_sanitize(); |
| |
| /* Get board UID and SEC_CONFIG so that we can determine system security state */ |
| if (board_get_UID_sec_config(UID_str, SHA256_CHUNK_SZ, &sec_config)) { |
| printf("%s: Error reading fuses\n", __func__); |
| } else { |
| printf("UID: %s\n", UID_str); |
| |
| if ((sec_config & SEC_CONFIG_CLOSED) == 0) { |
| /* Always unlock units that are still in open mode (i.e. SEC_CONFIG[1] != 1). |
| * This enables the release bootloader to be used at the factory without requiring |
| * unlocking tokens for units that haven't been through the shipping settings station. |
| * It also enables development on devices that aren't secure, or for which keys have |
| * not yet been generated. |
| */ |
| printf("Secure boot not enabled, device unlocked\n"); |
| unlocked = true; |
| } else { |
| /* Check for the presence of the unlock token */ |
| unlocked = board_unlock_token_check(UID_str); |
| } |
| } |
| |
| /* Update state based on locked/unlocked status */ |
| board_update_unlocked(unlocked); |
| |
| return 0; |
| } |
| |
| u32 get_board_rev(void) |
| { |
| return get_cpu_rev(); |
| } |
| |
| int checkboard(void) |
| { |
| puts("Board: Onyx\n"); |
| |
| return 0; |
| } |