Files
SDK_GD32W51x/MBL/source/mbl_image_validate.c
2023-05-18 18:53:00 +08:00

575 lines
20 KiB
C

/*!
\file mbl_image_validate.c
\brief MBL image validate file for GD32W51x WiFi SDK
\version 2021-10-30, V1.0.0, firmware for GD32W51x
*/
/*
Copyright (c) 2021, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "mbl_includes.h"
/*!
\brief find boot image
\param[in] none
\param[out] idx: pointer index of boot image
\param[out] image_offset: pointer to offset of boot image
\retval result of find boot image
\arg 0: find image success
\arg -1: offset of image is null
\arg -2: find image0 fail
\arg -3: find image1 fail
\arg -4: image1 has downloaded but image0 has not downloaded
\arg -5: other fail
\arg -6: read image address from system setting fail
*/
int boot_image_find(OUT uint32_t *idx, OUT uint32_t *image_offset)
{
uint8_t img0_stat = 0, img1_stat = 0;
struct sys_setting_t setting;
uint8_t img0_found = 0, img1_found = 0;
int boot_idx = -1;
int ret, result = 0;
if (NULL == image_offset) {
result = -1;
goto Failed;
}
ret = rom_sys_status_get(SYS_IMAGE0_STATUS, LEN_SYS_IMAGE_STATUS, &img0_stat);
if (ret <= SYS_STATUS_FOUND_ERR) {
result = -2;
goto Failed;
} else if (ret == SYS_STATUS_FOUND_OK) {
img0_found = 1;
} else { // SYS_STATUS_NOT_FOUND
img0_found = 0;
}
ret = rom_sys_status_get(SYS_IMAGE1_STATUS, LEN_SYS_IMAGE_STATUS, &img1_stat);
if (ret <= SYS_STATUS_FOUND_ERR) {
result = -3;
goto Failed;
} else if (ret == SYS_STATUS_FOUND_OK) {
img1_found = 1;
} else { // SYS_STATUS_NOT_FOUND
img1_found = 0;
}
/* Image0 never downloaded, Image1 should not be downloaded too. */
if (!img0_found && img1_found) {
result = -4;
goto Failed;
}
if (!img0_found && !img1_found) {
/* ISP newly downloaded */
rom_sys_set_img_flag(IMAGE_0, IMG_FLAG_NEWER_MASK, IMG_FLAG_NEWER);
boot_idx = 0;
goto ImgSelected;
}
if (img0_found && ((img0_stat & IMG_FLAG_NEWER_MASK) == IMG_FLAG_NEWER)) {
if (((img0_stat & IMG_FLAG_VERIFY_MASK) != IMG_FLAG_VERIFY_FAIL)
&& ((img0_stat & IMG_FLAG_IA_MASK) != IMG_FLAG_IA_FAIL)) {
boot_idx = 0;
goto ImgSelected;
}
}
if (img1_found && ((img1_stat & IMG_FLAG_NEWER_MASK) == IMG_FLAG_NEWER)) {
if (((img1_stat & IMG_FLAG_VERIFY_MASK) != IMG_FLAG_VERIFY_FAIL)
&& ((img1_stat & IMG_FLAG_IA_MASK) != IMG_FLAG_IA_FAIL)) {
boot_idx = 1;
goto ImgSelected;
}
}
if (img0_found && ((img0_stat & IMG_FLAG_VERIFY_MASK) == IMG_FLAG_VERIFY_OK)
&& ((img0_stat & IMG_FLAG_IA_MASK) == IMG_FLAG_IA_OK)) {
boot_idx = 0;
goto ImgSelected;
}
if (img1_found && ((img1_stat & IMG_FLAG_VERIFY_MASK) == IMG_FLAG_VERIFY_OK)
&& ((img1_stat & IMG_FLAG_IA_MASK) == IMG_FLAG_IA_OK)) {
boot_idx = 1;
goto ImgSelected;
}
if (boot_idx == -1) {
result = -5;
goto Failed;
}
ImgSelected:
/* Read Image Address from system setting */
ret = rom_sys_setting_get(&setting);
if (ret != 0) {
result = -6;
goto Failed;
}
*idx = boot_idx;
*image_offset = setting.img0_offset;
return 0;
Failed:
return result;
}
/*!
\brief find mbl image
\param[in] start_offset: flash start offset
\param[in] img_type: image type
\param[out] img_offset: pointer to offset of image
\retval result of find mbl image
\arg 0: find image success
\arg -1: invalid flash start offset
\arg -2: find image header fail
\arg -3: invalid flash offset
*/
int mbl_img_find(IN uint32_t start_offset,
IN uint8_t img_type,
OUT uint32_t *img_offset)
{
struct image_header hdr;
uint32_t flash_offset = start_offset;
uint32_t hdr_offset = 0;
uint32_t chksum;
int ret;
uint32_t flash_sz, erase_size;
if (!rom_is_valid_flash_offset(flash_offset)) {
return -3;
}
flash_sz = rom_flash_total_size();
erase_size = rom_flash_erase_size();
while (flash_offset < flash_sz) {
hdr_offset = flash_offset + RE_VTOR_ALIGNMENT - sizeof(hdr);
ret = rom_flash_read(hdr_offset, (void*)&hdr, sizeof(hdr));
if (ret != 0) {
return -2;
}
if ((hdr.magic_h == IMG_MAGIC_H) && (hdr.img_type == img_type)) {
chksum = rom_cal_checksum((uint8_t *)(&hdr), sizeof(hdr));
if (chksum == 0)
break;
}
flash_offset += erase_size;
}
if (!rom_is_valid_flash_offset(flash_offset)) {
return -1;
} else {
if (img_offset)
*img_offset = hdr_offset;
return 0;
}
}
/*!
\brief find PSA root, application root and non-secure processing environment
\param[in] start_offset: flash start offset
\param[out] prot_offset: pointer to offset of PSA root
\param[out] arot_offset: pointer to offset of application root
\param[out] nspe_offset: pointer to offfset of non-secure processing environment
\retval result of find mbl image
\arg 1: find success
\arg -1: read flash fail while get image header
\arg -2: find arot fail
\arg -3: read flash fail while get arot size
\arg -4: find nspe fail
*/
static int prot_arot_nspe_find(IN uint32_t start_offset,
OUT uint32_t *prot_offset,
OUT uint32_t *arot_offset,
OUT uint32_t *nspe_offset)
{
struct image_header hdr;
uint32_t prot_sz, arot_sz, offset;
int ret;
/* Get image header */
ret = rom_flash_read(start_offset, (void*)&hdr, sizeof(hdr));
if (ret != 0) {
rom_trace_ex(ROM_ERR, "find_arot_nspe: read flash error (%d)\r\n", ret);
return -1;
}
if (hdr.img_type == IMG_TYPE_NSPE) {
*prot_offset = 0;
*arot_offset = 0;
*nspe_offset = start_offset;
return 1;
} else if (hdr.img_type == IMG_TYPE_AROT) {
*prot_offset = 0;
*arot_offset = start_offset;
} else if (hdr.img_type == IMG_TYPE_PROT) {
*prot_offset = start_offset;
prot_sz = hdr.tot_sz;
/* Get AROT address */
offset = *prot_offset + prot_sz;
offset = (((offset >> 12) + 1) << 12);
ret = mbl_img_find(offset, IMG_TYPE_AROT, arot_offset);
if (ret == -1) {
/* Not found, it may be happened. */
*arot_offset = 0;
} else if (ret < -1) {
rom_trace_ex(ROM_ERR, "find_arot_nspe: found AROT error(%d, 0x%x). \r\n", ret, arot_offset);
return -2;
}
}
/* Get AROT size if existed */
if (*arot_offset) {
ret = rom_flash_read(*arot_offset, (void*)&hdr, sizeof(hdr));
if (ret != 0) {
rom_trace_ex(ROM_ERR, "find_arot_nspe: read flash error (%d)\r\n", ret);
return -3;
}
arot_sz = hdr.tot_sz;
offset = *arot_offset + arot_sz;
} else {
offset = (*prot_offset + prot_sz);
arot_sz = 0;
}
/* Get NSPE address */
offset = (((offset >> 12) + 1) << 12);
ret = mbl_img_find(offset, IMG_TYPE_NSPE, nspe_offset);
if (ret < 0) {
rom_trace_ex(ROM_ERR, "find_arot_nspe: not found NSPE(%d, 0x%x). \r\n", ret, *nspe_offset);
*nspe_offset = 0;
return -4;
}
return 1;
}
/*!
\brief check non-secure processing environment header
\param[in] nspe_offset: processing environment header offset
\param[out] none
\retval result of check nspe header
\arg 0: check success
\arg -1: read flash fail while get image header
\arg -2: nspe header check fail
*/
static int nspe_header_check(IN uint32_t nspe_offset)
{
struct image_header hdr;
int ret;
ret = rom_flash_read(nspe_offset, (void*)&hdr, sizeof(hdr));
if (ret != 0) {
rom_trace_ex(ROM_ERR, "nspe_header_check: read flash error (%d)\r\n", ret);
return -1;
}
ret = rom_img_verify_hdr(&hdr, IMG_TYPE_IMG);
if (ret != 0) {
rom_trace_ex(ROM_ERR, "nspe_header_check: nspe header check failed (%d).\r\n", ret);
return -2;
}
return 0;
}
/*!
\brief validate PSA root
\param[in] prot_offset: PSA root offset
\param[in] prot_pk: pointer to PSA root pass key
\param[out] prot_info: pointer to PSA root information
\retval result of validate arot cert, Return non-zero if image could not be validated/does not validate
*/
static int prot_validate(IN uint32_t prot_offset,
IN uint8_t *prot_pk,
OUT struct sw_info_t *prot_info)
{
return rom_img_validate(prot_offset, IMG_TYPE_PROT, prot_pk, prot_info);
}
/*!
\brief validate application root certification
\param[in] arot_offset: application root offset
\param[in] prot_pk: pointer to PSA root pass key
\param[out] arot_pk: pointer to application root pass key
\retval result of validate arot cert, Return non-zero if image could not be validated/does not validate
\arg CERT_ERR_READ_FLASH: read flash fail while get image header
\arg CERT_ERR_BAD_SZ: cert size error
*/
static int arot_cert_validate(IN uint32_t arot_offset,
IN uint8_t *prot_pk,
OUT uint8_t *arot_pk)
{
struct image_header hdr;
uint32_t crt_offset;
int crt_sz, ret;
ret = rom_flash_read(arot_offset, (void*)&hdr, sizeof(hdr));
if (ret != 0) {
rom_trace_ex(ROM_ERR, "arot_cert_validate: read flash error (%d)\r\n", ret);
return CERT_ERR_READ_FLASH;
}
crt_sz = hdr.tot_sz - hdr.hdr_sz - hdr.img_sz - hdr.ptlv_sz;
if (crt_sz <= 0) {
rom_trace_ex(ROM_ERR, "arot_cert_validate: the cert size is zero.\r\n", crt_sz);
return CERT_ERR_BAD_SZ;
}
crt_offset = arot_offset + (hdr.hdr_sz + hdr.img_sz + hdr.ptlv_sz);
return rom_cert_validate(crt_offset, crt_sz, prot_pk, arot_pk);
}
/*!
\brief validate application root
\param[in] arot_offset: application root offset
\param[in] arot_pk: pointer to application root pass key
\param[out] arot_info: pointer to application root information
\retval result of validate arot, Return non-zero if image could not be validated/does not validate
*/
static int arot_validate(IN uint32_t arot_offset,
IN uint8_t *arot_pk,
OUT struct sw_info_t *arot_info)
{
return rom_img_validate(arot_offset, IMG_TYPE_AROT, arot_pk, arot_info);
}
/*!
\brief validate non-secure processing environment certification
\param[in] nspe_offset: non-secure processing environment offset
\param[in] prot_pk: pointer to PSA root pass key
\param[out] nspe_pk: pointer to nspe information
\retval result of validate nspe cert
\arg CERT_ERR_READ_FLASH: read flash fail while get image header
\arg CERT_ERR_BAD_SZ: cert size error
*/
static int nspe_cert_validate(IN uint32_t nspe_offset,
IN uint8_t *prot_pk,
OUT uint8_t *nspe_pk)
{
struct image_header hdr;
uint32_t crt_offset;
int crt_sz, ret;
ret = rom_flash_read(nspe_offset, (void*)&hdr, sizeof(hdr));
if (ret != 0) {
rom_trace_ex(ROM_ERR, "nspe_cert_validate: read flash error (%d)\r\n", ret);
return CERT_ERR_READ_FLASH;
}
crt_sz = hdr.tot_sz - hdr.hdr_sz - hdr.img_sz - hdr.ptlv_sz;
if (crt_sz <= 0) {
rom_trace_ex(ROM_ERR, "nspe_cert_validate: the cert size is zero.\r\n", crt_sz);
return CERT_ERR_BAD_SZ;
}
crt_offset = nspe_offset + (hdr.hdr_sz + hdr.img_sz + hdr.ptlv_sz);
return rom_cert_validate(crt_offset, crt_sz, prot_pk, nspe_pk);
}
/*!
\brief validate non-secure processing environment
\param[in] nspe_offset: non-secure processing environment offset
\param[in] nspe_pk: pointer to nspe information
\param[out] nspe_info: pointer to nspe information
\retval result of validate nspe, Return non-zero if image could not be validated/does not validate
*/
static int nspe_validate(IN uint32_t nspe_offset,
IN uint8_t *nspe_pk,
OUT struct sw_info_t *nspe_info)
{
return rom_img_validate(nspe_offset, IMG_TYPE_NSPE, nspe_pk, nspe_info);
}
/*!
\brief validate image x
\param[in] img_offset: image offset
\param[in] pk: pointer to public key
\param[in] boot_opt: boot option
\param[out] only_nspe: pointer to only npse, there is only npse while *only_npse = 1.
\retval result of validate nspe
\arg 0: validate image success
\arg -1: find prot, arot and nspe address fail
\arg -2: check nspe header fail
\arg -3: verify prot fail
\arg -4: save prot information into mbl status fail
\arg -5: verify arot cert fail
\arg -6: verify arot fail
\arg -7: save arot information into mbl status fail
\arg -8: verify nspe cert fail
\arg -9: verify nspe fail
\arg -10: save nspe into mbl status fail
\arg -11: update main image version counter fail
*/
int image_x_validate(IN uint32_t img_offset,
IN uint8_t *pk,
IN uint8_t boot_opt,
OUT uint32_t *only_nspe)
{
struct sw_info_t sw_info;
uint32_t prot_offset = 0, arot_offset = 0, nspe_offset = 0;
uint8_t arot_pk[IMG_PK_LEN];
int ret, result = 0;
/* Find PROT, AROT and NSPE address */
ret = prot_arot_nspe_find(img_offset, &prot_offset, &arot_offset, &nspe_offset);
if (ret != 1) {
rom_trace_ex(ROM_ERR, "image_x_validate: image found error (ret = %d). \r\n", ret);
result = -1;
goto Failed;
}
if (prot_offset == 0 && arot_offset == 0 && only_nspe) {
*only_nspe = 1;
}
rom_trace_ex(ROM_DBG, "image_x_validate: NSPE offset is 0x%x.\r\n", nspe_offset);
if (boot_opt == IBL_VERIFY_NONE)
return 0;
/* Check if the Main Image Version (equal to NSPE Version) is higher than local NV counter */
ret = nspe_header_check(nspe_offset);
if (ret < 0) {
rom_trace_ex(ROM_ERR, "image_x_validate: the nspe header check failed (ret = %d). \r\n", ret);
result = -2;
goto Failed;
}
/* ============ PROT ============ */
if (prot_offset) {
/* Validate PROT */
memset(&sw_info, 0, sizeof(struct sw_info_t));
ret = prot_validate(prot_offset, pk, &sw_info);
if (ret != 0) {
rom_trace_ex(ROM_ERR, "image_x_validate: PROT verified failed. \r\n");
result = -3;
goto Failed;
} else {
rom_trace_ex(ROM_ALWAYS, "MBL: PROT image verify OK.\r\n");
}
/* Save PROT sw info to MBL status */
ret = mbl_status_sw_info_add(&sw_info);
if (ret != BOOT_STATUS_OK) {
rom_trace_ex(ROM_ERR, "image_x_validate: Failed to save PROT info to MBL status. \r\n");
result = -4;
goto Failed;
}
}
/* ============ AROT ============ */
if (arot_offset) {
/* Validate AROT cert */
if (boot_opt == IBL_VERIFY_CERT_IMG) {
ret = arot_cert_validate(arot_offset, pk, arot_pk);
if (ret != CERT_OK) {
rom_trace_ex(ROM_ERR, "image_x_validate: AROT cert verified failed. \r\n");
result = -5;
goto Failed;
} else {
rom_trace_ex(ROM_ALWAYS, "MBL: AROT cert verify OK.\r\n");
}
} else {
memcpy(arot_pk, pk, sizeof(arot_pk));
}
/* Validate AROT */
memset(&sw_info, 0, sizeof(struct sw_info_t));
ret = arot_validate(arot_offset, arot_pk, &sw_info);
if (ret != 0) {
rom_trace_ex(ROM_ERR, "image_x_validate: AROT verified failed. \r\n");
result = -6;
goto Failed;
} else {
rom_trace_ex(ROM_ALWAYS, "MBL: AROT image verify OK.\r\n");
}
/* Save AROT sw info to MBL status */
ret = mbl_status_sw_info_add(&sw_info);
if (ret != BOOT_STATUS_OK) {
rom_trace_ex(ROM_ERR, "image_x_validate: Failed to save AROT info to MBL status. \r\n");
result = -7;
goto Failed;
}
}
/* ============ NSPE ============ */
if (arot_offset == 0) {
/* Validate NSPE cert */
if (boot_opt == IBL_VERIFY_CERT_IMG) {
ret = nspe_cert_validate(nspe_offset, pk, arot_pk);
if (ret != CERT_OK) {
rom_trace_ex(ROM_ERR, "image_x_validate: NSPE cert verified failed. \r\n");
result = -8;
goto Failed;
} else {
rom_trace_ex(ROM_ALWAYS, "MBL: NSPE cert verify OK.\r\n");
}
} else {
memcpy(arot_pk, pk, sizeof(arot_pk));
}
}
/* Validate NSPE */
memset(&sw_info, 0, sizeof(struct sw_info_t));
ret = nspe_validate(nspe_offset, arot_pk, &sw_info);
if (ret != 0) {
rom_trace_ex(ROM_ERR, "image_x_validate: NSPE verified failed. \r\n");
result = -9;
goto Failed;
} else {
rom_trace_ex(ROM_ALWAYS, "MBL: NSPE image verify OK.\r\n");
}
/* Save NSPE sw info to MBL status */
ret = mbl_status_sw_info_add(&sw_info);
if (ret != BOOT_STATUS_OK) {
rom_trace_ex(ROM_ERR, "image_x_validate: Failed to save NSPE info to MBL status. \r\n");
result = -10;
goto Failed;
}
/* Update Main Image Version Counter */
ret = rom_sys_set_fw_ver(IMG_TYPE_IMG, sw_info.version);
if (ret < 0) {
rom_trace_ex(ROM_ERR, "Update Main Image version counter failed(%d).\r\n", ret);
result = -11;
goto Failed;
} else if (ret == 1){
/* The new image version is equal to the local counter. Do nothing. */
} else if (ret == 0){
rom_trace_ex(ROM_ALWAYS, "MBL: Current image version is %d.%d.%d\r\n",
(sw_info.version >> 24),
((sw_info.version << 8) >> 24),
(sw_info.version & 0xFFFF));
}
return 0;
Failed:
return result;
}