diff --git a/drivers/st/bsec/bsec.c b/drivers/st/bsec/bsec.c index b3c15ee..01c369e 100644 --- a/drivers/st/bsec/bsec.c +++ b/drivers/st/bsec/bsec.c @@ -841,22 +841,6 @@ } /* - * bsec_mode_is_closed_device: read OTP secure sub-mode. - * return: false if open_device and true of closed_device. - */ -bool bsec_mode_is_closed_device(void) -{ - uint32_t value; - - if ((bsec_shadow_register(DATA0_OTP) != BSEC_OK) || - (bsec_read_otp(&value, DATA0_OTP) != BSEC_OK)) { - return true; - } - - return (value & DATA0_OTP_SECURED) == DATA0_OTP_SECURED; -} - -/* * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value * otp_value: read value. * word: OTP number. @@ -894,7 +878,7 @@ if (otp >= STM32MP1_UPPER_OTP_START) { /* Check if BSEC is in OTP-SECURED closed_device state. */ - if (bsec_mode_is_closed_device()) { + if (stm32mp_is_closed_device()) { if (!non_secure_can_access(otp)) { return BSEC_ERROR; } diff --git a/drivers/st/crypto/stm32_hash.c b/drivers/st/crypto/stm32_hash.c new file mode 100644 index 0000000..f72787d --- /dev/null +++ b/drivers/st/crypto/stm32_hash.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DT_HASH_COMPAT "st,stm32f756-hash" + +#define HASH_CR 0x00U +#define HASH_DIN 0x04U +#define HASH_STR 0x08U +#define HASH_SR 0x24U +#define HASH_HREG(x) (0x310U + ((x) * 0x04U)) + +/* Control Register */ +#define HASH_CR_INIT BIT(2) +#define HASH_CR_DATATYPE_SHIFT U(4) + +#define HASH_CR_ALGO_SHA1 0x0U +#define HASH_CR_ALGO_MD5 BIT(7) +#define HASH_CR_ALGO_SHA224 BIT(18) +#define HASH_CR_ALGO_SHA256 (BIT(18) | BIT(7)) + +/* Status Flags */ +#define HASH_SR_DCIS BIT(1) +#define HASH_SR_BUSY BIT(3) + +/* STR Register */ +#define HASH_STR_NBLW_MASK GENMASK(4, 0) +#define HASH_STR_DCAL BIT(8) + +#define MD5_DIGEST_SIZE 16U +#define SHA1_DIGEST_SIZE 20U +#define SHA224_DIGEST_SIZE 28U +#define SHA256_DIGEST_SIZE 32U + +#define HASH_TIMEOUT_US 10000U + +enum stm32_hash_data_format { + HASH_DATA_32_BITS, + HASH_DATA_16_BITS, + HASH_DATA_8_BITS, + HASH_DATA_1_BIT +}; + +struct stm32_hash_instance { + uintptr_t base; + unsigned int clock; + size_t digest_size; +}; + +struct stm32_hash_remain { + uint32_t buffer; + size_t length; +}; + +/* Expect a single HASH peripheral */ +static struct stm32_hash_instance stm32_hash; +static struct stm32_hash_remain stm32_remain; + +static uintptr_t hash_base(void) +{ + return stm32_hash.base; +} + +static int hash_wait_busy(void) +{ + uint64_t timeout = timeout_init_us(HASH_TIMEOUT_US); + + while ((mmio_read_32(hash_base() + HASH_SR) & HASH_SR_BUSY) != 0U) { + if (timeout_elapsed(timeout)) { + ERROR("%s: busy timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int hash_wait_computation(void) +{ + uint64_t timeout = timeout_init_us(HASH_TIMEOUT_US); + + while ((mmio_read_32(hash_base() + HASH_SR) & HASH_SR_DCIS) == 0U) { + if (timeout_elapsed(timeout)) { + ERROR("%s: busy timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int hash_write_data(uint32_t data) +{ + int ret; + + ret = hash_wait_busy(); + if (ret != 0) { + return ret; + } + + mmio_write_32(hash_base() + HASH_DIN, data); + + return 0; +} + +static void hash_hw_init(enum stm32_hash_algo_mode mode) +{ + uint32_t reg; + + reg = HASH_CR_INIT | (HASH_DATA_8_BITS << HASH_CR_DATATYPE_SHIFT); + + switch (mode) { + case HASH_MD5SUM: + reg |= HASH_CR_ALGO_MD5; + stm32_hash.digest_size = MD5_DIGEST_SIZE; + break; + case HASH_SHA1: + reg |= HASH_CR_ALGO_SHA1; + stm32_hash.digest_size = SHA1_DIGEST_SIZE; + break; + case HASH_SHA224: + reg |= HASH_CR_ALGO_SHA224; + stm32_hash.digest_size = SHA224_DIGEST_SIZE; + break; + /* Default selected algo is SHA256 */ + case HASH_SHA256: + default: + reg |= HASH_CR_ALGO_SHA256; + stm32_hash.digest_size = SHA256_DIGEST_SIZE; + break; + } + + mmio_write_32(hash_base() + HASH_CR, reg); +} + +static int hash_get_digest(uint8_t *digest) +{ + int ret; + uint32_t i; + uint32_t dsg; + + ret = hash_wait_computation(); + if (ret != 0) { + return ret; + } + + for (i = 0U; i < (stm32_hash.digest_size / sizeof(uint32_t)); i++) { + dsg = __builtin_bswap32(mmio_read_32(hash_base() + + HASH_HREG(i))); + memcpy(digest + (i * sizeof(uint32_t)), &dsg, sizeof(uint32_t)); + } + +#if defined(IMAGE_BL2) + /* + * Clean hardware context as HASH could be used later + * by non-secure software + */ + hash_hw_init(HASH_SHA256); +#endif + return 0; +} + +int stm32_hash_update(const uint8_t *buffer, size_t length) +{ + size_t remain_length = length; + int ret = 0; + + if ((length == 0U) || (buffer == NULL)) { + return 0; + } + + stm32mp_clk_enable(stm32_hash.clock); + + if (stm32_remain.length != 0U) { + uint32_t copysize; + + copysize = MIN((sizeof(uint32_t) - stm32_remain.length), + length); + memcpy(((uint8_t *)&stm32_remain.buffer) + stm32_remain.length, + buffer, copysize); + remain_length -= copysize; + buffer += copysize; + if (stm32_remain.length == sizeof(uint32_t)) { + ret = hash_write_data(stm32_remain.buffer); + if (ret != 0) { + goto exit; + } + + zeromem(&stm32_remain, sizeof(stm32_remain)); + } + } + + while (remain_length / sizeof(uint32_t) != 0U) { + uint32_t tmp_buf; + + memcpy(&tmp_buf, buffer, sizeof(uint32_t)); + ret = hash_write_data(tmp_buf); + if (ret != 0) { + goto exit; + } + + buffer += sizeof(uint32_t); + remain_length -= sizeof(uint32_t); + } + + if (remain_length != 0U) { + assert(stm32_remain.length == 0U); + + memcpy((uint8_t *)&stm32_remain.buffer, buffer, remain_length); + stm32_remain.length = remain_length; + } + +exit: + stm32mp_clk_disable(stm32_hash.clock); + + return ret; +} + +int stm32_hash_final(uint8_t *digest) +{ + int ret; + + stm32mp_clk_enable(stm32_hash.clock); + + if (stm32_remain.length != 0U) { + ret = hash_write_data(stm32_remain.buffer); + if (ret != 0) { + stm32mp_clk_disable(stm32_hash.clock); + return ret; + } + + mmio_clrsetbits_32(hash_base() + HASH_STR, HASH_STR_NBLW_MASK, + 8U * stm32_remain.length); + zeromem(&stm32_remain, sizeof(stm32_remain)); + } + + mmio_setbits_32(hash_base() + HASH_STR, HASH_STR_DCAL); + + ret = hash_get_digest(digest); + + stm32mp_clk_disable(stm32_hash.clock); + + return ret; +} + +int stm32_hash_final_update(const uint8_t *buffer, uint32_t length, + uint8_t *digest) +{ + int ret; + + ret = stm32_hash_update(buffer, length); + if (ret != 0) { + return ret; + } + + return stm32_hash_final(digest); +} + +void stm32_hash_init(enum stm32_hash_algo_mode mode) +{ + stm32mp_clk_enable(stm32_hash.clock); + + hash_hw_init(mode); + + stm32mp_clk_disable(stm32_hash.clock); + + zeromem(&stm32_remain, sizeof(stm32_remain)); +} + +int stm32_hash_register(void) +{ + struct dt_node_info hash_info; + int node; + + for (node = dt_get_node(&hash_info, -1, DT_HASH_COMPAT); + node != -FDT_ERR_NOTFOUND; + node = dt_get_node(&hash_info, node, DT_HASH_COMPAT)) { +#if defined(IMAGE_BL2) + if (hash_info.status != DT_DISABLED) { + break; + } +#else + if (hash_info.status == DT_SECURE) { + break; + } +#endif + } + + if (node == -FDT_ERR_NOTFOUND) { + return -ENODEV; + } + + if (hash_info.clock < 0) { + return -EINVAL; + } + + stm32_hash.base = hash_info.base; + stm32_hash.clock = hash_info.clock; + + stm32mp_clk_enable(stm32_hash.clock); + + if (hash_info.reset >= 0) { + stm32mp_reset_assert((unsigned long)hash_info.reset); + udelay(20); + stm32mp_reset_deassert((unsigned long)hash_info.reset); + } + + stm32mp_clk_disable(stm32_hash.clock); + + return 0; +} diff --git a/drivers/st/io/io_stm32image.c b/drivers/st/io/io_stm32image.c index 971dcce..413521b 100644 --- a/drivers/st/io/io_stm32image.c +++ b/drivers/st/io/io_stm32image.c @@ -246,7 +246,7 @@ static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { - int result = 0; + int result; uint8_t *local_buffer = (uint8_t *)buffer; boot_api_image_header_t *header = (boot_api_image_header_t *)first_lba_buffer; @@ -341,6 +341,12 @@ header->magic = 0; } + result = stm32mp_auth_image(header, buffer); + if (result != 0) { + ERROR("Authentication Failed (%i)\n", result); + return result; + } + io_close(backend_handle); } diff --git a/fdts/stm32mp157c-security.dtsi b/fdts/stm32mp157c-security.dtsi index f7e55b3..165ffa0 100644 --- a/fdts/stm32mp157c-security.dtsi +++ b/fdts/stm32mp157c-security.dtsi @@ -28,6 +28,10 @@ }; }; +&hash1 { + secure-status = "okay"; +}; + &sdmmc1 { compatible = "st,stm32-sdmmc2"; }; diff --git a/include/drivers/st/bsec.h b/include/drivers/st/bsec.h index 2171550..d833e7a 100644 --- a/include/drivers/st/bsec.h +++ b/include/drivers/st/bsec.h @@ -199,7 +199,6 @@ bool bsec_wr_lock(uint32_t otp); uint32_t bsec_otp_lock(uint32_t service, uint32_t value); -bool bsec_mode_is_closed_device(void); uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word); uint32_t bsec_check_nsec_access_rights(uint32_t otp); diff --git a/include/drivers/st/stm32_hash.h b/include/drivers/st/stm32_hash.h new file mode 100644 index 0000000..969d7aa --- /dev/null +++ b/include/drivers/st/stm32_hash.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32_HASH_H +#define STM32_HASH_H + +enum stm32_hash_algo_mode { + HASH_MD5SUM, + HASH_SHA1, + HASH_SHA224, + HASH_SHA256 +}; + +int stm32_hash_update(const uint8_t *buffer, uint32_t length); +int stm32_hash_final(uint8_t *digest); +int stm32_hash_final_update(const uint8_t *buffer, uint32_t buf_length, + uint8_t *digest); +void stm32_hash_init(enum stm32_hash_algo_mode mode); +int stm32_hash_register(void); + +#endif /* STM32_HASH_H */ diff --git a/plat/st/common/include/stm32mp_auth.h b/plat/st/common/include/stm32mp_auth.h new file mode 100644 index 0000000..3075d18 --- /dev/null +++ b/plat/st/common/include/stm32mp_auth.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP_AUTH_H +#define STM32MP_AUTH_H + +struct stm32mp_auth_ops { + uint32_t (*check_key)(uint8_t *pubkey_in, uint8_t *pubkey_out); + uint32_t (*verify_signature)(uint8_t *hash_in, uint8_t *pubkey_in, + uint8_t *signature, uint32_t ecc_algo); +}; + +void stm32mp_init_auth(struct stm32mp_auth_ops *init_ptr); +int stm32mp_auth_image(boot_api_image_header_t *header, uintptr_t buffer); + +#endif /* STM32MP_AUTH_H */ diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h index e20308e..59657fd 100644 --- a/plat/st/common/include/stm32mp_common.h +++ b/plat/st/common/include/stm32mp_common.h @@ -19,6 +19,7 @@ uintptr_t stm32mp_get_boot_ctx_address(void); bool stm32mp_is_single_core(void); +bool stm32mp_is_closed_device(void); /* Return the base address of the DDR controller */ uintptr_t stm32mp_ddrctrl_base(void); diff --git a/plat/st/common/stm32mp_auth.c b/plat/st/common/stm32mp_auth.c new file mode 100644 index 0000000..0ef6d54 --- /dev/null +++ b/plat/st/common/stm32mp_auth.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +static const struct stm32mp_auth_ops *auth_ops; + +void stm32mp_init_auth(struct stm32mp_auth_ops *init_ptr) +{ + if ((init_ptr == NULL) || + (init_ptr->check_key == NULL) || + (init_ptr->verify_signature == NULL) || + (stm32_hash_register() != 0)) { + panic(); + } + + auth_ops = init_ptr; +} + +int stm32mp_auth_image(boot_api_image_header_t *header, uintptr_t buffer) +{ + int ret; + uint8_t image_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES]; + uint32_t header_skip_cksum = sizeof(header->magic) + + sizeof(header->image_signature) + + sizeof(header->payload_checksum); + + /* Check Security Status */ + if (!stm32mp_is_closed_device()) { + if (header->option_flags != 0U) { + WARN("Skip signature check (header option)\n"); + return 0; + } + INFO("Check signature on Open device\n"); + } + + ret = mmap_add_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_BASE, + STM32MP_ROM_SIZE, MT_CODE | MT_SECURE); + if (ret != 0) { + return ret; + } + + /* Check Public Key */ + if (auth_ops->check_key(header->ecc_pubk, NULL) != BOOT_API_RETURN_OK) { + ret = -EINVAL; + goto err; + } + + /* Compute end of header hash and payload hash */ + stm32_hash_init(HASH_SHA256); + + ret = stm32_hash_update((uint8_t *)&header->header_version, + sizeof(boot_api_image_header_t) - + header_skip_cksum); + if (ret != 0) { + ERROR("Hash of header failed, %i\n", ret); + goto err; + } + + ret = stm32_hash_final_update((uint8_t *)buffer, + header->image_length, image_hash); + if (ret != 0) { + ERROR("Hash of payload failed\n"); + goto err; + } + + /* Verify signature */ + if (auth_ops->verify_signature(image_hash, header->ecc_pubk, + header->image_signature, + header->ecc_algo_type) != + BOOT_API_RETURN_OK) { + ret = -EINVAL; + } + +err: + mmap_remove_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_SIZE); + return ret; +} diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index c6aefe3..d9e29b4 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -32,6 +32,7 @@ #include static struct console_stm32 console; +static struct stm32mp_auth_ops stm32mp1_auth_ops; static void print_reset_reason(void) { @@ -284,6 +285,12 @@ stm32mp_print_boardinfo(); + if (boot_context->auth_status != BOOT_API_CTX_AUTH_NO) { + NOTICE("Bootrom authentication %s\n", + (boot_context->auth_status == BOOT_API_CTX_AUTH_FAILED) ? + "failed" : "succeeded"); + } + skip_console_init: if (stm32_iwdg_init() < 0) { panic(); @@ -302,6 +309,12 @@ ERROR("Cannot save boot interface\n"); } + stm32mp1_auth_ops.check_key = boot_context->bootrom_ecdsa_check_key; + stm32mp1_auth_ops.verify_signature = + boot_context->bootrom_ecdsa_verify_signature; + + stm32mp_init_auth(&stm32mp1_auth_ops); + stm32mp1_arch_security_setup(); print_reset_reason(); diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h index c841a74..2284970 100644 --- a/plat/st/stm32mp1/include/boot_api.h +++ b/plat/st/stm32mp1/include/boot_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,16 @@ #include /* + * Possible value of boot context field 'auth_status' + */ +/* No authentication done */ +#define BOOT_API_CTX_AUTH_NO 0x0U +/* Authentication done and failed */ +#define BOOT_API_CTX_AUTH_FAILED 0x1U +/* Authentication done and succeeded */ +#define BOOT_API_CTX_AUTH_SUCCESS 0x2U + +/* * Possible value of boot context field 'boot_interface_sel' */ @@ -114,6 +124,8 @@ /* Closed = OTP_CFG0[6] */ #define BOOT_API_OTP_MODE_CLOSED_BIT_POS 6 +#define BOOT_API_RETURN_OK 0x66U + /* * Boot Context related definitions */ @@ -132,7 +144,27 @@ uint16_t boot_interface_instance; uint32_t reserved1[13]; uint32_t otp_afmux_values[3]; - uint32_t reserved[9]; + uint32_t reserved[5]; + uint32_t auth_status; + + /* + * Pointers to bootROM External Secure Services + * - ECDSA check key + * - ECDSA verify signature + * - ECDSA verify signature and go + */ + uint32_t (*bootrom_ecdsa_check_key)(uint8_t *pubkey_in, + uint8_t *pubkey_out); + uint32_t (*bootrom_ecdsa_verify_signature)(uint8_t *hash_in, + uint8_t *pubkey_in, + uint8_t *signature, + uint32_t ecc_algo); + uint32_t (*bootrom_ecdsa_verify_and_go)(uint8_t *hash_in, + uint8_t *pub_key_in, + uint8_t *signature, + uint32_t ecc_algo, + uint32_t *entry_in); + /* * Information specific to an SD boot * Updated each time an SD boot is at least attempted, diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index 83d9770..90b3e3c 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -71,7 +71,9 @@ BL2_SOURCES += drivers/io/io_block.c \ drivers/io/io_dummy.c \ drivers/io/io_storage.c \ + drivers/st/crypto/stm32_hash.c \ drivers/st/io/io_stm32image.c \ + plat/st/common/stm32mp_auth.c \ plat/st/common/bl2_io_storage.c \ plat/st/stm32mp1/bl2_plat_setup.c @@ -103,6 +105,8 @@ STM32_TF_DTBFILE := ${BUILD_PLAT}/fdts/${DTB_FILE_NAME} STM32_TF_OBJS := ${BUILD_PLAT}/stm32mp1.o +BL2_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 + # Variables for use with stm32image STM32IMAGEPATH ?= tools/stm32image STM32IMAGE ?= ${STM32IMAGEPATH}/stm32image${BIN_EXT} diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index 0eba8a6..a40852b 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -49,6 +50,8 @@ /******************************************************************************* * STM32MP1 memory map related constants ******************************************************************************/ +#define STM32MP_ROM_BASE U(0x00000000) +#define STM32MP_ROM_SIZE U(0x00020000) #define STM32MP_SYSRAM_BASE U(0x2FFC0000) #define STM32MP_SYSRAM_SIZE U(0x00040000) diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c index 38ebcef..e2dcd2a 100644 --- a/plat/st/stm32mp1/stm32mp1_private.c +++ b/plat/st/stm32mp1/stm32mp1_private.c @@ -278,6 +278,19 @@ return ret; } +/* Return true when device is in closed state */ +bool stm32mp_is_closed_device(void) +{ + uint32_t value; + + if ((bsec_shadow_register(DATA0_OTP) != BSEC_OK) || + (bsec_read_otp(&value, DATA0_OTP) != BSEC_OK)) { + return true; + } + + return (value & DATA0_OTP_SECURED) == DATA0_OTP_SECURED; +} + uint32_t stm32_iwdg_get_instance(uintptr_t base) { switch (base) {