diff --git a/Makefile b/Makefile index 3fb6393c90..d3222a086c 100644 --- a/Makefile +++ b/Makefile @@ -957,7 +957,8 @@ MKIMAGEFLAGS_u-boot.kwb = -n $(srctree)/$(CONFIG_SYS_KWD_CONFIG:"%"=%) \ -T kwbimage -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) MKIMAGEFLAGS_u-boot-spl.kwb = -n $(srctree)/$(CONFIG_SYS_KWD_CONFIG:"%"=%) \ - -T kwbimage -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) + -T kwbimage -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) \ + $(if $(KEYDIR),-k $(KEYDIR)) MKIMAGEFLAGS_u-boot.pbl = -n $(srctree)/$(CONFIG_SYS_FSL_PBL_RCW:"%"=%) \ -R $(srctree)/$(CONFIG_SYS_FSL_PBL_PBI:"%"=%) -T pblimage diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 53117c4296..412bda4160 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -1,5 +1,9 @@ if ARCH_MVEBU +config HAVE_MVEBU_EFUSE + bool + default n + config ARMADA_32BIT bool select CPU_V7 @@ -23,6 +27,7 @@ config ARMADA_375 config ARMADA_38X bool select ARMADA_32BIT + select HAVE_MVEBU_EFUSE config ARMADA_XP bool @@ -146,4 +151,34 @@ config SYS_VENDOR config SYS_SOC default "mvebu" +config MVEBU_EFUSE + bool "Enable eFuse support" + default n + depends on HAVE_MVEBU_EFUSE + help + Enable support for reading and writing eFuses on mvebu SoCs. + +config MVEBU_EFUSE_FAKE + bool "Fake eFuse access (dry run)" + default n + depends on MVEBU_EFUSE + help + This enables a "dry run" mode where eFuses are not really programmed. + Instead the eFuse accesses are emulated by writing to and reading + from a memory block. + This is can be used for testing prog scripts. + +config SECURED_MODE_IMAGE + bool "Build image for trusted boot" + default false + depends on 88F6820 + help + Build an image that employs the ARMADA SoC's trusted boot framework + for securely booting images. + +config SECURED_MODE_CSK_INDEX + int "Index of active CSK" + default 0 + depends on SECURED_MODE_IMAGE + endif diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 65e90c4fc9..d4210af9d2 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -27,6 +27,7 @@ ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ARMADA_375) += ../../../drivers/ddr/marvell/axp/xor.o obj-$(CONFIG_ARMADA_38X) += ../../../drivers/ddr/marvell/a38x/xor.o obj-$(CONFIG_ARMADA_XP) += ../../../drivers/ddr/marvell/axp/xor.o +obj-$(CONFIG_MVEBU_EFUSE) += efuse.o endif # CONFIG_SPL_BUILD obj-y += gpio.o obj-y += mbus.o diff --git a/arch/arm/mach-mvebu/efuse.c b/arch/arm/mach-mvebu/efuse.c new file mode 100644 index 0000000000..67fcadcf60 --- /dev/null +++ b/arch/arm/mach-mvebu/efuse.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2015-2016 Reinhard Pfau + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_MVEBU_EFUSE_FAKE) +#define DRY_RUN +#else +#undef DRY_RUN +#endif + +#define MBUS_EFUSE_BASE 0xF6000000 +#define MBUS_EFUSE_SIZE BIT(20) + +#define MVEBU_EFUSE_CONTROL (MVEBU_REGISTER(0xE4008)) + +enum { + MVEBU_EFUSE_CTRL_PROGRAM_ENABLE = (1 << 31), +}; + +struct mvebu_hd_efuse { + u32 bits_31_0; + u32 bits_63_32; + u32 bit64; + u32 reserved0; +}; + +#ifndef DRY_RUN +static struct mvebu_hd_efuse *efuses = + (struct mvebu_hd_efuse *)(MBUS_EFUSE_BASE + 0xF9000); +#else +static struct mvebu_hd_efuse efuses[EFUSE_LINE_MAX + 1]; +#endif + +static int efuse_initialised; + +static struct mvebu_hd_efuse *get_efuse_line(int nr) +{ + if (nr < 0 || nr > 63 || !efuse_initialised) + return NULL; + + return efuses + nr; +} + +static void enable_efuse_program(void) +{ +#ifndef DRY_RUN + setbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); +#endif +} + +static void disable_efuse_program(void) +{ +#ifndef DRY_RUN + clrbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); +#endif +} + +static int do_prog_efuse(struct mvebu_hd_efuse *efuse, + struct efuse_val *new_val, u32 mask0, u32 mask1) +{ + struct efuse_val val; + + val.dwords.d[0] = readl(&efuse->bits_31_0); + val.dwords.d[1] = readl(&efuse->bits_63_32); + val.lock = readl(&efuse->bit64); + + if (val.lock & 1) + return -EPERM; + + val.dwords.d[0] |= (new_val->dwords.d[0] & mask0); + val.dwords.d[1] |= (new_val->dwords.d[1] & mask1); + val.lock |= new_val->lock; + + writel(val.dwords.d[0], &efuse->bits_31_0); + mdelay(1); + writel(val.dwords.d[1], &efuse->bits_63_32); + mdelay(1); + writel(val.lock, &efuse->bit64); + mdelay(5); + + return 0; +} + +static int prog_efuse(int nr, struct efuse_val *new_val, u32 mask0, u32 mask1) +{ + struct mvebu_hd_efuse *efuse; + int res = 0; + + res = mvebu_efuse_init_hw(); + if (res) + return res; + + efuse = get_efuse_line(nr); + if (!efuse) + return -ENODEV; + + if (!new_val) + return -EINVAL; + + /* only write a fuse line with lock bit */ + if (!new_val->lock) + return -EINVAL; + + /* according to specs ECC protection bits must be 0 on write */ + if (new_val->bytes.d[7] & 0xFE) + return -EINVAL; + + if (!new_val->dwords.d[0] && !new_val->dwords.d[1] && (mask0 | mask1)) + return 0; + + enable_efuse_program(); + + res = do_prog_efuse(efuse, new_val, mask0, mask1); + + disable_efuse_program(); + + return res; +} + +int mvebu_efuse_init_hw(void) +{ + int ret; + + if (efuse_initialised) + return 0; + + ret = mvebu_mbus_add_window_by_id( + CPU_TARGET_SATA23_DFX, 0xA, MBUS_EFUSE_BASE, MBUS_EFUSE_SIZE); + + if (ret) + return ret; + + efuse_initialised = 1; + + return 0; +} + +int mvebu_read_efuse(int nr, struct efuse_val *val) +{ + struct mvebu_hd_efuse *efuse; + int res; + + res = mvebu_efuse_init_hw(); + if (res) + return res; + + efuse = get_efuse_line(nr); + if (!efuse) + return -ENODEV; + + if (!val) + return -EINVAL; + + val->dwords.d[0] = readl(&efuse->bits_31_0); + val->dwords.d[1] = readl(&efuse->bits_63_32); + val->lock = readl(&efuse->bit64); + return 0; +} + +int mvebu_write_efuse(int nr, struct efuse_val *val) +{ + return prog_efuse(nr, val, ~0, ~0); +} + +int mvebu_lock_efuse(int nr) +{ + struct efuse_val val = { + .lock = 1, + }; + + return prog_efuse(nr, &val, 0, 0); +} + +/* + * wrapper funcs providing the fuse API + * + * we use the following mapping: + * "bank" -> eFuse line + * "word" -> 0: bits 0-31 + * 1: bits 32-63 + * 2: bit 64 (lock) + */ + +static struct efuse_val prog_val; +static int valid_prog_words; + +int fuse_read(u32 bank, u32 word, u32 *val) +{ + struct efuse_val fuse_line; + int res; + + if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) + return -EINVAL; + + res = mvebu_read_efuse(bank, &fuse_line); + if (res) + return res; + + if (word < 2) + *val = fuse_line.dwords.d[word]; + else + *val = fuse_line.lock; + + return res; +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + /* not supported */ + return -ENOSYS; +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + int res = 0; + + /* + * NOTE: Fuse line should be written as whole. + * So how can we do that with this API? + * For now: remember values for word == 0 and word == 1 and write the + * whole line when word == 2. + * This implies that we always require all 3 fuse prog cmds (one for + * for each word) to write a single fuse line. + * Exception is a single write to word 2 which will lock the fuse line. + * + * Hope that will be OK. + */ + + if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) + return -EINVAL; + + if (word < 2) { + prog_val.dwords.d[word] = val; + valid_prog_words |= (1 << word); + } else if ((valid_prog_words & 3) == 0 && val) { + res = mvebu_lock_efuse(bank); + valid_prog_words = 0; + } else if ((valid_prog_words & 3) != 3 || !val) { + res = -EINVAL; + } else { + prog_val.lock = val != 0; + res = mvebu_write_efuse(bank, &prog_val); + valid_prog_words = 0; + } + + return res; +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + /* not supported */ + return -ENOSYS; +} diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h index 66f7680fb3..d241eea956 100644 --- a/arch/arm/mach-mvebu/include/mach/cpu.h +++ b/arch/arm/mach-mvebu/include/mach/cpu.h @@ -36,7 +36,9 @@ enum cpu_target { CPU_TARGET_ETH01 = 0x7, CPU_TARGET_PCIE13 = 0x8, CPU_TARGET_SASRAM = 0x9, + CPU_TARGET_SATA01 = 0xa, /* A38X */ CPU_TARGET_NAND = 0xd, + CPU_TARGET_SATA23_DFX = 0xe, /* A38X */ }; enum cpu_attrib { diff --git a/arch/arm/mach-mvebu/include/mach/efuse.h b/arch/arm/mach-mvebu/include/mach/efuse.h new file mode 100644 index 0000000000..ef693e6749 --- /dev/null +++ b/arch/arm/mach-mvebu/include/mach/efuse.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 Reinhard Pfau + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MVEBU_EFUSE_H +#define _MVEBU_EFUSE_H + +#include + +struct efuse_val { + union { + struct { + u8 d[8]; + } bytes; + struct { + u16 d[4]; + } words; + struct { + u32 d[2]; + } dwords; + }; + u32 lock; +}; + +#if defined(CONFIG_ARMADA_38X) + +enum efuse_line { + EFUSE_LINE_SECURE_BOOT = 24, + EFUSE_LINE_PUBKEY_DIGEST_0 = 26, + EFUSE_LINE_PUBKEY_DIGEST_1 = 27, + EFUSE_LINE_PUBKEY_DIGEST_2 = 28, + EFUSE_LINE_PUBKEY_DIGEST_3 = 29, + EFUSE_LINE_PUBKEY_DIGEST_4 = 30, + EFUSE_LINE_CSK_0_VALID = 31, + EFUSE_LINE_CSK_1_VALID = 32, + EFUSE_LINE_CSK_2_VALID = 33, + EFUSE_LINE_CSK_3_VALID = 34, + EFUSE_LINE_CSK_4_VALID = 35, + EFUSE_LINE_CSK_5_VALID = 36, + EFUSE_LINE_CSK_6_VALID = 37, + EFUSE_LINE_CSK_7_VALID = 38, + EFUSE_LINE_CSK_8_VALID = 39, + EFUSE_LINE_CSK_9_VALID = 40, + EFUSE_LINE_CSK_10_VALID = 41, + EFUSE_LINE_CSK_11_VALID = 42, + EFUSE_LINE_CSK_12_VALID = 43, + EFUSE_LINE_CSK_13_VALID = 44, + EFUSE_LINE_CSK_14_VALID = 45, + EFUSE_LINE_CSK_15_VALID = 46, + EFUSE_LINE_FLASH_ID = 47, + EFUSE_LINE_BOX_ID = 48, + + EFUSE_LINE_MIN = 0, + EFUSE_LINE_MAX = 63, +}; + +#endif + +int mvebu_efuse_init_hw(void); + +int mvebu_read_efuse(int nr, struct efuse_val *val); + +int mvebu_write_efuse(int nr, struct efuse_val *val); + +int mvebu_lock_efuse(int nr); + +#endif diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h index 0f69f3341b..0900e4008c 100644 --- a/arch/arm/mach-mvebu/include/mach/soc.h +++ b/arch/arm/mach-mvebu/include/mach/soc.h @@ -67,6 +67,7 @@ #define MVEBU_REG_PCIE_BASE (MVEBU_REGISTER(0x40000)) #define MVEBU_AXP_USB_BASE (MVEBU_REGISTER(0x50000)) #define MVEBU_USB20_BASE (MVEBU_REGISTER(0x58000)) +#define MVEBU_REG_PCIE0_BASE (MVEBU_REGISTER(0x80000)) #define MVEBU_AXP_SATA_BASE (MVEBU_REGISTER(0xa0000)) #define MVEBU_SATA0_BASE (MVEBU_REGISTER(0xa8000)) #define MVEBU_NAND_BASE (MVEBU_REGISTER(0xd0000)) diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c index 98c447ce94..9e5b647e25 100644 --- a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c @@ -13,6 +13,11 @@ #include "ctrl_pex.h" #include "sys_env_lib.h" +__weak void board_pex_config(void) +{ + /* nothing in this weak default implementation */ +} + int hws_pex_config(const struct serdes_map *serdes_map, u8 count) { u32 pex_idx, tmp, next_busno, first_busno, temp_pex_reg, @@ -77,6 +82,9 @@ int hws_pex_config(const struct serdes_map *serdes_map, u8 count) /* Support gen1/gen2 */ DEBUG_INIT_FULL_S("Support gen1/gen2\n"); + + board_pex_config(); + next_busno = 0; mdelay(150); diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h index 5f7e2c7aa2..ca8a4d206a 100644 --- a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h @@ -83,4 +83,6 @@ int pex_local_bus_num_set(u32 pex_if, u32 bus_num); int pex_local_dev_num_set(u32 pex_if, u32 dev_num); u32 pex_config_read(u32 pex_if, u32 bus, u32 dev, u32 func, u32 reg_off); +void board_pex_config(void); + #endif diff --git a/arch/arm/mach-mvebu/spl.c b/arch/arm/mach-mvebu/spl.c index e1c9cdba50..3cf02a54ce 100644 --- a/arch/arm/mach-mvebu/spl.c +++ b/arch/arm/mach-mvebu/spl.c @@ -13,8 +13,6 @@ #include #include -DECLARE_GLOBAL_DATA_PTR; - static u32 get_boot_device(void) { u32 val; diff --git a/board/gdsys/p1022/controlcenterd-id.c b/board/gdsys/p1022/controlcenterd-id.c index 2c6c698fb3..1648f13340 100644 --- a/board/gdsys/p1022/controlcenterd-id.c +++ b/board/gdsys/p1022/controlcenterd-id.c @@ -43,15 +43,6 @@ #define CCDM_AUTO_FIRST_STAGE #endif -/* enums from TCG specs */ -enum { - /* capability areas */ - TPM_CAP_NV_INDEX = 0x00000011, - TPM_CAP_HANDLE = 0x00000014, - /* resource types */ - TPM_RT_KEY = 0x00000001, -}; - /* CCDM specific contants */ enum { /* NV indices */ diff --git a/cmd/tpm.c b/cmd/tpm.c index 312503fb96..625fc43d26 100644 --- a/cmd/tpm.c +++ b/cmd/tpm.c @@ -646,6 +646,64 @@ TPM_COMMAND_NO_ARG(tpm_end_oiap) #endif /* CONFIG_TPM_AUTH_SESSIONS */ +#ifdef CONFIG_TPM_FLUSH_RESOURCES +static int do_tpm_flush(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int type = 0; + + if (argc != 2) + return CMD_RET_USAGE; + + if (strcasecmp(argv[1], "key")) + type = TPM_RT_KEY; + else if (strcasecmp(argv[1], "auth")) + type = TPM_RT_AUTH; + else if (strcasecmp(argv[1], "hash")) + type = TPM_RT_HASH; + else if (strcasecmp(argv[1], "trans")) + type = TPM_RT_TRANS; + else if (strcasecmp(argv[1], "context")) + type = TPM_RT_CONTEXT; + else if (strcasecmp(argv[1], "counter")) + type = TPM_RT_COUNTER; + else if (strcasecmp(argv[1], "delegate")) + type = TPM_RT_DELEGATE; + else if (strcasecmp(argv[1], "daa_tpm")) + type = TPM_RT_DAA_TPM; + else if (strcasecmp(argv[1], "daa_v0")) + type = TPM_RT_DAA_V0; + else if (strcasecmp(argv[1], "daa_v1")) + type = TPM_RT_DAA_V1; + + if (strcasecmp(argv[2], "all")) { + uint16_t res_count; + uint8_t buf[288]; + uint8_t *ptr; + int err; + uint i; + + /* fetch list of already loaded resources in the TPM */ + err = tpm_get_capability(TPM_CAP_HANDLE, type, buf, + sizeof(buf)); + if (err) + return -1; + res_count = get_unaligned_be16(buf); + ptr = buf + 2; + for (i = 0; i < res_count; ++i, ptr += 4) + tpm_flush_specific(get_unaligned_be32(ptr), type); + } else { + uint32_t handle = simple_strtoul(argv[2], NULL, 0); + + if (!handle) + return -1; + tpm_flush_specific(cpu_to_be32(handle), type); + } + + return 0; +} +#endif /* CONFIG_TPM_FLUSH_RESOURCES */ + #define MAKE_TPM_CMD_ENTRY(cmd) \ U_BOOT_CMD_MKENT(cmd, 0, 1, do_tpm_ ## cmd, "", "") @@ -701,6 +759,10 @@ static cmd_tbl_t tpm_commands[] = { U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1, do_tpm_get_pub_key_oiap, "", ""), #endif /* CONFIG_TPM_AUTH_SESSIONS */ +#ifdef CONFIG_TPM_FLUSH_RESOURCES + U_BOOT_CMD_MKENT(flush, 0, 1, + do_tpm_flush, "", ""), +#endif /* CONFIG_TPM_FLUSH_RESOURCES */ }; static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) @@ -750,6 +812,14 @@ U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, " get_capability cap_area sub_cap addr count\n" " - Read bytes of TPM capability indexed by and\n" " to memory address .\n" +#ifdef CONFIG_TPM_FLUSH_RESOURCES +"Resource management functions\n" +" flush resource_type id\n" +" - flushes a resource of type (may be one of key, auth,\n" +" hash, trans, context, counter, delegate, daa_tpm, daa_v0, daa_v1),\n" +" and id from the TPM. Use an of \"all\" to flush all\n" +" resources of that type.\n" +#endif /* CONFIG_TPM_FLUSH_RESOURCES */ #ifdef CONFIG_TPM_AUTH_SESSIONS "Storage functions\n" " loadkey2_oiap parent_handle key_addr key_len usage_auth\n" diff --git a/doc/README.armada-secureboot b/doc/README.armada-secureboot new file mode 100644 index 0000000000..157cb5a231 --- /dev/null +++ b/doc/README.armada-secureboot @@ -0,0 +1,373 @@ +The trusted boot framework on Marvell Armada 38x +================================================ + +Contents: + +1. Overview of the trusted boot +2. Terminology +3. Boot image layout +4. The secured header +5. The secured boot flow +6. Usage example +7. Work to be done +8. Bibliography + +1. Overview of the trusted boot +------------------------------- + +The Armada's trusted boot framework enables the SoC to cryptographically verify +a specially prepared boot image. This can be used to establish a chain of trust +from the boot firmware all the way to the OS. + +To achieve this, the Armada SoC requires a specially prepared boot image, which +contains the relevant cryptographic data, as well as other information +pertaining to the boot process. Furthermore, a eFuse structure (a +one-time-writeable memory) need to be configured in the correct way. + +Roughly, the secure boot process works as follows: + +* Load the header block of the boot image, extract a special "root" public RSA + key from it, and verify its SHA-256 hash against a SHA-256 stored in a eFuse + field. +* Load an array of code signing public RSA keys from the header block, and + verify its RSA signature (contained in the header block as well) using the + "root" RSA key. +* Choose a code signing key, and use it to verify the header block (excluding + the key array). +* Verify the binary image's signature (contained in the header block) using the + code signing key. +* If all checks pass successfully, boot the image. + +The chain of trust is thus as follows: + +* The SHA-256 value in the eFuse field verifies the "root" public key. +* The "root" public key verifies the code signing key array. +* The selected code signing key verifies the header block and the binary image. + +In the special case of building a boot image containing U-Boot as the binary +image, which employs this trusted boot framework, the following tasks need to +be addressed: + +1. Creation of the needed cryptographic key material. +2. Creation of a conforming boot image containing the U-Boot image as binary + image. +3. Burning the necessary eFuse values. + +(1) will be addressed later, (2) will be taken care of by U-Boot's build +system (some user configuration is required, though), and for (3) the necessary +data (essentially a series of U-Boot commands to be entered at the U-Boot +command prompt) will be created by the build system as well. + +The documentation of the trusted boot mode is contained in part 1, chapter +7.2.5 in the functional specification [1], and in application note [2]. + +2. Terminology +-------------- + + CSK - Code Signing Key(s): An array of RSA key pairs, which + are used to sign and verify the secured header and the + boot loader image. + KAK - Key Authentication Key: A RSA key pair, which is used + to sign and verify the array of CSKs. + Header block - The first part of the boot image, which contains the + image's headers (also known as "headers block", "boot + header", and "image header") + eFuse - A one-time-writeable memory. + BootROM - The Armada's built-in boot firmware, which is + responsible for verifying and starting secure images. + Boot image - The complete image the SoC's boot firmware loads + (contains the header block and the binary image) + Main header - The header in the header block containing information + and data pertaining to the boot process (used for both + the regular and secured boot processes) + Binary image - The binary code payload of the boot image; in this + case the U-Boot's code (also known as "source image", + or just "image") + Secured header - The specialized header in the header block that + contains information and data pertaining to the + trusted boot (also known as "security header") + Secured boot mode - A special boot mode of the Armada SoC in which secured + images are verified (non-secure images won't boot); + the mode is activated by setting a eFuse field. + Trusted debug mode - A special mode for the trusted boot that allows + debugging of devices employing the trusted boot + framework in a secure manner (untested in the current + implementation). +Trusted boot framework - The ARMADA SoC's implementation of a secure verified + boot process. + +3. Boot image layout +-------------------- + ++-- Boot image --------------------------------------------+ +| | +| +-- Header block --------------------------------------+ | +| | Main header | | +| +------------------------------------------------------+ | +| | Secured header | | +| +------------------------------------------------------+ | +| | BIN header(s) | | +| +------------------------------------------------------+ | +| | REG header(s) | | +| +------------------------------------------------------+ | +| | Padding | | +| +------------------------------------------------------+ | +| | +| +------------------------------------------------------+ | +| | Binary image + checksum | | +| +------------------------------------------------------+ | ++----------------------------------------------------------+ + +4. The secured header +--------------------- + +For the trusted boot framework, a additional header is added to the boot image. +The following data are relevant for the secure boot: + + KAK: The KAK is contained in the secured header in the form + of a RSA-2048 public key in DER format with a length of + 524 bytes. +Header block signature: The RSA signature of the header block (excluding the + CSK array), created using the selected CSK. +Binary image signature: The RSA signature of the binary image, created using + the selected CSK. + CSK array: The array of the 16 CSKs as RSA-2048 public keys in DER + format with a length of 8384 = 16 * 524 bytes. + CSK block signature: The RSA signature of the CSK array, created using the + KAK. + +NOTE: The JTAG delay, Box ID, and Flash ID header fields do play a role in the +trusted boot process to enable and configure secure debugging, but they were +not tested in the current implementation of the trusted boot in U-Boot. + +5. The secured boot flow +------------------------ + +The steps in the boot flow that are relevant for the trusted boot framework +proceed as follows: + +1) Check if trusted boot is enabled, and perform regular boot if it is not. +2) Load the secured header, and verify its checksum. +3) Select the lowest valid CSK from CSK0 to CSK15. +4) Verify the SHA-256 hash of the KAK embedded in the secured header. +5) Verify the RSA signature of the CSK block from the secured header with the + KAK. +6) Verify the header block signature (which excludes the CSK block) from the + secured header with the selected CSK. +7) Load the binary image to the main memory and verify its checksum. +8) Verify the binary image's RSA signature from the secured header with the + selected CSK. +9) Continue the boot process as in the case of the regular boot. + +NOTE: All RSA signatures are verified according to the PKCS #1 v2.1 standard +described in [3]. + +NOTE: The Box ID and Flash ID are checked after step 6, and the trusted debug +mode may be entered there, but since this mode is untested in the current +implementation, it is not described further. + +6. Usage example +---------------- + +### Create key material + +To employ the trusted boot framework, cryptographic key material needs to be +created. In the current implementation, two keys are needed to build a valid +secured boot image: The KAK private key and a CSK private key (both have to be +2048 bit RSA keys in PEM format). Note that the usage of more than one CSK is +currently not supported. + +NOTE: Since the public key can be generated from the private key, it is +sufficient to store the private key for each key pair. + +OpenSSL can be used to generate the needed files kwb_kak.key and kwb_csk.key +(the names of these files have to be configured, see the next section on +kwbimage.cfg settings): + +openssl genrsa -out kwb_kak.key 2048 +openssl genrsa -out kwb_csk.key 2048 + +The generated files have to be placed in the U-Boot root directory. + +Alternatively, instead of copying the files, symlinks to the private keys can +be placed in the U-Boot root directory. + +WARNING: Knowledge of the KAK or CSK private key would enable an attacker to +generate secured boot images containing arbitrary code. Hence, the private keys +should be carefully guarded. + +### Create/Modifiy kwbimage.cfg + +The Kirkwook architecture in U-Boot employs a special board-specific +configuration file (kwbimage.cfg), which controls various boot image settings +that are interpreted by the BootROM, such as the boot medium. The support the +trusted boot framework, several new options were added to faciliate +configuration of the secured boot. + +The configuration file's layout has been retained, only the following new +options were added: + + KAK - The name of the KAK RSA private key file in the U-Boot + root directory, without the trailing extension of ".key". + CSK - The name of the (active) CSK RSA private key file in the + U-Boot root directory, without the trailing extension of + ".key". + BOX_ID - The BoxID to be used for trusted debugging (a integer + value). + FLASH_ID - The FlashID to be used for trusted debugging (a integer + value). + JTAG_DELAY - The JTAG delay to be used for trusted debugging (a + integer value). + CSK_INDEX - The index of the active CSK (a integer value). +SEC_SPECIALIZED_IMG - Flag to indicate whether to include the BoxID and FlashID + in the image (that is, whether to use the trusted debug + mode or not); no parameters. + SEC_BOOT_DEV - The boot device from which the trusted boot is allowed to + proceed, identified via a numeric ID. The tested values + are 0x34 = NOR flash, 0x31 = SDIO/MMC card; for + additional ID values, consult the documentation in [1]. + SEC_FUSE_DUMP - Dump the "fuse prog" commands necessary for writing the + correct eFuse values to a text file in the U-Boot root + directory. The parameter is the architecture for which to + dump the commands (currently only "a38x" is supported). + +The parameter values may be hardcoded into the file, but it is also possible to +employ a dynamic approach of creating a Autoconf-like kwbimage.cfg.in, then +reading configuration values from Kconfig options or from the board config +file, and generating the actual kwbimage.cfg from this template using Makefile +mechanisms (see board/gdsys/a38x/Makefile as an example for this approach). + +### Set config options + +To enable the generation of trusted boot images, the corresponding support +needs to be activated, and a index for the active CSK needs to be selected as +well. + +Furthermore, eFuse writing support has to be activated in order to burn the +eFuse structure's values (this option is just needed for programming the eFuse +structure; production boot images may disable it). + +ARM architecture + -> [*] Build image for trusted boot + (0) Index of active CSK + -> [*] Enable eFuse support + [ ] Fake eFuse access (dry run) + +### Build and test boot image + +The creation of the boot image is done via the usual invocation of make (with a +suitably set CROSS_COMPILE environment variable, of course). The resulting boot +image u-boot-spl.kwb can then be tested, if so desired. The hdrparser from [5] +can be used for this purpose. To build the tool, invoke make in the +'tools/marvell/doimage_mv' directory of [5], which builds a stand-alone +hdrparser executable. A test can be conducted by calling hdrparser with the +produced boot image and the following (mandatory) parameters: + +./hdrparser -k 0 -t u-boot-spl.kwb + +Here we assume that the CSK index is 0 and the boot image file resides in the +same directory (adapt accordingly if needed). The tool should report that all +checksums are valid ("GOOD"), that all signature verifications succeed +("PASSED"), and, finally, that the overall test was successful +("T E S T S U C C E E D E D" in the last line of output). + +### Burn eFuse structure + ++----------------------------------------------------------+ +| WARNING: Burning the eFuse structure is a irreversible | +| operation! Should wrong or corrupted values be used, the | +| board won't boot anymore, and recovery is likely | +| impossible! | ++----------------------------------------------------------+ + +After the build process has finished, and the SEC_FUSE_DUMP option was set in +the kwbimage.cfg was set, a text file kwb_fuses_a38x.txt should be present in +the U-Boot top-level directory. It contains all the necessary commands to set +the eFuse structure to the values needed for the used KAK digest, as well as +the CSK index, Flash ID and Box ID that were selected in kwbimage.cfg. + +Sequentially executing the commands in this file at the U-Boot command prompt +will write these values to the eFuse structure. + +If the SEC_FUSE_DUMP option was not set, the commands needed to burn the fuses +have to be crafted by hand. The needed fuse lines can be looked up in [1]; a +rough overview of the process is: + +* Burn the KAK public key hash. The hash itself can be found in the file + pub_kak_hash.txt in the U-Boot top-level directory; be careful to account for + the endianness! +* Burn the CSK selection, BoxID, and FlashID +* Enable trusted boot by burning the corresponding fuse (WARNING: this must be + the last fuse line written!) +* Lock the unused fuse lines + +The command to employ is the "fuse prog" command previously enabled by setting +the corresponding configuration option. + +For the trusted boot, the fuse prog command has a special syntax, since the +ARMADA SoC demands that whole fuse lines (64 bit values) have to be written as +a whole. The fuse prog command itself allows lists of 32 bit words to be +written at a time, but this is translated to a series of single 32 bit write +operations to the fuse line, where the individual 32 bit words are identified +by a "word" counter that is increased for each write. + +To work around this restriction, we interpret each line to have three "words" +(0-2): The first and second words are the values to be written to the fuse +line, and the third is a lock flag, which is supposed to lock the fuse line +when set to 1. Writes to the first and second words are memoized between +function calls, and the fuse line is only really written and locked (on writing +the third word) if both words were previously set, so that "incomplete" writes +are prevented. An exception to this is a single write to the third word (index +2) without previously writing neither the first nor the second word, which +locks the fuse line without setting any value; this is needed to lock the +unused fuse lines. + +As an example, to write the value 0011223344556677 to fuse line 10, we would +use the following command: + +fuse prog -y 10 0 00112233 44556677 1 + +Here 10 is the fuse line number, 0 is the index of the first word to be +written, 00112233 and 44556677 are the values to be written to the fuse line +(first and second word) and the trailing 1 is the value for the third word +responsible for locking the line. + +A "lock-only" command would look like this: + +fuse prog -y 11 2 1 + +Here 11 is the fuse number, 2 is the index of the first word to be written +(notice that we only write to word 2 here; the third word for fuse line +locking), and the 1 is the value for the word we are writing to. + +WARNING: According to application note [4], the VHV pin of the SoC must be +connected to a 1.8V source during eFuse programming, but *must* be disconnected +for normal operation. The AN [4] describes a software-controlled circuit (based +on a N-channel or P-channel FET and a free GPIO pin of the SoC) to achieve +this, but a jumper-based circuit should suffice as well. Regardless of the +chosen circuit, the issue needs to be addressed accordingly! + +7. Work to be done +------------------ + +* Add the ability to populate more than one CSK +* Test secure debug +* Test on Armada XP + +8. Bibliography +--------------- + +[1] ARMADA(R) 38x Family High-Performance Single/Dual CPU System on Chip + Functional Specification; MV-S109094-00, Rev. C; August 2, 2015, + Preliminary +[2] AN-383: ARMADA(R) 38x Families Secure Boot Mode Support; MV-S302501-00 + Rev. A; March 11, 2015, Preliminary +[3] Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography + Specifications Version 2.1; February 2003; + https://www.ietf.org/rfc/rfc3447.txt +[4] AN-389: ARMADA(R) VHV Power; MV-S302545-00 Rev. B; January 28, 2016, + Released +[5] Marvell Armada 38x U-Boot support; November 25, 2015; + https://github.com/MarvellEmbeddedProcessors/u-boot-marvell + +2017-01-05, Mario Six diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 4eeb0f6ede..c3058a40b2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -480,6 +480,49 @@ static int m88e1310_config(struct phy_device *phydev) return genphy_config_aneg(phydev); } +static int m88e1680_config(struct phy_device *phydev) +{ + /* + * As per Marvell Release Notes - Alaska V 88E1680 Rev A2 + * Errata Section 4.1 + */ + u16 reg; + int res; + + /* Matrix LED mode (not neede if single LED mode is used */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0004); + reg = phy_read(phydev, MDIO_DEVAD_NONE, 27); + reg |= (1 << 5); + phy_write(phydev, MDIO_DEVAD_NONE, 27, reg); + + /* QSGMII TX amplitude change */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x00fd); + phy_write(phydev, MDIO_DEVAD_NONE, 8, 0x0b53); + phy_write(phydev, MDIO_DEVAD_NONE, 7, 0x200d); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000); + + /* EEE initialization */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x00ff); + phy_write(phydev, MDIO_DEVAD_NONE, 17, 0xb030); + phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x215c); + phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x00fc); + phy_write(phydev, MDIO_DEVAD_NONE, 24, 0x888c); + phy_write(phydev, MDIO_DEVAD_NONE, 25, 0x888c); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000); + phy_write(phydev, MDIO_DEVAD_NONE, 0, 0x9140); + + res = genphy_config_aneg(phydev); + if (res < 0) + return res; + + /* soft reset */ + reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + reg |= BMCR_RESET; + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg); + + return 0; +} + static struct phy_driver M88E1011S_driver = { .name = "Marvell 88E1011S", .uid = 0x1410c60, @@ -580,6 +623,16 @@ static struct phy_driver M88E1310_driver = { .shutdown = &genphy_shutdown, }; +static struct phy_driver M88E1680_driver = { + .name = "Marvell 88E1680", + .uid = 0x1410ed0, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1680_config, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, +}; + int phy_marvell_init(void) { phy_register(&M88E1310_driver); @@ -592,6 +645,7 @@ int phy_marvell_init(void) phy_register(&M88E1011S_driver); phy_register(&M88E1510_driver); phy_register(&M88E1518_driver); + phy_register(&M88E1680_driver); return 0; } diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 0f449703de..da0aa29865 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -91,25 +91,26 @@ static void __iomem *mvebu_pcie_membase = (void __iomem *)MBUS_PCI_MEM_BASE; #if defined(CONFIG_ARMADA_38X) #define PCIE_BASE(if) \ ((if) == 0 ? \ - MVEBU_REG_PCIE_BASE + 0x40000 : \ - MVEBU_REG_PCIE_BASE + 0x4000 * (if)) + MVEBU_REG_PCIE0_BASE : \ + (MVEBU_REG_PCIE_BASE + 0x4000 * (if - 1))) /* * On A38x MV6820 these PEX ports are supported: * 0 - Port 0.0 - * 1 - Port 0.1 - * 2 - Port 0.2 + * 1 - Port 1.0 + * 2 - Port 2.0 + * 3 - Port 3.0 */ -#define MAX_PEX 3 +#define MAX_PEX 4 static struct mvebu_pcie pcie_bus[MAX_PEX]; static void mvebu_get_port_lane(struct mvebu_pcie *pcie, int pex_idx, int *mem_target, int *mem_attr) { - u8 port[] = { 0, 1, 2 }; - u8 lane[] = { 0, 0, 0 }; - u8 target[] = { 8, 4, 4 }; - u8 attr[] = { 0xe8, 0xe8, 0xd8 }; + u8 port[] = { 0, 1, 2, 3 }; + u8 lane[] = { 0, 0, 0, 0 }; + u8 target[] = { 8, 4, 4, 4 }; + u8 attr[] = { 0xe8, 0xe8, 0xd8, 0xb8 }; pcie->port = port[pex_idx]; pcie->lane = lane[pex_idx]; @@ -351,9 +352,9 @@ void pci_init_board(void) mvebu_get_port_lane(pcie, i, &mem_target, &mem_attr); /* Don't read at all from pci registers if port power is down */ - if (pcie->lane == 0 && SELECT(soc_ctrl, pcie->port) == 0) { - i += 3; - debug("%s: skipping port %d\n", __func__, pcie->port); + if (SELECT(soc_ctrl, pcie->port) == 0) { + if (pcie->lane == 0) + debug("%s: skipping port %d\n", __func__, pcie->port); continue; } diff --git a/drivers/phy/marvell/comphy_a3700.c b/drivers/phy/marvell/comphy_a3700.c index faa62f90ae..5afd23c052 100644 --- a/drivers/phy/marvell/comphy_a3700.c +++ b/drivers/phy/marvell/comphy_a3700.c @@ -884,11 +884,10 @@ void comphy_dedicated_phys_init(void) } node = fdt_node_offset_by_compatible(blob, -1, - "marvell,armada-3700-sdio"); + "marvell,armada-8k-sdhci"); if (node <= 0) { - debug("No SDIO node in DT, looking for MMC one\n"); - node = fdt_node_offset_by_compatible(blob, -1, - "marvell,xenon-sdhci"); + node = fdt_node_offset_by_compatible( + blob, -1, "marvell,armada-3700-sdhci"); } if (node > 0) { diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index 7ab34ce863..3490ee0c3b 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -82,4 +82,10 @@ config TPM_ST33ZP24_SPI to the device using the standard TPM Interface Specification (TIS) protocol +config TPM_FLUSH_RESOURCES + bool "Enable TPM resource flushing support" + depends on TPM + help + Enable support to flush specific resources (e.g. keys) from the TPM. + The functionality is available via the 'tpm' command as well. endmenu diff --git a/include/tpm.h b/include/tpm.h index 9a6585d3d4..800f29c101 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -47,6 +47,42 @@ enum tpm_nv_index { TPM_NV_INDEX_DIR = 0x10000001, }; +enum tpm_resource_type { + TPM_RT_KEY = 0x00000001, + TPM_RT_AUTH = 0x00000002, + TPM_RT_HASH = 0x00000003, + TPM_RT_TRANS = 0x00000004, + TPM_RT_CONTEXT = 0x00000005, + TPM_RT_COUNTER = 0x00000006, + TPM_RT_DELEGATE = 0x00000007, + TPM_RT_DAA_TPM = 0x00000008, + TPM_RT_DAA_V0 = 0x00000009, + TPM_RT_DAA_V1 = 0x0000000A, +}; + +enum tpm_capability_areas { + TPM_CAP_ORD = 0x00000001, + TPM_CAP_ALG = 0x00000002, + TPM_CAP_PID = 0x00000003, + TPM_CAP_FLAG = 0x00000004, + TPM_CAP_PROPERTY = 0x00000005, + TPM_CAP_VERSION = 0x00000006, + TPM_CAP_KEY_HANDLE = 0x00000007, + TPM_CAP_CHECK_LOADED = 0x00000008, + TPM_CAP_SYM_MODE = 0x00000009, + TPM_CAP_KEY_STATUS = 0x0000000C, + TPM_CAP_NV_LIST = 0x0000000D, + TPM_CAP_MFR = 0x00000010, + TPM_CAP_NV_INDEX = 0x00000011, + TPM_CAP_TRANS_ALG = 0x00000012, + TPM_CAP_HANDLE = 0x00000014, + TPM_CAP_TRANS_ES = 0x00000015, + TPM_CAP_AUTH_ENCRYPT = 0x00000017, + TPM_CAP_SELECT_SIZE = 0x00000018, + TPM_CAP_DA_LOGIC = 0x00000019, + TPM_CAP_VERSION_VAL = 0x0000001A, +}; + #define TPM_NV_PER_GLOBALLOCK (1U << 15) #define TPM_NV_PER_PPWRITE (1U << 0) #define TPM_NV_PER_READ_STCLEAR (1U << 31) @@ -594,4 +630,13 @@ uint32_t tpm_get_permanent_flags(struct tpm_permanent_flags *pflags); */ uint32_t tpm_get_permissions(uint32_t index, uint32_t *perm); +/** + * Flush a resource with a given handle and type from the TPM + * + * @param key_handle handle of the resource + * @param resource_type type of the resource + * @return return code of the operation + */ +uint32_t tpm_flush_specific(uint32_t key_handle, uint32_t resource_type); + #endif /* __TPM_H */ diff --git a/lib/tpm.c b/lib/tpm.c index 88f24060f0..fb1221472a 100644 --- a/lib/tpm.c +++ b/lib/tpm.c @@ -645,6 +645,35 @@ uint32_t tpm_get_permissions(uint32_t index, uint32_t *perm) return 0; } +#ifdef CONFIG_TPM_FLUSH_RESOURCES +uint32_t tpm_flush_specific(uint32_t key_handle, uint32_t resource_type) +{ + const uint8_t command[18] = { + 0x00, 0xc1, /* TPM_TAG */ + 0x00, 0x00, 0x00, 0x12, /* parameter size */ + 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */ + 0x00, 0x00, 0x00, 0x00, /* key handle */ + 0x00, 0x00, 0x00, 0x00, /* resource type */ + }; + const size_t key_handle_offset = 10; + const size_t resource_type_offset = 14; + uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; + size_t response_length = sizeof(response); + uint32_t err; + + if (pack_byte_string(buf, sizeof(buf), "sdd", + 0, command, sizeof(command), + key_handle_offset, key_handle, + resource_type_offset, resource_type)) + return TPM_LIB_ERROR; + + err = tpm_sendrecv_command(buf, response, &response_length); + if (err) + return err; + return 0; +} +#endif /* CONFIG_TPM_FLUSH_RESOURCES */ + #ifdef CONFIG_TPM_AUTH_SESSIONS /** diff --git a/tools/Makefile b/tools/Makefile index cefcedf683..f5ac6313e1 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -142,8 +142,12 @@ ifdef CONFIG_SYS_U_BOOT_OFFS HOSTCFLAGS_kwbimage.o += -DCONFIG_SYS_U_BOOT_OFFS=$(CONFIG_SYS_U_BOOT_OFFS) endif +ifneq ($(CONFIG_ARMADA_38X)$(CONFIG_ARMADA_39X),) +HOSTCFLAGS_kwbimage.o += -DCONFIG_KWB_SECURE +endif + # MXSImage needs LibSSL -ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_FIT_SIGNATURE),) +ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_ARMADA_38X)$(CONFIG_ARMADA_39X)$(CONFIG_FIT_SIGNATURE),) HOSTLOADLIBES_mkimage += \ $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto") diff --git a/tools/kwbimage.c b/tools/kwbimage.c index 69844d9169..93797c99da 100644 --- a/tools/kwbimage.c +++ b/tools/kwbimage.c @@ -1,30 +1,47 @@ /* * Image manipulator for Marvell SoCs - * supports Kirkwood, Dove, Armada 370, and Armada XP + * supports Kirkwood, Dove, Armada 370, Armada XP, and Armada 38x * * (C) Copyright 2013 Thomas Petazzoni * * * SPDX-License-Identifier: GPL-2.0+ * - * Not implemented: support for the register headers and secure - * headers in v1 images + * Not implemented: support for the register headers in v1 images */ #include "imagetool.h" #include #include +#include #include #include "kwbimage.h" +#ifdef CONFIG_KWB_SECURE +#include +#include +#include +#include +#endif + static struct image_cfg_element *image_cfg; static int cfgn; +#ifdef CONFIG_KWB_SECURE +static int verbose_mode; +#endif struct boot_mode { unsigned int id; const char *name; }; +/* + * SHA2-256 hash + */ +struct hash_v1 { + uint8_t hash[32]; +}; + struct boot_mode boot_modes[] = { { 0x4D, "i2c" }, { 0x5A, "spi" }, @@ -55,22 +72,63 @@ struct nand_ecc_mode nand_ecc_modes[] = { #define BINARY_MAX_ARGS 8 /* In-memory representation of a line of the configuration file */ + +enum image_cfg_type { + IMAGE_CFG_VERSION = 0x1, + IMAGE_CFG_BOOT_FROM, + IMAGE_CFG_DEST_ADDR, + IMAGE_CFG_EXEC_ADDR, + IMAGE_CFG_NAND_BLKSZ, + IMAGE_CFG_NAND_BADBLK_LOCATION, + IMAGE_CFG_NAND_ECC_MODE, + IMAGE_CFG_NAND_PAGESZ, + IMAGE_CFG_BINARY, + IMAGE_CFG_PAYLOAD, + IMAGE_CFG_DATA, + IMAGE_CFG_BAUDRATE, + IMAGE_CFG_DEBUG, + IMAGE_CFG_KAK, + IMAGE_CFG_CSK, + IMAGE_CFG_CSK_INDEX, + IMAGE_CFG_JTAG_DELAY, + IMAGE_CFG_BOX_ID, + IMAGE_CFG_FLASH_ID, + IMAGE_CFG_SEC_COMMON_IMG, + IMAGE_CFG_SEC_SPECIALIZED_IMG, + IMAGE_CFG_SEC_BOOT_DEV, + IMAGE_CFG_SEC_FUSE_DUMP, + + IMAGE_CFG_COUNT +} type; + +static const char * const id_strs[] = { + [IMAGE_CFG_VERSION] = "VERSION", + [IMAGE_CFG_BOOT_FROM] = "BOOT_FROM", + [IMAGE_CFG_DEST_ADDR] = "DEST_ADDR", + [IMAGE_CFG_EXEC_ADDR] = "EXEC_ADDR", + [IMAGE_CFG_NAND_BLKSZ] = "NAND_BLKSZ", + [IMAGE_CFG_NAND_BADBLK_LOCATION] = "NAND_BADBLK_LOCATION", + [IMAGE_CFG_NAND_ECC_MODE] = "NAND_ECC_MODE", + [IMAGE_CFG_NAND_PAGESZ] = "NAND_PAGE_SIZE", + [IMAGE_CFG_BINARY] = "BINARY", + [IMAGE_CFG_PAYLOAD] = "PAYLOAD", + [IMAGE_CFG_DATA] = "DATA", + [IMAGE_CFG_BAUDRATE] = "BAUDRATE", + [IMAGE_CFG_DEBUG] = "DEBUG", + [IMAGE_CFG_KAK] = "KAK", + [IMAGE_CFG_CSK] = "CSK", + [IMAGE_CFG_CSK_INDEX] = "CSK_INDEX", + [IMAGE_CFG_JTAG_DELAY] = "JTAG_DELAY", + [IMAGE_CFG_BOX_ID] = "BOX_ID", + [IMAGE_CFG_FLASH_ID] = "FLASH_ID", + [IMAGE_CFG_SEC_COMMON_IMG] = "SEC_COMMON_IMG", + [IMAGE_CFG_SEC_SPECIALIZED_IMG] = "SEC_SPECIALIZED_IMG", + [IMAGE_CFG_SEC_BOOT_DEV] = "SEC_BOOT_DEV", + [IMAGE_CFG_SEC_FUSE_DUMP] = "SEC_FUSE_DUMP" +}; + struct image_cfg_element { - enum { - IMAGE_CFG_VERSION = 0x1, - IMAGE_CFG_BOOT_FROM, - IMAGE_CFG_DEST_ADDR, - IMAGE_CFG_EXEC_ADDR, - IMAGE_CFG_NAND_BLKSZ, - IMAGE_CFG_NAND_BADBLK_LOCATION, - IMAGE_CFG_NAND_ECC_MODE, - IMAGE_CFG_NAND_PAGESZ, - IMAGE_CFG_BINARY, - IMAGE_CFG_PAYLOAD, - IMAGE_CFG_DATA, - IMAGE_CFG_BAUDRATE, - IMAGE_CFG_DEBUG, - } type; + enum image_cfg_type type; union { unsigned int version; unsigned int bootfrom; @@ -89,6 +147,14 @@ struct image_cfg_element { struct ext_hdr_v0_reg regdata; unsigned int baudrate; unsigned int debug; + const char *key_name; + int csk_idx; + uint8_t jtag_delay; + uint32_t boxid; + uint32_t flashid; + bool sec_specialized_img; + unsigned int sec_boot_dev; + const char *name; }; }; @@ -103,6 +169,7 @@ struct image_cfg_element { static const char *image_boot_mode_name(unsigned int id) { int i; + for (i = 0; boot_modes[i].name; i++) if (boot_modes[i].id == id) return boot_modes[i].name; @@ -112,6 +179,7 @@ static const char *image_boot_mode_name(unsigned int id) int image_boot_mode_id(const char *boot_mode_name) { int i; + for (i = 0; boot_modes[i].name; i++) if (!strcmp(boot_modes[i].name, boot_mode_name)) return boot_modes[i].id; @@ -122,6 +190,7 @@ int image_boot_mode_id(const char *boot_mode_name) int image_nand_ecc_mode_id(const char *nand_ecc_mode_name) { int i; + for (i = 0; nand_ecc_modes[i].name; i++) if (!strcmp(nand_ecc_modes[i].name, nand_ecc_mode_name)) return nand_ecc_modes[i].id; @@ -154,6 +223,32 @@ image_count_options(unsigned int optiontype) return count; } +#if defined(CONFIG_KWB_SECURE) + +static int image_get_csk_index(void) +{ + struct image_cfg_element *e; + + e = image_find_option(IMAGE_CFG_CSK_INDEX); + if (!e) + return -1; + + return e->csk_idx; +} + +static bool image_get_spezialized_img(void) +{ + struct image_cfg_element *e; + + e = image_find_option(IMAGE_CFG_SEC_SPECIALIZED_IMG); + if (!e) + return false; + + return e->sec_specialized_img; +} + +#endif + /* * Compute a 8-bit checksum of a memory area. This algorithm follows * the requirements of the Marvell SoC BootROM specifications. @@ -221,14 +316,500 @@ static uint8_t baudrate_to_option(unsigned int baudrate) } } +#if defined(CONFIG_KWB_SECURE) +static void kwb_msg(const char *fmt, ...) +{ + if (verbose_mode) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + } +} + +static int openssl_err(const char *msg) +{ + unsigned long ssl_err = ERR_get_error(); + + fprintf(stderr, "%s", msg); + fprintf(stderr, ": %s\n", + ERR_error_string(ssl_err, 0)); + + return -1; +} + +static int kwb_load_rsa_key(const char *keydir, const char *name, RSA **p_rsa) +{ + char path[PATH_MAX]; + RSA *rsa; + FILE *f; + + if (!keydir) + keydir = "."; + + snprintf(path, sizeof(path), "%s/%s.key", keydir, name); + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Couldn't open RSA private key: '%s': %s\n", + path, strerror(errno)); + return -ENOENT; + } + + rsa = PEM_read_RSAPrivateKey(f, 0, NULL, ""); + if (!rsa) { + openssl_err("Failure reading private key"); + fclose(f); + return -EPROTO; + } + fclose(f); + *p_rsa = rsa; + + return 0; +} + +static int kwb_load_cfg_key(struct image_tool_params *params, + unsigned int cfg_option, const char *key_name, + RSA **p_key) +{ + struct image_cfg_element *e_key; + RSA *key; + int res; + + *p_key = NULL; + + e_key = image_find_option(cfg_option); + if (!e_key) { + fprintf(stderr, "%s not configured\n", key_name); + return -ENOENT; + } + + res = kwb_load_rsa_key(params->keydir, e_key->key_name, &key); + if (res < 0) { + fprintf(stderr, "Failed to load %s\n", key_name); + return -ENOENT; + } + + *p_key = key; + + return 0; +} + +static int kwb_load_kak(struct image_tool_params *params, RSA **p_kak) +{ + return kwb_load_cfg_key(params, IMAGE_CFG_KAK, "KAK", p_kak); +} + +static int kwb_load_csk(struct image_tool_params *params, RSA **p_csk) +{ + return kwb_load_cfg_key(params, IMAGE_CFG_CSK, "CSK", p_csk); +} + +static int kwb_compute_pubkey_hash(struct pubkey_der_v1 *pk, + struct hash_v1 *hash) +{ + EVP_MD_CTX *ctx; + unsigned int key_size; + unsigned int hash_size; + int ret = 0; + + if (!pk || !hash || pk->key[0] != 0x30 || pk->key[1] != 0x82) + return -EINVAL; + + key_size = (pk->key[2] << 8) + pk->key[3] + 4; + + ctx = EVP_MD_CTX_create(); + if (!ctx) + return openssl_err("EVP context creation failed"); + + EVP_MD_CTX_init(ctx); + if (!EVP_DigestInit(ctx, EVP_sha256())) { + ret = openssl_err("Digest setup failed"); + goto hash_err_ctx; + } + + if (!EVP_DigestUpdate(ctx, pk->key, key_size)) { + ret = openssl_err("Hashing data failed"); + goto hash_err_ctx; + } + + if (!EVP_DigestFinal(ctx, hash->hash, &hash_size)) { + ret = openssl_err("Could not obtain hash"); + goto hash_err_ctx; + } + + EVP_MD_CTX_cleanup(ctx); + +hash_err_ctx: + EVP_MD_CTX_destroy(ctx); + return ret; +} + +static int kwb_import_pubkey(RSA **key, struct pubkey_der_v1 *src, char *keyname) +{ + RSA *rsa; + const unsigned char *ptr; + + if (!key || !src) + goto fail; + + ptr = src->key; + rsa = d2i_RSAPublicKey(key, &ptr, sizeof(src->key)); + if (!rsa) { + openssl_err("error decoding public key"); + goto fail; + } + + return 0; +fail: + fprintf(stderr, "Failed to decode %s pubkey\n", keyname); + return -EINVAL; +} + +static int kwb_export_pubkey(RSA *key, struct pubkey_der_v1 *dst, FILE *hashf, + char *keyname) +{ + int size_exp, size_mod, size_seq; + uint8_t *cur; + char *errmsg = "Failed to encode %s\n"; + + if (!key || !key->e || !key->n || !dst) { + fprintf(stderr, "export pk failed: (%p, %p, %p, %p)", + key, key->e, key->n, dst); + fprintf(stderr, errmsg, keyname); + return -EINVAL; + } + + /* + * According to the specs, the key should be PKCS#1 DER encoded. + * But unfortunately the really required encoding seems to be different; + * it violates DER...! (But it still conformes to BER.) + * (Length always in long form w/ 2 byte length code; no leading zero + * when MSB of first byte is set...) + * So we cannot use the encoding func provided by OpenSSL and have to + * do the encoding manually. + */ + + size_exp = BN_num_bytes(key->e); + size_mod = BN_num_bytes(key->n); + size_seq = 4 + size_mod + 4 + size_exp; + + if (size_mod > 256) { + fprintf(stderr, "export pk failed: wrong mod size: %d\n", + size_mod); + fprintf(stderr, errmsg, keyname); + return -EINVAL; + } + + if (4 + size_seq > sizeof(dst->key)) { + fprintf(stderr, "export pk failed: seq too large (%d, %lu)\n", + 4 + size_seq, sizeof(dst->key)); + fprintf(stderr, errmsg, keyname); + return -ENOBUFS; + } + + cur = dst->key; + + /* PKCS#1 (RFC3447) RSAPublicKey structure */ + *cur++ = 0x30; /* SEQUENCE */ + *cur++ = 0x82; + *cur++ = (size_seq >> 8) & 0xFF; + *cur++ = size_seq & 0xFF; + /* Modulus */ + *cur++ = 0x02; /* INTEGER */ + *cur++ = 0x82; + *cur++ = (size_mod >> 8) & 0xFF; + *cur++ = size_mod & 0xFF; + BN_bn2bin(key->n, cur); + cur += size_mod; + /* Exponent */ + *cur++ = 0x02; /* INTEGER */ + *cur++ = 0x82; + *cur++ = (size_exp >> 8) & 0xFF; + *cur++ = size_exp & 0xFF; + BN_bn2bin(key->e, cur); + + if (hashf) { + struct hash_v1 pk_hash; + int i; + int ret = 0; + + ret = kwb_compute_pubkey_hash(dst, &pk_hash); + if (ret < 0) { + fprintf(stderr, errmsg, keyname); + return ret; + } + + fprintf(hashf, "SHA256 = "); + for (i = 0 ; i < sizeof(pk_hash.hash); ++i) + fprintf(hashf, "%02X", pk_hash.hash[i]); + fprintf(hashf, "\n"); + } + + return 0; +} + +int kwb_sign(RSA *key, void *data, int datasz, struct sig_v1 *sig, char *signame) +{ + EVP_PKEY *evp_key; + EVP_MD_CTX *ctx; + unsigned int sig_size; + int size; + int ret = 0; + + evp_key = EVP_PKEY_new(); + if (!evp_key) + return openssl_err("EVP_PKEY object creation failed"); + + if (!EVP_PKEY_set1_RSA(evp_key, key)) { + ret = openssl_err("EVP key setup failed"); + goto err_key; + } + + size = EVP_PKEY_size(evp_key); + if (size > sizeof(sig->sig)) { + fprintf(stderr, "Buffer to small for signature (%d bytes)\n", + size); + ret = -ENOBUFS; + goto err_key; + } + + ctx = EVP_MD_CTX_create(); + if (!ctx) { + ret = openssl_err("EVP context creation failed"); + goto err_key; + } + EVP_MD_CTX_init(ctx); + if (!EVP_SignInit(ctx, EVP_sha256())) { + ret = openssl_err("Signer setup failed"); + goto err_ctx; + } + + if (!EVP_SignUpdate(ctx, data, datasz)) { + ret = openssl_err("Signing data failed"); + goto err_ctx; + } + + if (!EVP_SignFinal(ctx, sig->sig, &sig_size, evp_key)) { + ret = openssl_err("Could not obtain signature"); + goto err_ctx; + } + + EVP_MD_CTX_cleanup(ctx); + EVP_MD_CTX_destroy(ctx); + EVP_PKEY_free(evp_key); + + return 0; + +err_ctx: + EVP_MD_CTX_destroy(ctx); +err_key: + EVP_PKEY_free(evp_key); + fprintf(stderr, "Failed to create %s signature\n", signame); + return ret; +} + +int kwb_verify(RSA *key, void *data, int datasz, struct sig_v1 *sig, + char *signame) +{ + EVP_PKEY *evp_key; + EVP_MD_CTX *ctx; + int size; + int ret = 0; + + evp_key = EVP_PKEY_new(); + if (!evp_key) + return openssl_err("EVP_PKEY object creation failed"); + + if (!EVP_PKEY_set1_RSA(evp_key, key)) { + ret = openssl_err("EVP key setup failed"); + goto err_key; + } + + size = EVP_PKEY_size(evp_key); + if (size > sizeof(sig->sig)) { + fprintf(stderr, "Invalid signature size (%d bytes)\n", + size); + ret = -EINVAL; + goto err_key; + } + + ctx = EVP_MD_CTX_create(); + if (!ctx) { + ret = openssl_err("EVP context creation failed"); + goto err_key; + } + EVP_MD_CTX_init(ctx); + if (!EVP_VerifyInit(ctx, EVP_sha256())) { + ret = openssl_err("Verifier setup failed"); + goto err_ctx; + } + + if (!EVP_VerifyUpdate(ctx, data, datasz)) { + ret = openssl_err("Hashing data failed"); + goto err_ctx; + } + + if (!EVP_VerifyFinal(ctx, sig->sig, sizeof(sig->sig), evp_key)) { + ret = openssl_err("Could not verify signature"); + goto err_ctx; + } + + EVP_MD_CTX_cleanup(ctx); + EVP_MD_CTX_destroy(ctx); + EVP_PKEY_free(evp_key); + + return 0; + +err_ctx: + EVP_MD_CTX_destroy(ctx); +err_key: + EVP_PKEY_free(evp_key); + fprintf(stderr, "Failed to verify %s signature\n", signame); + return ret; +} + +int kwb_sign_and_verify(RSA *key, void *data, int datasz, struct sig_v1 *sig, + char *signame) +{ + if (kwb_sign(key, data, datasz, sig, signame) < 0) + return -1; + + if (kwb_verify(key, data, datasz, sig, signame) < 0) + return -1; + + return 0; +} + + +int kwb_dump_fuse_cmds_38x(FILE *out, struct secure_hdr_v1 *sec_hdr) +{ + struct hash_v1 kak_pub_hash; + struct image_cfg_element *e; + unsigned int fuse_line; + int i, idx; + uint8_t *ptr; + uint32_t val; + int ret = 0; + + if (!out || !sec_hdr) + return -EINVAL; + + ret = kwb_compute_pubkey_hash(&sec_hdr->kak, &kak_pub_hash); + if (ret < 0) + goto done; + + fprintf(out, "# burn KAK pub key hash\n"); + ptr = kak_pub_hash.hash; + for (fuse_line = 26; fuse_line <= 30; ++fuse_line) { + fprintf(out, "fuse prog -y %u 0 ", fuse_line); + + for (i = 4; i-- > 0;) + fprintf(out, "%02hx", (ushort)ptr[i]); + ptr += 4; + fprintf(out, " 00"); + + if (fuse_line < 30) { + for (i = 3; i-- > 0;) + fprintf(out, "%02hx", (ushort)ptr[i]); + ptr += 3; + } else { + fprintf(out, "000000"); + } + + fprintf(out, " 1\n"); + } + + fprintf(out, "# burn CSK selection\n"); + + idx = image_get_csk_index(); + if (idx < 0 || idx > 15) { + ret = -EINVAL; + goto done; + } + if (idx > 0) { + for (fuse_line = 31; fuse_line < 31 + idx; ++fuse_line) + fprintf(out, "fuse prog -y %u 0 00000001 00000000 1\n", + fuse_line); + } else { + fprintf(out, "# CSK index is 0; no mods needed\n"); + } + + e = image_find_option(IMAGE_CFG_BOX_ID); + if (e) { + fprintf(out, "# set box ID\n"); + fprintf(out, "fuse prog -y 48 0 %08x 00000000 1\n", e->boxid); + } + + e = image_find_option(IMAGE_CFG_FLASH_ID); + if (e) { + fprintf(out, "# set flash ID\n"); + fprintf(out, "fuse prog -y 47 0 %08x 00000000 1\n", e->flashid); + } + + fprintf(out, "# enable secure mode "); + fprintf(out, "(must be the last fuse line written)\n"); + + val = 1; + e = image_find_option(IMAGE_CFG_SEC_BOOT_DEV); + if (!e) { + fprintf(stderr, "ERROR: secured mode boot device not given\n"); + ret = -EINVAL; + goto done; + } + + if (e->sec_boot_dev > 0xff) { + fprintf(stderr, "ERROR: secured mode boot device invalid\n"); + ret = -EINVAL; + goto done; + } + + val |= (e->sec_boot_dev << 8); + + fprintf(out, "fuse prog -y 24 0 %08x 0103e0a9 1\n", val); + + fprintf(out, "# lock (unused) fuse lines (0-23)s\n"); + for (fuse_line = 0; fuse_line < 24; ++fuse_line) + fprintf(out, "fuse prog -y %u 2 1\n", fuse_line); + + fprintf(out, "# OK, that's all :-)\n"); + +done: + return ret; +} + +static int kwb_dump_fuse_cmds(struct secure_hdr_v1 *sec_hdr) +{ + int ret = 0; + struct image_cfg_element *e; + + e = image_find_option(IMAGE_CFG_SEC_FUSE_DUMP); + if (!e) + return 0; + + if (!strcmp(e->name, "a38x")) { + FILE *out = fopen("kwb_fuses_a38x.txt", "w+"); + + kwb_dump_fuse_cmds_38x(out, sec_hdr); + fclose(out); + goto done; + } + + ret = -ENOSYS; + +done: + return ret; +} + +#endif + static void *image_create_v0(size_t *imagesz, struct image_tool_params *params, int payloadsz) { struct image_cfg_element *e; size_t headersz; struct main_hdr_v0 *main_hdr; - struct ext_hdr_v0 *ext_hdr; - void *image; + uint8_t *image; int has_ext = 0; /* @@ -255,7 +836,7 @@ static void *image_create_v0(size_t *imagesz, struct image_tool_params *params, memset(image, 0, headersz); - main_hdr = image; + main_hdr = (struct main_hdr_v0 *)image; /* Fill in the main header */ main_hdr->blocksize = @@ -279,9 +860,11 @@ static void *image_create_v0(size_t *imagesz, struct image_tool_params *params, /* Generate the ext header */ if (has_ext) { + struct ext_hdr_v0 *ext_hdr; int cfgi, datai; - ext_hdr = image + sizeof(struct main_hdr_v0); + ext_hdr = (struct ext_hdr_v0 *) + (image + sizeof(struct main_hdr_v0)); ext_hdr->offset = cpu_to_le32(0x40); for (cfgi = 0, datai = 0; cfgi < cfgn; cfgi++) { @@ -304,12 +887,10 @@ static void *image_create_v0(size_t *imagesz, struct image_tool_params *params, return image; } -static size_t image_headersz_v1(struct image_tool_params *params, - int *hasext) +static size_t image_headersz_v1(int *hasext) { struct image_cfg_element *binarye; size_t headersz; - int ret; /* * Calculate the size of the header and the size of the @@ -329,6 +910,7 @@ static size_t image_headersz_v1(struct image_tool_params *params, binarye = image_find_option(IMAGE_CFG_BINARY); if (binarye) { + int ret; struct stat s; ret = stat(binarye->binary.file, &s); @@ -357,16 +939,25 @@ static size_t image_headersz_v1(struct image_tool_params *params, *hasext = 1; } +#if defined(CONFIG_KWB_SECURE) + if (image_get_csk_index() >= 0) { + headersz += sizeof(struct secure_hdr_v1); + if (hasext) + *hasext = 1; + } +#endif + #if defined(CONFIG_SYS_U_BOOT_OFFS) if (headersz > CONFIG_SYS_U_BOOT_OFFS) { - fprintf(stderr, "Error: Image header (incl. SPL image) too big!\n"); + fprintf(stderr, + "Error: Image header (incl. SPL image) too big!\n"); fprintf(stderr, "header=0x%x CONFIG_SYS_U_BOOT_OFFS=0x%x!\n", (int)headersz, CONFIG_SYS_U_BOOT_OFFS); fprintf(stderr, "Increase CONFIG_SYS_U_BOOT_OFFS!\n"); return 0; - } else { - headersz = CONFIG_SYS_U_BOOT_OFFS; } + + headersz = CONFIG_SYS_U_BOOT_OFFS; #endif /* @@ -376,21 +967,210 @@ static size_t image_headersz_v1(struct image_tool_params *params, return ALIGN_SUP(headersz, 4096); } -static void *image_create_v1(size_t *imagesz, struct image_tool_params *params, - int payloadsz) +int add_binary_header_v1(uint8_t *cur) { - struct image_cfg_element *e, *binarye; - struct main_hdr_v1 *main_hdr; - size_t headersz; - void *image, *cur; - int hasext = 0; + struct image_cfg_element *binarye; + struct opt_hdr_v1 *hdr = (struct opt_hdr_v1 *)cur; + uint32_t *args; + size_t binhdrsz; + struct stat s; + int argi; + FILE *bin; int ret; + binarye = image_find_option(IMAGE_CFG_BINARY); + + if (!binarye) + return 0; + + hdr->headertype = OPT_HDR_V1_BINARY_TYPE; + + bin = fopen(binarye->binary.file, "r"); + if (!bin) { + fprintf(stderr, "Cannot open binary file %s\n", + binarye->binary.file); + return -1; + } + + fstat(fileno(bin), &s); + + binhdrsz = sizeof(struct opt_hdr_v1) + + (binarye->binary.nargs + 2) * sizeof(uint32_t) + + s.st_size; + + /* + * The size includes the binary image size, rounded + * up to a 4-byte boundary. Plus 4 bytes for the + * next-header byte and 3-byte alignment at the end. + */ + binhdrsz = ALIGN_SUP(binhdrsz, 4) + 4; + hdr->headersz_lsb = cpu_to_le16(binhdrsz & 0xFFFF); + hdr->headersz_msb = (binhdrsz & 0xFFFF0000) >> 16; + + cur += sizeof(struct opt_hdr_v1); + + args = (uint32_t *)cur; + *args = cpu_to_le32(binarye->binary.nargs); + args++; + for (argi = 0; argi < binarye->binary.nargs; argi++) + args[argi] = cpu_to_le32(binarye->binary.args[argi]); + + cur += (binarye->binary.nargs + 1) * sizeof(uint32_t); + + ret = fread(cur, s.st_size, 1, bin); + if (ret != 1) { + fprintf(stderr, + "Could not read binary image %s\n", + binarye->binary.file); + return -1; + } + + fclose(bin); + + cur += ALIGN_SUP(s.st_size, 4); + + /* + * For now, we don't support more than one binary + * header, and no other header types are + * supported. So, the binary header is necessarily the + * last one + */ + *((uint32_t *)cur) = 0x00000000; + + cur += sizeof(uint32_t); + + return 0; +} + +#if defined(CONFIG_KWB_SECURE) + +int export_pub_kak_hash(RSA *kak, struct secure_hdr_v1 *secure_hdr) +{ + FILE *hashf; + int res; + + hashf = fopen("pub_kak_hash.txt", "w"); + + res = kwb_export_pubkey(kak, &secure_hdr->kak, hashf, "KAK"); + + fclose(hashf); + + return res < 0 ? 1 : 0; +} + +int kwb_sign_csk_with_kak(struct image_tool_params *params, + struct secure_hdr_v1 *secure_hdr, RSA *csk) +{ + RSA *kak = NULL; + RSA *kak_pub = NULL; + int csk_idx = image_get_csk_index(); + struct sig_v1 tmp_sig; + + if (csk_idx >= 16) { + fprintf(stderr, "Invalid CSK index %d\n", csk_idx); + return 1; + } + + if (kwb_load_kak(params, &kak) < 0) + return 1; + + if (export_pub_kak_hash(kak, secure_hdr)) + return 1; + + if (kwb_import_pubkey(&kak_pub, &secure_hdr->kak, "KAK") < 0) + return 1; + + if (kwb_export_pubkey(csk, &secure_hdr->csk[csk_idx], NULL, "CSK") < 0) + return 1; + + if (kwb_sign_and_verify(kak, &secure_hdr->csk, + sizeof(secure_hdr->csk) + + sizeof(secure_hdr->csksig), + &tmp_sig, "CSK") < 0) + return 1; + + if (kwb_verify(kak_pub, &secure_hdr->csk, + sizeof(secure_hdr->csk) + + sizeof(secure_hdr->csksig), + &tmp_sig, "CSK (2)") < 0) + return 1; + + secure_hdr->csksig = tmp_sig; + + return 0; +} + +int add_secure_header_v1(struct image_tool_params *params, uint8_t *ptr, + int payloadsz, size_t headersz, uint8_t *image, + struct secure_hdr_v1 *secure_hdr) +{ + struct image_cfg_element *e_jtagdelay; + struct image_cfg_element *e_boxid; + struct image_cfg_element *e_flashid; + RSA *csk = NULL; + unsigned char *image_ptr; + size_t image_size; + struct sig_v1 tmp_sig; + bool specialized_img = image_get_spezialized_img(); + + kwb_msg("Create secure header content\n"); + + e_jtagdelay = image_find_option(IMAGE_CFG_JTAG_DELAY); + e_boxid = image_find_option(IMAGE_CFG_BOX_ID); + e_flashid = image_find_option(IMAGE_CFG_FLASH_ID); + + if (kwb_load_csk(params, &csk) < 0) + return 1; + + secure_hdr->headertype = OPT_HDR_V1_SECURE_TYPE; + secure_hdr->headersz_msb = 0; + secure_hdr->headersz_lsb = cpu_to_le16(sizeof(struct secure_hdr_v1)); + if (e_jtagdelay) + secure_hdr->jtag_delay = e_jtagdelay->jtag_delay; + if (e_boxid && specialized_img) + secure_hdr->boxid = cpu_to_le32(e_boxid->boxid); + if (e_flashid && specialized_img) + secure_hdr->flashid = cpu_to_le32(e_flashid->flashid); + + if (kwb_sign_csk_with_kak(params, secure_hdr, csk)) + return 1; + + image_ptr = ptr + headersz; + image_size = payloadsz - headersz; + + if (kwb_sign_and_verify(csk, image_ptr, image_size, + &secure_hdr->imgsig, "image") < 0) + return 1; + + if (kwb_sign_and_verify(csk, image, headersz, &tmp_sig, "header") < 0) + return 1; + + secure_hdr->hdrsig = tmp_sig; + + kwb_dump_fuse_cmds(secure_hdr); + + return 0; +} +#endif + +static void *image_create_v1(size_t *imagesz, struct image_tool_params *params, + uint8_t *ptr, int payloadsz) +{ + struct image_cfg_element *e; + struct main_hdr_v1 *main_hdr; +#if defined(CONFIG_KWB_SECURE) + struct secure_hdr_v1 *secure_hdr = NULL; +#endif + size_t headersz; + uint8_t *image, *cur; + int hasext = 0; + uint8_t *next_ext = NULL; + /* * Calculate the size of the header and the size of the * payload */ - headersz = image_headersz_v1(params, &hasext); + headersz = image_headersz_v1(&hasext); if (headersz == 0) return NULL; @@ -402,15 +1182,18 @@ static void *image_create_v1(size_t *imagesz, struct image_tool_params *params, memset(image, 0, headersz); - cur = main_hdr = image; + main_hdr = (struct main_hdr_v1 *)image; + cur = image; cur += sizeof(struct main_hdr_v1); + next_ext = &main_hdr->ext; /* Fill the main header */ main_hdr->blocksize = cpu_to_le32(payloadsz - headersz + sizeof(uint32_t)); main_hdr->headersz_lsb = cpu_to_le16(headersz & 0xFFFF); main_hdr->headersz_msb = (headersz & 0xFFFF0000) >> 16; - main_hdr->destaddr = cpu_to_le32(params->addr); + main_hdr->destaddr = cpu_to_le32(params->addr) + - sizeof(image_header_t); main_hdr->execaddr = cpu_to_le32(params->ep); main_hdr->srcaddr = cpu_to_le32(headersz); main_hdr->ext = hasext; @@ -431,71 +1214,27 @@ static void *image_create_v1(size_t *imagesz, struct image_tool_params *params, if (e) main_hdr->flags = e->debug ? 0x1 : 0; - binarye = image_find_option(IMAGE_CFG_BINARY); - if (binarye) { - struct opt_hdr_v1 *hdr = cur; - uint32_t *args; - size_t binhdrsz; - struct stat s; - int argi; - FILE *bin; - - hdr->headertype = OPT_HDR_V1_BINARY_TYPE; - - bin = fopen(binarye->binary.file, "r"); - if (!bin) { - fprintf(stderr, "Cannot open binary file %s\n", - binarye->binary.file); - return NULL; - } - - fstat(fileno(bin), &s); - - binhdrsz = sizeof(struct opt_hdr_v1) + - (binarye->binary.nargs + 2) * sizeof(uint32_t) + - s.st_size; - +#if defined(CONFIG_KWB_SECURE) + if (image_get_csk_index() >= 0) { /* - * The size includes the binary image size, rounded - * up to a 4-byte boundary. Plus 4 bytes for the - * next-header byte and 3-byte alignment at the end. + * only reserve the space here; we fill the header later since + * we need the header to be complete to compute the signatures */ - binhdrsz = ALIGN_SUP(binhdrsz, 4) + 4; - hdr->headersz_lsb = cpu_to_le16(binhdrsz & 0xFFFF); - hdr->headersz_msb = (binhdrsz & 0xFFFF0000) >> 16; - - cur += sizeof(struct opt_hdr_v1); - - args = cur; - *args = cpu_to_le32(binarye->binary.nargs); - args++; - for (argi = 0; argi < binarye->binary.nargs; argi++) - args[argi] = cpu_to_le32(binarye->binary.args[argi]); - - cur += (binarye->binary.nargs + 1) * sizeof(uint32_t); - - ret = fread(cur, s.st_size, 1, bin); - if (ret != 1) { - fprintf(stderr, - "Could not read binary image %s\n", - binarye->binary.file); - return NULL; - } - - fclose(bin); - - cur += ALIGN_SUP(s.st_size, 4); - - /* - * For now, we don't support more than one binary - * header, and no other header types are - * supported. So, the binary header is necessarily the - * last one - */ - *((uint32_t *)cur) = 0x00000000; - - cur += sizeof(uint32_t); + secure_hdr = (struct secure_hdr_v1 *)cur; + cur += sizeof(struct secure_hdr_v1); + next_ext = &secure_hdr->next; } +#endif + *next_ext = 1; + + if (add_binary_header_v1(cur)) + return NULL; + +#if defined(CONFIG_KWB_SECURE) + if (secure_hdr && add_secure_header_v1(params, ptr, payloadsz, + headersz, image, secure_hdr)) + return NULL; +#endif /* Calculate and set the header checksum */ main_hdr->checksum = image_checksum8(main_hdr, headersz); @@ -504,72 +1243,94 @@ static void *image_create_v1(size_t *imagesz, struct image_tool_params *params, return image; } +int recognize_keyword(char *keyword) +{ + int kw_id; + + for (kw_id = 1; kw_id < IMAGE_CFG_COUNT; ++kw_id) + if (!strcmp(keyword, id_strs[kw_id])) + return kw_id; + + return 0; +} + static int image_create_config_parse_oneline(char *line, struct image_cfg_element *el) { - char *keyword, *saveptr; - char deliminiters[] = " \t"; + char *keyword, *saveptr, *value1, *value2; + char delimiters[] = " \t"; + int keyword_id, ret, argi; + char *unknown_msg = "Ignoring unknown line '%s'\n"; + + keyword = strtok_r(line, delimiters, &saveptr); + keyword_id = recognize_keyword(keyword); + + if (!keyword_id) { + fprintf(stderr, unknown_msg, line); + return 0; + } + + el->type = keyword_id; + + value1 = strtok_r(NULL, delimiters, &saveptr); + + if (!value1) { + fprintf(stderr, "Parameter missing in line '%s'\n", line); + return -1; + } + + switch (keyword_id) { + case IMAGE_CFG_VERSION: + el->version = atoi(value1); + break; + case IMAGE_CFG_BOOT_FROM: + ret = image_boot_mode_id(value1); - keyword = strtok_r(line, deliminiters, &saveptr); - if (!strcmp(keyword, "VERSION")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - el->type = IMAGE_CFG_VERSION; - el->version = atoi(value); - } else if (!strcmp(keyword, "BOOT_FROM")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - int ret = image_boot_mode_id(value); if (ret < 0) { - fprintf(stderr, - "Invalid boot media '%s'\n", value); + fprintf(stderr, "Invalid boot media '%s'\n", value1); return -1; } - el->type = IMAGE_CFG_BOOT_FROM; el->bootfrom = ret; - } else if (!strcmp(keyword, "NAND_BLKSZ")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - el->type = IMAGE_CFG_NAND_BLKSZ; - el->nandblksz = strtoul(value, NULL, 16); - } else if (!strcmp(keyword, "NAND_BADBLK_LOCATION")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - el->type = IMAGE_CFG_NAND_BADBLK_LOCATION; - el->nandbadblklocation = - strtoul(value, NULL, 16); - } else if (!strcmp(keyword, "NAND_ECC_MODE")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - int ret = image_nand_ecc_mode_id(value); + break; + case IMAGE_CFG_NAND_BLKSZ: + el->nandblksz = strtoul(value1, NULL, 16); + break; + case IMAGE_CFG_NAND_BADBLK_LOCATION: + el->nandbadblklocation = strtoul(value1, NULL, 16); + break; + case IMAGE_CFG_NAND_ECC_MODE: + ret = image_nand_ecc_mode_id(value1); + if (ret < 0) { - fprintf(stderr, - "Invalid NAND ECC mode '%s'\n", value); + fprintf(stderr, "Invalid NAND ECC mode '%s'\n", value1); return -1; } - el->type = IMAGE_CFG_NAND_ECC_MODE; el->nandeccmode = ret; - } else if (!strcmp(keyword, "NAND_PAGE_SIZE")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - el->type = IMAGE_CFG_NAND_PAGESZ; - el->nandpagesz = strtoul(value, NULL, 16); - } else if (!strcmp(keyword, "BINARY")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - int argi = 0; + break; + case IMAGE_CFG_NAND_PAGESZ: + el->nandpagesz = strtoul(value1, NULL, 16); + break; + case IMAGE_CFG_BINARY: + argi = 0; - el->type = IMAGE_CFG_BINARY; - el->binary.file = strdup(value); + el->binary.file = strdup(value1); while (1) { - value = strtok_r(NULL, deliminiters, &saveptr); + char *value = strtok_r(NULL, delimiters, &saveptr); + if (!value) break; el->binary.args[argi] = strtoul(value, NULL, 16); argi++; if (argi >= BINARY_MAX_ARGS) { fprintf(stderr, - "Too many argument for binary\n"); + "Too many arguments for BINARY\n"); return -1; } } el->binary.nargs = argi; - } else if (!strcmp(keyword, "DATA")) { - char *value1 = strtok_r(NULL, deliminiters, &saveptr); - char *value2 = strtok_r(NULL, deliminiters, &saveptr); + break; + case IMAGE_CFG_DATA: + value2 = strtok_r(NULL, delimiters, &saveptr); if (!value1 || !value2) { fprintf(stderr, @@ -577,19 +1338,47 @@ static int image_create_config_parse_oneline(char *line, return -1; } - el->type = IMAGE_CFG_DATA; el->regdata.raddr = strtoul(value1, NULL, 16); el->regdata.rdata = strtoul(value2, NULL, 16); - } else if (!strcmp(keyword, "BAUDRATE")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - el->type = IMAGE_CFG_BAUDRATE; - el->baudrate = strtoul(value, NULL, 10); - } else if (!strcmp(keyword, "DEBUG")) { - char *value = strtok_r(NULL, deliminiters, &saveptr); - el->type = IMAGE_CFG_DEBUG; - el->debug = strtoul(value, NULL, 10); - } else { - fprintf(stderr, "Ignoring unknown line '%s'\n", line); + break; + case IMAGE_CFG_BAUDRATE: + el->baudrate = strtoul(value1, NULL, 10); + break; + case IMAGE_CFG_DEBUG: + el->debug = strtoul(value1, NULL, 10); + break; + case IMAGE_CFG_KAK: + el->key_name = strdup(value1); + break; + case IMAGE_CFG_CSK: + el->key_name = strdup(value1); + break; + case IMAGE_CFG_CSK_INDEX: + el->csk_idx = strtol(value1, NULL, 0); + break; + case IMAGE_CFG_JTAG_DELAY: + el->jtag_delay = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_BOX_ID: + el->boxid = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_FLASH_ID: + el->flashid = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_SEC_SPECIALIZED_IMG: + el->sec_specialized_img = true; + break; + case IMAGE_CFG_SEC_COMMON_IMG: + el->sec_specialized_img = false; + break; + case IMAGE_CFG_SEC_BOOT_DEV: + el->sec_boot_dev = strtoul(value1, NULL, 0); + break; + case IMAGE_CFG_SEC_FUSE_DUMP: + el->name = strdup(value1); + break; + default: + fprintf(stderr, unknown_msg, line); } return 0; @@ -747,7 +1536,7 @@ static void kwbimage_set_header(void *ptr, struct stat *sbuf, int ifd, break; case 1: - image = image_create_v1(&headersz, params, sbuf->st_size); + image = image_create_v1(&headersz, params, ptr, sbuf->st_size); break; default: @@ -799,18 +1588,17 @@ static int kwbimage_check_image_types(uint8_t type) { if (type == IH_TYPE_KWBIMAGE) return EXIT_SUCCESS; - else - return EXIT_FAILURE; + + return EXIT_FAILURE; } static int kwbimage_verify_header(unsigned char *ptr, int image_size, struct image_tool_params *params) { struct main_hdr_v0 *main_hdr; - struct ext_hdr_v0 *ext_hdr; uint8_t checksum; - main_hdr = (void *)ptr; + main_hdr = (struct main_hdr_v0 *)ptr; checksum = image_checksum8(ptr, sizeof(struct main_hdr_v0) - sizeof(uint8_t)); @@ -819,7 +1607,10 @@ static int kwbimage_verify_header(unsigned char *ptr, int image_size, /* Only version 0 extended header has checksum */ if (image_version((void *)ptr) == 0) { - ext_hdr = (void *)ptr + sizeof(struct main_hdr_v0); + struct ext_hdr_v0 *ext_hdr; + + ext_hdr = (struct ext_hdr_v0 *) + (ptr + sizeof(struct main_hdr_v0)); checksum = image_checksum8(ext_hdr, sizeof(struct ext_hdr_v0) - sizeof(uint8_t)); @@ -842,7 +1633,7 @@ static int kwbimage_generate(struct image_tool_params *params, alloc_len = sizeof(struct main_hdr_v0) + sizeof(struct ext_hdr_v0); } else { - alloc_len = image_headersz_v1(params, NULL); + alloc_len = image_headersz_v1(NULL); } hdr = malloc(alloc_len); @@ -873,9 +1664,9 @@ static int kwbimage_generate(struct image_tool_params *params, static int kwbimage_check_params(struct image_tool_params *params) { if (!strlen(params->imagename)) { - fprintf(stderr, "Error:%s - Configuration file not specified, " - "it is needed for kwbimage generation\n", - params->cmdname); + char *msg = "Configuration file for kwbimage creation omitted"; + + fprintf(stderr, "Error:%s - %s\n", params->cmdname, msg); return CFG_INVALID; } diff --git a/tools/kwbimage.h b/tools/kwbimage.h index 01c2f1f323..20f4d0d9dd 100644 --- a/tools/kwbimage.h +++ b/tools/kwbimage.h @@ -113,6 +113,43 @@ struct opt_hdr_v1 { char data[0]; }; +/* + * Public Key data in DER format + */ +struct pubkey_der_v1 { + uint8_t key[524]; +}; + +/* + * Signature (RSA 2048) + */ +struct sig_v1 { + uint8_t sig[256]; +}; + +/* + * Structure of secure header (Armada 38x) + */ +struct secure_hdr_v1 { + uint8_t headertype; /* 0x0 */ + uint8_t headersz_msb; /* 0x1 */ + uint16_t headersz_lsb; /* 0x2 - 0x3 */ + uint32_t reserved1; /* 0x4 - 0x7 */ + struct pubkey_der_v1 kak; /* 0x8 - 0x213 */ + uint8_t jtag_delay; /* 0x214 */ + uint8_t reserved2; /* 0x215 */ + uint16_t reserved3; /* 0x216 - 0x217 */ + uint32_t boxid; /* 0x218 - 0x21B */ + uint32_t flashid; /* 0x21C - 0x21F */ + struct sig_v1 hdrsig; /* 0x220 - 0x31F */ + struct sig_v1 imgsig; /* 0x320 - 0x41F */ + struct pubkey_der_v1 csk[16]; /* 0x420 - 0x24DF */ + struct sig_v1 csksig; /* 0x24E0 - 0x25DF */ + uint8_t next; /* 0x25E0 */ + uint8_t reserved4; /* 0x25E1 */ + uint16_t reserved5; /* 0x25E2 - 0x25E3 */ +}; + /* * Various values for the opt_hdr_v1->headertype field, describing the * different types of optional headers. The "secure" header contains