From 8f4ce6404382e02a30fcef4d93fac0770110170e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 19 Jul 2019 14:14:43 +0200 Subject: [PATCH] mx6: hab: add function to authenticate any data blob via HAB --- arch/arm/cpu/armv7/mx6/hab.c | 387 +++++++++++++++++++++++++--- arch/arm/include/asm/arch-mx6/hab.h | 40 ++- 2 files changed, 391 insertions(+), 36 deletions(-) diff --git a/arch/arm/cpu/armv7/mx6/hab.c b/arch/arm/cpu/armv7/mx6/hab.c index 83b1e26a10..64e687e379 100644 --- a/arch/arm/cpu/armv7/mx6/hab.c +++ b/arch/arm/cpu/armv7/mx6/hab.c @@ -24,11 +24,15 @@ HAB_FUNC3(assert, enum hab_status, uint8_t, const void *, size_t) #define MAX_RECORD_BYTES (8 * 1024) +#define MX6DQ_PU_IROM_MMU_EN_VAR 0x009024a8 +#define MX6SDL_PU_IROM_MMU_EN_VAR 0x00901dd0 +#define MX6SL_PU_IROM_MMU_EN_VAR 0x00900a18 + struct record { - uint8_t tag; /* Tag */ - uint8_t len[2]; /* Length */ - uint8_t par; /* Version */ - uint8_t contents[MAX_RECORD_BYTES];/* Record Data */ + uint8_t tag; /* Tag */ + uint8_t len[2]; /* Length */ + uint8_t par; /* Version */ + uint8_t contents[MAX_RECORD_BYTES]; /* Record Data */ bool any_rec_flag; }; @@ -239,32 +243,34 @@ void display_event(uint8_t *event_data, size_t bytes) process_event_record(event_data, bytes); } +static uint32_t last_hab_event __attribute__((section(".data"))); + int get_hab_status(void) { - static uint32_t last_hab_event __attribute__((section(".data"))); uint32_t index = last_hab_event; /* Loop index */ uint8_t event_data[128]; /* Event data buffer */ size_t bytes = sizeof(event_data); /* Event size in bytes */ - enum hab_config config; - enum hab_state state; + enum hab_config config = 0; + enum hab_state state = 0; + static int first = 1; int ret; - if (is_hab_enabled()) - puts("Secure boot enabled\n"); - else - puts("Secure boot disabled\n"); + if (first) { + printf("Secure boot %sabled\n", + is_hab_enabled() ? "en" : "dis"); + } /* Check HAB status */ enable_ocotp_clk(1); - config = state = 0; /* ROM code assumes short enums! */ ret = hab_rvt_report_status(&config, &state); enable_ocotp_clk(0); - printf("HAB Configuration: 0x%02x, HAB State: 0x%02x\n", - config, state); + if (first) + printf("HAB Configuration: 0x%02x, HAB State: 0x%02x\n", + config, state); if (ret != HAB_SUCCESS) { /* Display HAB Error events */ while (hab_rvt_report_event(HAB_STS_ANY, index, event_data, - &bytes) == HAB_SUCCESS) { + &bytes) == HAB_SUCCESS) { puts("\n"); printf("--------- HAB Event %d -----------------\n", index + 1); @@ -281,18 +287,37 @@ int get_hab_status(void) puts("No HAB Events Found!\n"); ret = 0; } + first = 0; return ret; } +static inline void hab_clear_events(void) +{ + uint32_t index = last_hab_event; /* Loop index */ + uint8_t event_data[128]; /* Event data buffer */ + size_t bytes = sizeof(event_data); /* Event size in bytes */ + enum hab_config config = 0; + enum hab_state state = 0; + int ret; + + /* Check HAB status */ + enable_ocotp_clk(1); + ret = hab_rvt_report_status(&config, &state); + enable_ocotp_clk(0); + if (ret != HAB_SUCCESS) { + while (hab_rvt_report_event(HAB_STS_ANY, index, event_data, + &bytes) == HAB_SUCCESS) { + index++; + } + last_hab_event = index; + } +} + static inline enum hab_status hab_init(void) { enum hab_status ret; enable_ocotp_clk(1); - if (!is_hab_enabled()) { - puts("hab fuse not enabled\n"); - return HAB_FAILURE; - } hab_caam_clock_enable(1); @@ -433,24 +458,316 @@ static int do_hab_assert(cmd_tbl_t *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; } +/* Get CSF Header length */ +static int get_hab_hdr_len(struct hab_hdr *hdr) +{ + return (size_t)((hdr->len[0] << 8) + (hdr->len[1])); +} + +/* Check whether addr lies between start and + * end and is within the length of the image + */ +static int chk_bounds(u8 *addr, size_t bytes, u8 *start, u8 *end) +{ + size_t csf_size = (size_t)((end + 1) - addr); + + return addr && (addr >= start) && (addr <= end) && + (csf_size >= bytes); +} + +/* Get Length of each command in CSF */ +static int get_csf_cmd_hdr_len(u8 *csf_hdr) +{ + if (*csf_hdr == HAB_CMD_HDR) + return sizeof(struct hab_hdr); + + return get_hab_hdr_len((struct hab_hdr *)csf_hdr); +} + +/* Check if CSF is valid */ +static inline bool csf_is_valid(struct ivt *ivt, void *addr, size_t bytes) +{ + u8 *start = addr; + u8 *csf_hdr; + u8 *end; + + size_t csf_hdr_len; + size_t cmd_hdr_len; + size_t offset = 0; + + if (bytes != 0) + end = start + bytes - 1; + else + end = start; + + /* Verify if CSF pointer content is zero */ + if (!ivt->csf) { + puts("Error: CSF pointer is NULL\n"); + return false; + } + + csf_hdr = (u8 *)(uintptr_t)ivt->csf; + + /* Verify if CSF Header exist */ + if (*csf_hdr != HAB_CMD_HDR) { + puts("Error: CSF header command not found\n"); + return false; + } + + csf_hdr_len = get_hab_hdr_len((struct hab_hdr *)csf_hdr); + + /* Check if the CSF lies within the image bounds */ + if (!chk_bounds(csf_hdr, csf_hdr_len, start, end)) { + puts("Error: CSF lies outside the image bounds\n"); + return false; + } + + do { + struct hab_hdr *cmd; + + cmd = (struct hab_hdr *)&csf_hdr[offset]; + + switch (cmd->tag) { + case (HAB_CMD_WRT_DAT): + puts("Error: Deprecated write command found\n"); + return false; + case (HAB_CMD_CHK_DAT): + puts("Error: Deprecated check command found\n"); + return false; + case (HAB_CMD_SET): + if (cmd->par == HAB_PAR_MID) { + puts("Error: Deprecated Set MID command found\n"); + return false; + } + default: + break; + } + + cmd_hdr_len = get_csf_cmd_hdr_len(&csf_hdr[offset]); + if (!cmd_hdr_len) { + puts("Error: Invalid command length\n"); + return false; + } + offset += cmd_hdr_len; + + } while (offset < csf_hdr_len); + + return true; +} + +static int ivt_header_error(const char *err_str, struct ivt_header *ivt_hdr) +{ + printf("%s magic=0x%x length=0x%02x version=0x%x\n", err_str, + ivt_hdr->magic, ivt_hdr->length, ivt_hdr->version); + + return 1; +} + +static int verify_ivt_header(struct ivt_header *ivt_hdr) +{ + if (ivt_hdr->magic != IVT_HEADER_MAGIC) + return ivt_header_error("bad IVT magic", ivt_hdr); + + if (be16_to_cpu(ivt_hdr->length) != IVT_TOTAL_LENGTH) + return ivt_header_error("bad IVT length", ivt_hdr); + + if (ivt_hdr->version != IVT_HEADER_V1 && + ivt_hdr->version != IVT_HEADER_V2) + return ivt_header_error("bad IVT version", ivt_hdr); + + return 0; +} + +/* + * Validate IVT structure of the image being authenticated + */ +static int validate_ivt(struct ivt *ivt) +{ + struct ivt_header *ivt_hdr = &ivt->hdr; + + if ((uintptr_t)ivt & 0x3) { + puts("Error: IVT address is not 4 byte aligned\n"); + return 0; + } + + /* Check IVT fields before allowing authentication */ + if ((!verify_ivt_header(ivt_hdr)) && + (ivt->entry != 0x0) && + (ivt->reserved1 == 0x0) && + (ivt->self == (uint32_t)ivt) && + (ivt->csf != 0x0) && + (ivt->reserved2 == 0x0) && + (ivt->dcd == 0x0)) { + return 1; + } + + puts("Error: Invalid IVT structure\n"); + if (ivt->entry == 0x0) + printf("entry address must not be NULL\n"); + if (ivt->reserved1 != 0x0) + printf("reserved word at offset 0x%02x should be NULL\n", + offsetof(struct ivt, reserved1)); + if (ivt->dcd != 0x0) + puts("Error: DCD pointer must be 0\n"); + if (ivt->self != (uint32_t)ivt) + printf("SELF pointer does not point to IVT\n"); + if (ivt->csf == 0x0) + printf("CSF address must not be NULL\n"); + if (ivt->reserved2 != 0x0) + printf("reserved word at offset 0x%02x should be NULL\n", + offsetof(struct ivt, reserved2)); + puts("\nAllowed IVT structure:\n"); + printf("IVT HDR = 0x4X2000D1 (0x%08x)\n", *((u32 *)&ivt->hdr)); + printf("IVT ENTRY = 0xXXXXXXXX (0x%08x)\n", ivt->entry); + printf("IVT RSV1 = 0x00000000 (0x%08x)\n", ivt->reserved1); + printf("IVT DCD = 0x00000000 (0x%08x)\n", ivt->dcd); + printf("IVT BOOT_DATA = 0xXXXXXXXX (0x%08x)\n", ivt->boot); + printf("IVT SELF = 0xXXXXXXXX (0x%08x)\n", ivt->self); + printf("IVT CSF = 0xXXXXXXXX (0x%08x)\n", ivt->csf); + printf("IVT RSV2 = 0x00000000 (0x%08x)\n", ivt->reserved2); + + /* Invalid IVT structure */ + return 0; +} + +bool imx_hab_authenticate_image(void *addr, size_t len, size_t ivt_offset) +{ + struct ivt *ivt; + enum hab_status status; + + if (len <= sizeof(ivt)) { + printf("Invalid image size %08x\n", len); + return false; + } + if (ivt_offset >= len - sizeof(*ivt)) { + printf("IVT offset must be smaller than image size - %u\n", + sizeof(*ivt)); + return false; + } + + ivt = addr + ivt_offset; + /* Verify IVT header bugging out on error */ + if (!validate_ivt(ivt)) { + print_buffer((ulong)ivt, ivt, 4, 0x8, 0); + return false; + } + + if (!csf_is_valid(ivt, addr, len)) { + print_buffer(ivt->csf, (void *)(ivt->csf), 4, 0x10, 0); + return false; + } + + status = hab_init(); + if (status != HAB_SUCCESS) + goto hab_auth_error; + + hab_clear_events(); + + status = hab_rvt_check_target(HAB_TGT_MEMORY, addr, len); + if (status != HAB_SUCCESS) { + printf("HAB check target 0x%p-0x%p failed\n", + addr, addr + len); + goto hab_auth_error; + } + get_hab_status(); + + /* + * If the MMU is enabled, we have to notify the ROM + * code, or it won't flush the caches when needed. + * This is done, by setting the "pu_irom_mmu_enabled" + * word to 1. You can find its address by looking in + * the ROM map. This is critical for + * authenticate_image(). If MMU is enabled, without + * setting this bit, authentication will fail and may + * crash. + */ + /* Check MMU enabled */ + if (get_cr() & CR_M) { + if (is_cpu_type(MXC_CPU_MX6Q) || is_cpu_type(MXC_CPU_MX6D)) { + /* + * This won't work on Rev 1.0.0 of + * i.MX6Q/D, since their ROM doesn't + * do cache flushes. don't think any + * exist, so we ignore them. + */ + if (!is_mx6dqp()) + writel(1, MX6DQ_PU_IROM_MMU_EN_VAR); + } else if (is_cpu_type(MXC_CPU_MX6SOLO) || + is_cpu_type(MXC_CPU_MX6DL)) { + writel(1, MX6SDL_PU_IROM_MMU_EN_VAR); + } else if (is_cpu_type(MXC_CPU_MX6SL)) { + writel(1, MX6SL_PU_IROM_MMU_EN_VAR); + } + } + + hab_rvt_authenticate_image(HAB_CID_UBOOT, ivt_offset, &addr, + &len, NULL); + status = hab_exit(); + if (status != HAB_SUCCESS) + goto hab_auth_error; + + if (get_hab_status() != 0) { + printf("ERROR: hab_authenticate_image failed\n"); + return false; + } + printf("HAB authentication of image at %p..%p successful\n", + addr, addr + len); + return true; + + hab_auth_error: + printf("ERROR: HAB authentication failure: %02x\n", status); + get_hab_status(); + return false; +} + +static int do_authenticate_image(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + void *addr; + size_t ivt_offset, len; + + if (argc < 4) + return CMD_RET_USAGE; + + addr = (void *)simple_strtoul(argv[1], NULL, 16); + len = simple_strtoul(argv[2], NULL, 16); + ivt_offset = simple_strtoul(argv[3], NULL, 16); + + if (imx_hab_authenticate_image(addr, len, ivt_offset)) + return CMD_RET_SUCCESS; + + get_hab_status(); + + return CMD_RET_FAILURE; +} + +U_BOOT_CMD( + hab_status, CONFIG_SYS_MAXARGS, 1, do_hab_status, + "display HAB status", + "" + ); + U_BOOT_CMD( - hab_status, CONFIG_SYS_MAXARGS, 1, do_hab_status, - "display HAB status", - "" - ); + hab_check_target, 4, 0, do_hab_check_target, + "verify an address range via HAB", + "addr len [type]\n" + "\t\taddr -\taddress to verify\n" + "\t\tlen -\tlength of addr range to verify\n" + ); U_BOOT_CMD( - hab_check_target, 4, 0, do_hab_check_target, - "verify an address range via HAB", - "addr len [type]\n" - "\t\taddr -\taddress to verify\n" - "\t\tlen -\tlength of addr range to verify\n" - ); + hab_assert, 4, 0, do_hab_assert, + "Test an assertion against the HAB audit log", + "addr len [type]\n" + "\t\taddr -\taddress to verify\n" + "\t\tlen -\tlength of addr range to verify\n" + ); U_BOOT_CMD( - hab_assert, 4, 0, do_hab_assert, - "Test an assertion against the HAB audit log", - "addr len [type]\n" - "\t\taddr -\taddress to verify\n" - "\t\tlen -\tlength of addr range to verify\n" - ); + hab_auth_img, 4, 0, do_authenticate_image, + "authenticate image via HAB", + "addr len ivt_offset\n" + "addr - image hex address\n" + "len - image size (hex)\n" + "ivt_offset - hex offset of IVT in the image" + ); diff --git a/arch/arm/include/asm/arch-mx6/hab.h b/arch/arm/include/asm/arch-mx6/hab.h index 770327ccef..f3e2dc89ea 100644 --- a/arch/arm/include/asm/arch-mx6/hab.h +++ b/arch/arm/include/asm/arch-mx6/hab.h @@ -14,6 +14,39 @@ #include int get_hab_status(void); +/* + * IVT header definitions + * Security Reference Manual for i.MX 7Dual and 7Solo Applications Processors, + * Rev. 0, 03/2017 + * Section : 6.7.1.1 + */ +#define IVT_HEADER_MAGIC 0xD1 +#define IVT_TOTAL_LENGTH 0x20 +#define IVT_HEADER_V1 0x40 +#define IVT_HEADER_V2 0x41 + +struct __packed ivt_header { + uint8_t magic; + uint16_t length; + uint8_t version; +}; + +struct ivt { + struct ivt_header hdr; /* IVT header above */ + uint32_t entry; /* Absolute address of first instruction */ + uint32_t reserved1; /* Reserved should be zero */ + uint32_t dcd; /* Absolute address of the image DCD */ + uint32_t boot; /* Absolute address of the boot data */ + uint32_t self; /* Absolute address of the IVT */ + uint32_t csf; /* Absolute address of the CSF */ + uint32_t reserved2; /* Reserved should be zero */ +}; + +struct __packed hab_hdr { + u8 tag; /* Tag field */ + u8 len[2]; /* Length field in bytes (big-endian) */ + u8 par; /* Parameters field */ +}; /* -------- start of HAB API updates ------------*/ /* The following are taken from HAB4 SIS */ @@ -200,6 +233,11 @@ static inline void **hab_rvt_base(void) #define HAB_CID_ROM 0 /**< ROM Caller ID */ #define HAB_CID_UBOOT 1 /**< UBOOT Caller ID*/ +#define HAB_CMD_HDR 0xD4 /* CSF Header */ +#define HAB_CMD_WRT_DAT 0xCC /* Write Data command tag */ +#define HAB_CMD_CHK_DAT 0xCF /* Check Data command tag */ +#define HAB_CMD_SET 0xB1 /* Set command tag */ +#define HAB_PAR_MID 0x01 /* MID parameter value */ /* ----------- end of HAB API updates ------------*/ #define hab_rvt_entry_p \ @@ -270,7 +308,7 @@ static inline rt hab_rvt_##n(t1 p1, t2 p2, t3 p3, t4 p4) \ } #define HAB_FUNC5(n, rt, t1, t2, t3, t4, t5) \ -static inline rt hab_rvt_##n(t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) \ +static inline rt hab_rvt_##n(t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) \ { \ if (hab_rvt_base() == NULL) \ return (rt)-1; \ -- 2.39.2