diff options
Diffstat (limited to 'tools')
108 files changed, 14194 insertions, 0 deletions
diff --git a/tools/amlogic/Makefile b/tools/amlogic/Makefile new file mode 100644 index 0000000..1a1d1f8 --- /dev/null +++ b/tools/amlogic/Makefile @@ -0,0 +1,49 @@ +# +# Copyright (C) 2019 Remi Pommarel <repk@triplefau.lt> +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses +# +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk + +PROJECT := doimage${BIN_EXT} +OBJECTS := doimage.o +V := 0 + +HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 -D_GNU_SOURCE + +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG +else + HOSTCCFLAGS += -O2 +endif + +ifeq (${V},0) + Q := @ +else + Q := +endif + +HOSTCC := gcc + +.PHONY: all clean distclean + +all: ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + @echo " HOSTLD $@" + ${Q}${HOSTCC} ${OBJECTS} -o $@ + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} + +%.o: %.c Makefile + @echo " HOSTCC $<" + ${Q}${HOSTCC} -c ${HOSTCCFLAGS} $< -o $@ + +clean: + $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS}) + +distclean: clean diff --git a/tools/amlogic/doimage.c b/tools/amlogic/doimage.c new file mode 100644 index 0000000..b304038 --- /dev/null +++ b/tools/amlogic/doimage.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019, Remi Pommarel <repk@triplefau.lt> + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdint.h> +#include <endian.h> + +#define DEFAULT_PROGNAME "doimage" +#define PROGNAME(argc, argv) (((argc) >= 1) ? ((argv)[0]) : DEFAULT_PROGNAME) + +#define BL31_MAGIC 0x12348765 +#define BL31_LOADADDR 0x05100000 +#define BUFLEN 512 + +static inline void usage(char const *prog) +{ + fprintf(stderr, "Usage: %s <bl31.bin> <bl31.img>\n", prog); +} + +static inline int fdwrite(int fd, uint8_t *data, size_t len) +{ + ssize_t nr; + size_t l; + int ret = -1; + + for (l = 0; l < len; l += nr) { + nr = write(fd, data + l, len - l); + if (nr < 0) { + perror("Cannot write to bl31.img"); + goto out; + } + } + + ret = 0; +out: + return ret; +} + +int main(int argc, char **argv) +{ + int fin, fout, ret = -1; + ssize_t len; + uint32_t data; + uint8_t buf[BUFLEN]; + + if (argc != 3) { + usage(PROGNAME(argc, argv)); + goto out; + } + + fin = open(argv[1], O_RDONLY); + if (fin < 0) { + perror("Cannot open bl31.bin"); + goto out; + } + + fout = open(argv[2], O_WRONLY | O_CREAT, 0660); + if (fout < 0) { + perror("Cannot open bl31.img"); + goto closefin; + } + + data = htole32(BL31_MAGIC); + if (fdwrite(fout, (uint8_t *)&data, sizeof(data)) < 0) + goto closefout; + + lseek(fout, 8, SEEK_SET); + data = htole32(BL31_LOADADDR); + if (fdwrite(fout, (uint8_t *)&data, sizeof(data)) < 0) + goto closefout; + + lseek(fout, 0x200, SEEK_SET); + while ((len = read(fin, buf, sizeof(buf))) > 0) + if (fdwrite(fout, buf, len) < 0) + goto closefout; + if (len < 0) { + perror("Cannot read bl31.bin"); + goto closefout; + } + + ret = 0; + +closefout: + close(fout); +closefin: + close(fin); +out: + return ret; +} diff --git a/tools/cert_create/Makefile b/tools/cert_create/Makefile new file mode 100644 index 0000000..b911d19 --- /dev/null +++ b/tools/cert_create/Makefile @@ -0,0 +1,111 @@ +# +# Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +PLAT := none +V ?= 0 +DEBUG := 0 +CRTTOOL ?= cert_create${BIN_EXT} +BINARY := $(notdir ${CRTTOOL}) +COT := tbbr + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}defaults.mk + +ifneq (${PLAT},none) +TF_PLATFORM_ROOT := ../../plat/ +include ${MAKE_HELPERS_DIRECTORY}plat_helpers.mk +PLAT_CERT_CREATE_HELPER_MK := ${PLAT_DIR}/cert_create_tbbr.mk +endif + +# Common source files. +OBJECTS := src/cert.o \ + src/cmd_opt.o \ + src/ext.o \ + src/key.o \ + src/main.o \ + src/sha.o + +# Chain of trust. +ifeq (${COT},tbbr) + include src/tbbr/tbbr.mk +else ifeq (${COT},dualroot) + include src/dualroot/cot.mk +else ifeq (${COT},cca) + include src/cca/cot.mk +else + $(error Unknown chain of trust ${COT}) +endif + +ifneq (,$(wildcard ${PLAT_CERT_CREATE_HELPER_MK})) +include ${PLAT_CERT_CREATE_HELPER_MK} +endif + +# Select OpenSSL version flag according to the OpenSSL build selected +# from setting the OPENSSL_DIR path. +$(eval $(call SELECT_OPENSSL_API_VERSION)) + +HOSTCCFLAGS := -Wall -std=c99 + +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG -DLOG_LEVEL=40 +else + HOSTCCFLAGS += -O2 -DLOG_LEVEL=20 +endif + +ifeq (${V},0) + Q := @ +else + Q := +endif + +HOSTCCFLAGS += ${DEFINES} +# USING_OPENSSL3 flag will be added to the HOSTCCFLAGS variable with the proper +# computed value. +HOSTCCFLAGS += -DUSING_OPENSSL3=$(USING_OPENSSL3) + +# Make soft links and include from local directory otherwise wrong headers +# could get pulled in from firmware tree. +INC_DIR += -I ./include -I ${PLAT_INCLUDE} -I ${OPENSSL_DIR}/include + +# Include library directories where OpenSSL library files are located. +# For a normal installation (i.e.: when ${OPENSSL_DIR} = /usr or +# /usr/local), binaries are located under the ${OPENSSL_DIR}/lib/ +# directory. However, for a local build of OpenSSL, the built binaries are +# located under the main project directory (i.e.: ${OPENSSL_DIR}, not +# ${OPENSSL_DIR}/lib/). +LIB_DIR := -L ${OPENSSL_DIR}/lib -L ${OPENSSL_DIR} +LIB := -lssl -lcrypto + +HOSTCC ?= gcc + +.PHONY: all clean realclean --openssl + +all: --openssl ${BINARY} + +${BINARY}: ${OBJECTS} Makefile + @echo " HOSTLD $@" + @echo 'const char build_msg[] = "Built : "__TIME__", "__DATE__; \ + const char platform_msg[] = "${PLAT_MSG}";' | \ + ${HOSTCC} -c ${HOSTCCFLAGS} -xc - -o src/build_msg.o + ${Q}${HOSTCC} src/build_msg.o ${OBJECTS} ${LIB_DIR} ${LIB} -o $@ + +%.o: %.c + @echo " HOSTCC $<" + ${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ + +--openssl: +ifeq ($(DEBUG),1) + @echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" +endif + +clean: + $(call SHELL_DELETE_ALL, src/build_msg.o ${OBJECTS}) + +realclean: clean + $(call SHELL_DELETE,${BINARY}) + diff --git a/tools/cert_create/include/cca/cca_cot.h b/tools/cert_create/include/cca/cca_cot.h new file mode 100644 index 0000000..152cb71 --- /dev/null +++ b/tools/cert_create/include/cca/cca_cot.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CCA_COT_H +#define CCA_COT_H + +/* Certificates. */ +enum { + /* Certificates owned by the silicon provider. */ + CCA_CONTENT_CERT, + CORE_SWD_KEY_CERT, + SPMC_CONTENT_CERT, + SIP_SECURE_PARTITION_CONTENT_CERT, + + /* Certificates owned by the platform owner. */ + PLAT_KEY_CERT, + PLAT_SECURE_PARTITION_CONTENT_CERT, + NON_TRUSTED_FW_CONTENT_CERT, +}; + +/* Certificate extensions. */ +enum { + /* Extensions used in certificates owned by the silicon provider. */ + CCA_FW_NVCOUNTER_EXT, + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_BOOT_FW_HASH_EXT, + TRUSTED_BOOT_FW_CONFIG_HASH_EXT, + HW_CONFIG_HASH_EXT, + FW_CONFIG_HASH_EXT, + SWD_ROT_PK_EXT, + CORE_SWD_PK_EXT, + SOC_AP_FW_HASH_EXT, + SOC_FW_CONFIG_HASH_EXT, + RMM_HASH_EXT, + TRUSTED_OS_FW_HASH_EXT, + TRUSTED_OS_FW_CONFIG_HASH_EXT, + SP_PKG1_HASH_EXT, + SP_PKG2_HASH_EXT, + SP_PKG3_HASH_EXT, + SP_PKG4_HASH_EXT, + + /* Extensions used in certificates owned by the platform owner. */ + PROT_PK_EXT, + PLAT_PK_EXT, + SP_PKG5_HASH_EXT, + SP_PKG6_HASH_EXT, + SP_PKG7_HASH_EXT, + SP_PKG8_HASH_EXT, + NON_TRUSTED_FW_NVCOUNTER_EXT, + NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT, + NON_TRUSTED_FW_CONFIG_HASH_EXT, +}; + +/* Keys. */ +enum { + /* Keys owned by the silicon provider. */ + ROT_KEY, + SWD_ROT_KEY, + CORE_SWD_KEY, + + /* Keys owned by the platform owner. */ + PROT_KEY, + PLAT_KEY, +}; + +#endif /* CCA_COT_H */ diff --git a/tools/cert_create/include/cert.h b/tools/cert_create/include/cert.h new file mode 100644 index 0000000..5d39a88 --- /dev/null +++ b/tools/cert_create/include/cert.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CERT_H +#define CERT_H + +#include <openssl/ossl_typ.h> +#include <openssl/x509.h> +#include "ext.h" +#include "key.h" + +#define CERT_MAX_EXT 9 + +/* + * This structure contains information related to the generation of the + * certificates. All these fields must be known and specified at build time + * except for the file name, which is picked up from the command line at + * run time. + * + * One instance of this structure must be created for each of the certificates + * present in the chain of trust. + * + * If the issuer points to this same instance, the generated certificate will + * be self-signed. + */ +typedef struct cert_s cert_t; +struct cert_s { + int id; /* Unique identifier */ + + const char *opt; /* Command line option to pass filename */ + const char *fn; /* Filename to save the certificate */ + const char *cn; /* Subject CN (Company Name) */ + const char *help_msg; /* Help message */ + + /* These fields must be defined statically */ + int key; /* Key to be signed */ + int issuer; /* Issuer certificate */ + int ext[CERT_MAX_EXT]; /* Certificate extensions */ + int num_ext; /* Number of extensions in the certificate */ + + X509 *x; /* X509 certificate container */ +}; + +/* Exported API */ +int cert_init(void); +cert_t *cert_get_by_opt(const char *opt); +int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value); +int cert_new( + int md_alg, + cert_t *cert, + int days, + int ca, + STACK_OF(X509_EXTENSION) * sk); +void cert_cleanup(void); + +/* Macro to register the certificates used in the CoT */ +#define REGISTER_COT(_certs) \ + cert_t *def_certs = &_certs[0]; \ + const unsigned int num_def_certs = sizeof(_certs)/sizeof(_certs[0]) + +/* Macro to register the platform defined certificates used in the CoT */ +#define PLAT_REGISTER_COT(_pdef_certs) \ + cert_t *pdef_certs = &_pdef_certs[0]; \ + const unsigned int num_pdef_certs = sizeof(_pdef_certs)/sizeof(_pdef_certs[0]) + +/* Exported variables */ +extern cert_t *def_certs; +extern const unsigned int num_def_certs; +extern cert_t *pdef_certs; +extern const unsigned int num_pdef_certs; + +extern cert_t *certs; +extern unsigned int num_certs; +#endif /* CERT_H */ diff --git a/tools/cert_create/include/cmd_opt.h b/tools/cert_create/include/cmd_opt.h new file mode 100644 index 0000000..10df00e --- /dev/null +++ b/tools/cert_create/include/cmd_opt.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CMD_OPT_H +#define CMD_OPT_H + +#include <getopt.h> + +#define CMD_OPT_MAX_NUM 64 + +/* Supported long command line option types */ +enum { + CMD_OPT_CERT, + CMD_OPT_KEY, + CMD_OPT_EXT +}; + +/* Structure to define a command line option */ +typedef struct cmd_opt_s { + struct option long_opt; + const char *help_msg; +} cmd_opt_t; + +/* Exported API*/ +void cmd_opt_add(const cmd_opt_t *cmd_opt); +const struct option *cmd_opt_get_array(void); +const char *cmd_opt_get_name(int idx); +const char *cmd_opt_get_help_msg(int idx); + +#endif /* CMD_OPT_H */ diff --git a/tools/cert_create/include/debug.h b/tools/cert_create/include/debug.h new file mode 100644 index 0000000..ee8f1f5 --- /dev/null +++ b/tools/cert_create/include/debug.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include <stdio.h> + +/* The log output macros print output to the console. These macros produce + * compiled log output only if the LOG_LEVEL defined in the makefile (or the + * make command line) is greater or equal than the level required for that + * type of log output. + * The format expected is the same as for printf(). For example: + * INFO("Info %s.\n", "message") -> INFO: Info message. + * WARN("Warning %s.\n", "message") -> WARNING: Warning message. + */ + +#define LOG_LEVEL_NONE 0 +#define LOG_LEVEL_ERROR 10 +#define LOG_LEVEL_NOTICE 20 +#define LOG_LEVEL_WARNING 30 +#define LOG_LEVEL_INFO 40 +#define LOG_LEVEL_VERBOSE 50 + + +#if LOG_LEVEL >= LOG_LEVEL_NOTICE +# define NOTICE(...) printf("NOTICE: " __VA_ARGS__) +#else +# define NOTICE(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_ERROR +# define ERROR(...) printf("ERROR: " __VA_ARGS__) +#else +# define ERROR(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_WARNING +# define WARN(...) printf("WARNING: " __VA_ARGS__) +#else +# define WARN(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_INFO +# define INFO(...) printf("INFO: " __VA_ARGS__) +#else +# define INFO(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE +# define VERBOSE(...) printf("VERBOSE: " __VA_ARGS__) +#else +# define VERBOSE(...) +#endif + +#endif /* DEBUG_H */ diff --git a/tools/cert_create/include/dualroot/cot.h b/tools/cert_create/include/dualroot/cot.h new file mode 100644 index 0000000..3e50c89 --- /dev/null +++ b/tools/cert_create/include/dualroot/cot.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DUALROOT_COT_H +#define DUALROOT_COT_H + +/* Certificates. */ +enum { + /* Certificates owned by the silicon provider. */ + TRUSTED_BOOT_FW_CERT, + TRUSTED_KEY_CERT, + SCP_FW_KEY_CERT, + SCP_FW_CONTENT_CERT, + SOC_FW_KEY_CERT, + SOC_FW_CONTENT_CERT, + TRUSTED_OS_FW_KEY_CERT, + TRUSTED_OS_FW_CONTENT_CERT, + SIP_SECURE_PARTITION_CONTENT_CERT, + FWU_CERT, + + /* Certificates owned by the platform owner. */ + NON_TRUSTED_FW_CONTENT_CERT, + PLAT_SECURE_PARTITION_CONTENT_CERT, +}; + +/* Certificate extensions. */ +enum { + /* Extensions used in certificates owned by the silicon provider. */ + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_BOOT_FW_HASH_EXT, + TRUSTED_BOOT_FW_CONFIG_HASH_EXT, + HW_CONFIG_HASH_EXT, + FW_CONFIG_HASH_EXT, + TRUSTED_WORLD_PK_EXT, + SCP_FW_CONTENT_CERT_PK_EXT, + SCP_FW_HASH_EXT, + SOC_FW_CONTENT_CERT_PK_EXT, + SOC_AP_FW_HASH_EXT, + SOC_FW_CONFIG_HASH_EXT, + TRUSTED_OS_FW_CONTENT_CERT_PK_EXT, + TRUSTED_OS_FW_HASH_EXT, + TRUSTED_OS_FW_EXTRA1_HASH_EXT, + TRUSTED_OS_FW_EXTRA2_HASH_EXT, + TRUSTED_OS_FW_CONFIG_HASH_EXT, + SP_PKG1_HASH_EXT, + SP_PKG2_HASH_EXT, + SP_PKG3_HASH_EXT, + SP_PKG4_HASH_EXT, + SP_PKG5_HASH_EXT, + SP_PKG6_HASH_EXT, + SP_PKG7_HASH_EXT, + SP_PKG8_HASH_EXT, + SCP_FWU_CFG_HASH_EXT, + AP_FWU_CFG_HASH_EXT, + FWU_HASH_EXT, + + /* Extensions used in certificates owned by the platform owner. */ + PROT_PK_EXT, + NON_TRUSTED_FW_NVCOUNTER_EXT, + NON_TRUSTED_FW_CONTENT_CERT_PK_EXT, + NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT, + NON_TRUSTED_FW_CONFIG_HASH_EXT, +}; + +/* Keys. */ +enum { + /* Keys owned by the silicon provider. */ + ROT_KEY, + TRUSTED_WORLD_KEY, + SCP_FW_CONTENT_CERT_KEY, + SOC_FW_CONTENT_CERT_KEY, + TRUSTED_OS_FW_CONTENT_CERT_KEY, + + /* Keys owned by the platform owner. */ + PROT_KEY, +}; + +#endif /* DUALROOT_COT_H */ diff --git a/tools/cert_create/include/ext.h b/tools/cert_create/include/ext.h new file mode 100644 index 0000000..1d55486 --- /dev/null +++ b/tools/cert_create/include/ext.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef EXT_H +#define EXT_H + +#include <openssl/x509v3.h> +#include "key.h" + +/* Extension types supported */ +enum ext_type_e { + EXT_TYPE_NVCOUNTER, + EXT_TYPE_PKEY, + EXT_TYPE_HASH +}; + +/* NV-Counter types */ +enum nvctr_type_e { + NVCTR_TYPE_TFW, + NVCTR_TYPE_NTFW, + NVCTR_TYPE_CCAFW +}; + +/* + * This structure contains the relevant information to create the extensions + * to be included in the certificates. This extensions will be used to + * establish the chain of trust. + */ +typedef struct ext_s { + const char *oid; /* OID of the extension */ + const char *sn; /* Short name */ + const char *ln; /* Long description */ + const char *opt; /* Command line option to specify data */ + const char *help_msg; /* Help message */ + const char *arg; /* Argument passed from command line */ + int asn1_type; /* OpenSSL ASN1 type of the extension data. + * Supported types are: + * - V_ASN1_INTEGER + * - V_ASN1_OCTET_STRING + */ + int type; /* See ext_type_e */ + + /* Extension attributes (depends on extension type) */ + union { + int nvctr_type; /* See nvctr_type_e */ + int key; /* Index into array of registered public keys */ + } attr; + + int alias; /* In case OpenSSL provides an standard + * extension of the same type, add the new + * extension as an alias of this one + */ + + X509V3_EXT_METHOD method; /* This field may be used to define a custom + * function to print the contents of the + * extension */ + + int optional; /* This field may be used optionally to exclude an image */ +} ext_t; + +enum { + EXT_NON_CRIT = 0, + EXT_CRIT = !EXT_NON_CRIT, +}; + +/* Exported API */ +int ext_init(void); +ext_t *ext_get_by_opt(const char *opt); +X509_EXTENSION *ext_new_hash(int nid, int crit, const EVP_MD *md, + unsigned char *buf, size_t len); +X509_EXTENSION *ext_new_nvcounter(int nid, int crit, int value); +X509_EXTENSION *ext_new_key(int nid, int crit, EVP_PKEY *k); +void ext_cleanup(void); + +/* Macro to register the extensions used in the CoT */ +#define REGISTER_EXTENSIONS(_ext) \ + ext_t *def_extensions = &_ext[0]; \ + const unsigned int num_def_extensions = sizeof(_ext)/sizeof(_ext[0]) + +/* Macro to register the platform defined extensions used in the CoT */ +#define PLAT_REGISTER_EXTENSIONS(_pdef_ext) \ + ext_t *pdef_extensions = &_pdef_ext[0]; \ + const unsigned int num_pdef_extensions = sizeof(_pdef_ext)/sizeof(_pdef_ext[0]) + +/* Exported variables */ +extern ext_t *def_extensions; +extern const unsigned int num_def_extensions; +extern ext_t *pdef_extensions; +extern const unsigned int num_pdef_extensions; + +extern ext_t *extensions; +extern unsigned int num_extensions; +#endif /* EXT_H */ diff --git a/tools/cert_create/include/key.h b/tools/cert_create/include/key.h new file mode 100644 index 0000000..e0ecdae --- /dev/null +++ b/tools/cert_create/include/key.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef KEY_H +#define KEY_H + +#include <openssl/ossl_typ.h> + +/* Error codes */ +enum { + KEY_ERR_NONE, + KEY_ERR_MALLOC, + KEY_ERR_FILENAME, + KEY_ERR_OPEN, + KEY_ERR_LOAD +}; + +/* Supported key algorithms */ +enum { + KEY_ALG_RSA, /* RSA PSS as defined by PKCS#1 v2.1 (default) */ +#ifndef OPENSSL_NO_EC + KEY_ALG_ECDSA_NIST, + KEY_ALG_ECDSA_BRAINPOOL_R, + KEY_ALG_ECDSA_BRAINPOOL_T, +#endif /* OPENSSL_NO_EC */ + KEY_ALG_MAX_NUM +}; + +/* Maximum number of valid key sizes per algorithm */ +#define KEY_SIZE_MAX_NUM 4 + +/* Supported hash algorithms */ +enum{ + HASH_ALG_SHA256, + HASH_ALG_SHA384, + HASH_ALG_SHA512, +}; + +/* Supported key sizes */ +/* NOTE: the first item in each array is the default key size */ +static const unsigned int KEY_SIZES[KEY_ALG_MAX_NUM][KEY_SIZE_MAX_NUM] = { + { 2048, 1024, 3072, 4096 }, /* KEY_ALG_RSA */ +#ifndef OPENSSL_NO_EC + { 256, 384 }, /* KEY_ALG_ECDSA_NIST */ + {}, /* KEY_ALG_ECDSA_BRAINPOOL_R */ + {} /* KEY_ALG_ECDSA_BRAINPOOL_T */ +#endif /* OPENSSL_NO_EC */ +}; + +/* + * This structure contains the relevant information to create the keys + * required to sign the certificates. + * + * One instance of this structure must be created for each key, usually in an + * array fashion. The filename is obtained at run time from the command line + * parameters + */ +typedef struct key_s { + int id; /* Key id */ + const char *opt; /* Command line option to specify a key */ + const char *help_msg; /* Help message */ + const char *desc; /* Key description (debug purposes) */ + char *fn; /* Filename to load/store the key */ + EVP_PKEY *key; /* Key container */ +} key_t; + +/* Exported API */ +int key_init(void); +key_t *key_get_by_opt(const char *opt); +#if !USING_OPENSSL3 +int key_new(key_t *key); +#endif +int key_create(key_t *key, int type, int key_bits); +unsigned int key_load(key_t *key); +int key_store(key_t *key); +void key_cleanup(void); + +/* Macro to register the keys used in the CoT */ +#define REGISTER_KEYS(_keys) \ + key_t *def_keys = &_keys[0]; \ + const unsigned int num_def_keys = sizeof(_keys)/sizeof(_keys[0]) + +/* Macro to register the platform defined keys used in the CoT */ +#define PLAT_REGISTER_KEYS(_pdef_keys) \ + key_t *pdef_keys = &_pdef_keys[0]; \ + const unsigned int num_pdef_keys = sizeof(_pdef_keys)/sizeof(_pdef_keys[0]) + +/* Exported variables */ +extern key_t *def_keys; +extern const unsigned int num_def_keys; +extern key_t *pdef_keys; +extern const unsigned int num_pdef_keys; + +extern key_t *keys; +extern unsigned int num_keys; +#endif /* KEY_H */ diff --git a/tools/cert_create/include/sha.h b/tools/cert_create/include/sha.h new file mode 100644 index 0000000..4c55f37 --- /dev/null +++ b/tools/cert_create/include/sha.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SHA_H +#define SHA_H + +int sha_file(int md_alg, const char *filename, unsigned char *md); + +#endif /* SHA_H */ diff --git a/tools/cert_create/include/tbbr/tbb_cert.h b/tools/cert_create/include/tbbr/tbb_cert.h new file mode 100644 index 0000000..e5fa3a2 --- /dev/null +++ b/tools/cert_create/include/tbbr/tbb_cert.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TBB_CERT_H +#define TBB_CERT_H + +#include "cert.h" + +/* + * Enumerate the certificates that are used to establish the chain of trust + */ +enum { + TRUSTED_BOOT_FW_CERT, + TRUSTED_KEY_CERT, + SCP_FW_KEY_CERT, + SCP_FW_CONTENT_CERT, + SOC_FW_KEY_CERT, + SOC_FW_CONTENT_CERT, + TRUSTED_OS_FW_KEY_CERT, + TRUSTED_OS_FW_CONTENT_CERT, + NON_TRUSTED_FW_KEY_CERT, + NON_TRUSTED_FW_CONTENT_CERT, + SIP_SECURE_PARTITION_CONTENT_CERT, + FWU_CERT +}; + +#endif /* TBB_CERT_H */ diff --git a/tools/cert_create/include/tbbr/tbb_ext.h b/tools/cert_create/include/tbbr/tbb_ext.h new file mode 100644 index 0000000..692b2d4 --- /dev/null +++ b/tools/cert_create/include/tbbr/tbb_ext.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef TBB_EXT_H +#define TBB_EXT_H + +#include "ext.h" + +/* TBBR extensions */ +enum { + TRUSTED_FW_NVCOUNTER_EXT, + NON_TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_BOOT_FW_HASH_EXT, + TRUSTED_BOOT_FW_CONFIG_HASH_EXT, + HW_CONFIG_HASH_EXT, + FW_CONFIG_HASH_EXT, + TRUSTED_WORLD_PK_EXT, + NON_TRUSTED_WORLD_PK_EXT, + SCP_FW_CONTENT_CERT_PK_EXT, + SCP_FW_HASH_EXT, + SOC_FW_CONTENT_CERT_PK_EXT, + SOC_AP_FW_HASH_EXT, + SOC_FW_CONFIG_HASH_EXT, + TRUSTED_OS_FW_CONTENT_CERT_PK_EXT, + TRUSTED_OS_FW_HASH_EXT, + TRUSTED_OS_FW_EXTRA1_HASH_EXT, + TRUSTED_OS_FW_EXTRA2_HASH_EXT, + TRUSTED_OS_FW_CONFIG_HASH_EXT, + NON_TRUSTED_FW_CONTENT_CERT_PK_EXT, + NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT, + NON_TRUSTED_FW_CONFIG_HASH_EXT, + SP_PKG1_HASH_EXT, + SP_PKG2_HASH_EXT, + SP_PKG3_HASH_EXT, + SP_PKG4_HASH_EXT, + SP_PKG5_HASH_EXT, + SP_PKG6_HASH_EXT, + SP_PKG7_HASH_EXT, + SP_PKG8_HASH_EXT, + SCP_FWU_CFG_HASH_EXT, + AP_FWU_CFG_HASH_EXT, + FWU_HASH_EXT +}; + +#endif /* TBB_EXT_H */ diff --git a/tools/cert_create/include/tbbr/tbb_key.h b/tools/cert_create/include/tbbr/tbb_key.h new file mode 100644 index 0000000..47ad1de --- /dev/null +++ b/tools/cert_create/include/tbbr/tbb_key.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TBB_KEY_H +#define TBB_KEY_H + +#include "key.h" + +/* + * Enumerate the keys that are used to establish the chain of trust + */ +enum { + ROT_KEY, + TRUSTED_WORLD_KEY, + NON_TRUSTED_WORLD_KEY, + SCP_FW_CONTENT_CERT_KEY, + SOC_FW_CONTENT_CERT_KEY, + TRUSTED_OS_FW_CONTENT_CERT_KEY, + NON_TRUSTED_FW_CONTENT_CERT_KEY +}; + +#endif /* TBB_KEY_H */ diff --git a/tools/cert_create/src/cca/cot.c b/tools/cert_create/src/cca/cot.c new file mode 100644 index 0000000..372d908 --- /dev/null +++ b/tools/cert_create/src/cca/cot.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2022-2023, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "cca/cca_cot.h" + +#include <cca_oid.h> + +#include "cert.h" +#include "ext.h" +#include "key.h" + +/* + * Certificates used in the chain of trust. + * + * All certificates are self-signed so the issuer certificate field points to + * itself. + */ +static cert_t cot_certs[] = { + [CCA_CONTENT_CERT] = { + .id = CCA_CONTENT_CERT, + .opt = "cca-cert", + .help_msg = "CCA Content Certificate (output file)", + .cn = "CCA Content Certificate", + .key = ROT_KEY, + .issuer = CCA_CONTENT_CERT, + .ext = { + CCA_FW_NVCOUNTER_EXT, + SOC_AP_FW_HASH_EXT, + SOC_FW_CONFIG_HASH_EXT, + RMM_HASH_EXT, + TRUSTED_BOOT_FW_HASH_EXT, + TRUSTED_BOOT_FW_CONFIG_HASH_EXT, + HW_CONFIG_HASH_EXT, + FW_CONFIG_HASH_EXT, + }, + .num_ext = 8 + }, + + [CORE_SWD_KEY_CERT] = { + .id = CORE_SWD_KEY_CERT, + .opt = "core-swd-cert", + .help_msg = "Core Secure World Key Certificate (output file)", + .cn = "Core Secure World Key Certificate", + .key = SWD_ROT_KEY, + .issuer = CORE_SWD_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SWD_ROT_PK_EXT, + CORE_SWD_PK_EXT, + }, + .num_ext = 3 + }, + + [SPMC_CONTENT_CERT] = { + .id = SPMC_CONTENT_CERT, + .opt = "tos-fw-cert", + .help_msg = "SPMC Content Certificate (output file)", + .cn = "SPMC Content Certificate", + .key = CORE_SWD_KEY, + .issuer = SPMC_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_OS_FW_HASH_EXT, + TRUSTED_OS_FW_CONFIG_HASH_EXT, + }, + .num_ext = 3 + }, + + [SIP_SECURE_PARTITION_CONTENT_CERT] = { + .id = SIP_SECURE_PARTITION_CONTENT_CERT, + .opt = "sip-sp-cert", + .help_msg = "SiP owned Secure Partition Content Certificate (output file)", + .cn = "SiP owned Secure Partition Content Certificate", + .key = CORE_SWD_KEY, + .issuer = SIP_SECURE_PARTITION_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SP_PKG1_HASH_EXT, + SP_PKG2_HASH_EXT, + SP_PKG3_HASH_EXT, + SP_PKG4_HASH_EXT, + }, + .num_ext = 5 + }, + + [PLAT_KEY_CERT] = { + .id = PLAT_KEY_CERT, + .opt = "plat-key-cert", + .help_msg = "Platform Key Certificate (output file)", + .cn = "Platform Key Certificate", + .key = PROT_KEY, + .issuer = PLAT_KEY_CERT, + .ext = { + NON_TRUSTED_FW_NVCOUNTER_EXT, + PROT_PK_EXT, + PLAT_PK_EXT, + }, + .num_ext = 3 + }, + + [PLAT_SECURE_PARTITION_CONTENT_CERT] = { + .id = PLAT_SECURE_PARTITION_CONTENT_CERT, + .opt = "plat-sp-cert", + .help_msg = "Platform owned Secure Partition Content Certificate (output file)", + .cn = "Platform owned Secure Partition Content Certificate", + .key = PLAT_KEY, + .issuer = PLAT_SECURE_PARTITION_CONTENT_CERT, + .ext = { + NON_TRUSTED_FW_NVCOUNTER_EXT, + SP_PKG5_HASH_EXT, + SP_PKG6_HASH_EXT, + SP_PKG7_HASH_EXT, + SP_PKG8_HASH_EXT, + }, + .num_ext = 5 + }, + + [NON_TRUSTED_FW_CONTENT_CERT] = { + .id = NON_TRUSTED_FW_CONTENT_CERT, + .opt = "nt-fw-cert", + .help_msg = "Non-Trusted Firmware Content Certificate (output file)", + .cn = "Non-Trusted Firmware Content Certificate", + .key = PLAT_KEY, + .issuer = NON_TRUSTED_FW_CONTENT_CERT, + .ext = { + NON_TRUSTED_FW_NVCOUNTER_EXT, + NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT, + NON_TRUSTED_FW_CONFIG_HASH_EXT, + }, + .num_ext = 3 + }, +}; + +REGISTER_COT(cot_certs); + + +/* Certificate extensions. */ +static ext_t cot_ext[] = { + [CCA_FW_NVCOUNTER_EXT] = { + .oid = CCA_FW_NVCOUNTER_OID, + .opt = "ccafw-nvctr", + .help_msg = "CCA Firmware Non-Volatile counter value", + .sn = "CCANVCounter", + .ln = "CCA Non-Volatile counter", + .asn1_type = V_ASN1_INTEGER, + .type = EXT_TYPE_NVCOUNTER, + .attr.nvctr_type = NVCTR_TYPE_CCAFW + }, + + [TRUSTED_FW_NVCOUNTER_EXT] = { + .oid = TRUSTED_FW_NVCOUNTER_OID, + .opt = "tfw-nvctr", + .help_msg = "Trusted Firmware Non-Volatile counter value", + .sn = "TrustedWorldNVCounter", + .ln = "Trusted World Non-Volatile counter", + .asn1_type = V_ASN1_INTEGER, + .type = EXT_TYPE_NVCOUNTER, + .attr.nvctr_type = NVCTR_TYPE_TFW + }, + + [TRUSTED_BOOT_FW_HASH_EXT] = { + .oid = TRUSTED_BOOT_FW_HASH_OID, + .opt = "tb-fw", + .help_msg = "Trusted Boot Firmware image file", + .sn = "TrustedBootFirmwareHash", + .ln = "Trusted Boot Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [TRUSTED_BOOT_FW_CONFIG_HASH_EXT] = { + .oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID, + .opt = "tb-fw-config", + .help_msg = "Trusted Boot Firmware Config file", + .sn = "TrustedBootFirmwareConfigHash", + .ln = "Trusted Boot Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [HW_CONFIG_HASH_EXT] = { + .oid = HW_CONFIG_HASH_OID, + .opt = "hw-config", + .help_msg = "HW Config file", + .sn = "HWConfigHash", + .ln = "HW Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [FW_CONFIG_HASH_EXT] = { + .oid = FW_CONFIG_HASH_OID, + .opt = "fw-config", + .help_msg = "Firmware Config file", + .sn = "FirmwareConfigHash", + .ln = "Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [SWD_ROT_PK_EXT] = { + .oid = SWD_ROT_PK_OID, + .sn = "SWDRoTKey", + .ln = "Secure World Root of Trust Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = SWD_ROT_KEY + }, + + [CORE_SWD_PK_EXT] = { + .oid = CORE_SWD_PK_OID, + .sn = "CORESWDKey", + .ln = "Core Secure World Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = CORE_SWD_KEY + }, + + [SOC_AP_FW_HASH_EXT] = { + .oid = SOC_AP_FW_HASH_OID, + .opt = "soc-fw", + .help_msg = "SoC AP Firmware image file", + .sn = "SoCAPFirmwareHash", + .ln = "SoC AP Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [SOC_FW_CONFIG_HASH_EXT] = { + .oid = SOC_FW_CONFIG_HASH_OID, + .opt = "soc-fw-config", + .help_msg = "SoC Firmware Config file", + .sn = "SocFirmwareConfigHash", + .ln = "SoC Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [RMM_HASH_EXT] = { + .oid = RMM_HASH_OID, + .opt = "rmm-fw", + .help_msg = "RMM Firmware image file", + .sn = "RMMFirmwareHash", + .ln = "RMM Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [TRUSTED_OS_FW_HASH_EXT] = { + .oid = TRUSTED_OS_FW_HASH_OID, + .opt = "tos-fw", + .help_msg = "Trusted OS image file", + .sn = "TrustedOSHash", + .ln = "Trusted OS hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [TRUSTED_OS_FW_CONFIG_HASH_EXT] = { + .oid = TRUSTED_OS_FW_CONFIG_HASH_OID, + .opt = "tos-fw-config", + .help_msg = "Trusted OS Firmware Config file", + .sn = "TrustedOSFirmwareConfigHash", + .ln = "Trusted OS Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [SP_PKG1_HASH_EXT] = { + .oid = SP_PKG1_HASH_OID, + .opt = "sp-pkg1", + .help_msg = "Secure Partition Package1 file", + .sn = "SPPkg1Hash", + .ln = "SP Pkg1 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG2_HASH_EXT] = { + .oid = SP_PKG2_HASH_OID, + .opt = "sp-pkg2", + .help_msg = "Secure Partition Package2 file", + .sn = "SPPkg2Hash", + .ln = "SP Pkg2 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG3_HASH_EXT] = { + .oid = SP_PKG3_HASH_OID, + .opt = "sp-pkg3", + .help_msg = "Secure Partition Package3 file", + .sn = "SPPkg3Hash", + .ln = "SP Pkg3 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG4_HASH_EXT] = { + .oid = SP_PKG4_HASH_OID, + .opt = "sp-pkg4", + .help_msg = "Secure Partition Package4 file", + .sn = "SPPkg4Hash", + .ln = "SP Pkg4 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [PROT_PK_EXT] = { + .oid = PROT_PK_OID, + .sn = "PlatformRoTKey", + .ln = "Platform Root of Trust Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = PROT_KEY + }, + + [PLAT_PK_EXT] = { + .oid = PLAT_PK_OID, + .sn = "PLATKey", + .ln = "Platform Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = PLAT_KEY + }, + + [SP_PKG5_HASH_EXT] = { + .oid = SP_PKG5_HASH_OID, + .opt = "sp-pkg5", + .help_msg = "Secure Partition Package5 file", + .sn = "SPPkg5Hash", + .ln = "SP Pkg5 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG6_HASH_EXT] = { + .oid = SP_PKG6_HASH_OID, + .opt = "sp-pkg6", + .help_msg = "Secure Partition Package6 file", + .sn = "SPPkg6Hash", + .ln = "SP Pkg6 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG7_HASH_EXT] = { + .oid = SP_PKG7_HASH_OID, + .opt = "sp-pkg7", + .help_msg = "Secure Partition Package7 file", + .sn = "SPPkg7Hash", + .ln = "SP Pkg7 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG8_HASH_EXT] = { + .oid = SP_PKG8_HASH_OID, + .opt = "sp-pkg8", + .help_msg = "Secure Partition Package8 file", + .sn = "SPPkg8Hash", + .ln = "SP Pkg8 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [NON_TRUSTED_FW_NVCOUNTER_EXT] = { + .oid = NON_TRUSTED_FW_NVCOUNTER_OID, + .opt = "ntfw-nvctr", + .help_msg = "Non-Trusted Firmware Non-Volatile counter value", + .sn = "NormalWorldNVCounter", + .ln = "Non-Trusted Firmware Non-Volatile counter", + .asn1_type = V_ASN1_INTEGER, + .type = EXT_TYPE_NVCOUNTER, + .attr.nvctr_type = NVCTR_TYPE_NTFW + }, + + [NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT] = { + .oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID, + .opt = "nt-fw", + .help_msg = "Non-Trusted World Bootloader image file", + .sn = "NonTrustedWorldBootloaderHash", + .ln = "Non-Trusted World hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [NON_TRUSTED_FW_CONFIG_HASH_EXT] = { + .oid = NON_TRUSTED_FW_CONFIG_HASH_OID, + .opt = "nt-fw-config", + .help_msg = "Non Trusted OS Firmware Config file", + .sn = "NonTrustedOSFirmwareConfigHash", + .ln = "Non-Trusted OS Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, +}; + +REGISTER_EXTENSIONS(cot_ext); + +/* Keys used to establish the chain of trust. */ +static key_t cot_keys[] = { + [ROT_KEY] = { + .id = ROT_KEY, + .opt = "rot-key", + .help_msg = "Root Of Trust key file or PKCS11 URI", + .desc = "Root Of Trust key" + }, + + [SWD_ROT_KEY] = { + .id = SWD_ROT_KEY, + .opt = "swd-rot-key", + .help_msg = "Secure World Root of Trust key file or PKCS11 URI", + .desc = "Secure World Root of Trust key" + }, + + [CORE_SWD_KEY] = { + .id = CORE_SWD_KEY, + .opt = "core-swd-key", + .help_msg = "Core Secure World key file or PKCS11 URI", + .desc = "Core Secure World key" + }, + + [PROT_KEY] = { + .id = PROT_KEY, + .opt = "prot-key", + .help_msg = "Platform Root of Trust key file or PKCS11 URI", + .desc = "Platform Root of Trust key" + }, + + [PLAT_KEY] = { + .id = PLAT_KEY, + .opt = "plat-key", + .help_msg = "Platform key file or PKCS11 URI", + .desc = "Platform key" + }, +}; + +REGISTER_KEYS(cot_keys); diff --git a/tools/cert_create/src/cca/cot.mk b/tools/cert_create/src/cca/cot.mk new file mode 100644 index 0000000..d0c80bb --- /dev/null +++ b/tools/cert_create/src/cca/cot.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +PLAT_MSG := Confidential Compute Architecture root of trust +PLAT_INCLUDE := ../../include/tools_share + +OBJECTS += src/cca/cot.o diff --git a/tools/cert_create/src/cert.c b/tools/cert_create/src/cert.c new file mode 100644 index 0000000..2513213 --- /dev/null +++ b/tools/cert_create/src/cert.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/conf.h> +#include <openssl/err.h> +#include <openssl/opensslv.h> +#include <openssl/pem.h> +#include <openssl/sha.h> +#include <openssl/x509v3.h> + +#include "cert.h" +#include "cmd_opt.h" +#include "debug.h" +#include "key.h" +#include "sha.h" + +#define SERIAL_RAND_BITS 64 +#define RSA_SALT_LEN 32 + +cert_t *certs; +unsigned int num_certs; + +int rand_serial(BIGNUM *b, ASN1_INTEGER *ai) +{ + BIGNUM *btmp; + int ret = 0; + if (b) + btmp = b; + else + btmp = BN_new(); + + if (!btmp) + return 0; + +#if USING_OPENSSL3 + if (!BN_rand(btmp, SERIAL_RAND_BITS, 0, 0)) +#else + if (!BN_pseudo_rand(btmp, SERIAL_RAND_BITS, 0, 0)) +#endif + goto error; + if (ai && !BN_to_ASN1_INTEGER(btmp, ai)) + goto error; + + ret = 1; + +error: + + if (!b) + BN_free(btmp); + + return ret; +} +const EVP_MD *get_digest(int alg) +{ + switch (alg) { + case HASH_ALG_SHA256: + return EVP_sha256(); + case HASH_ALG_SHA384: + return EVP_sha384(); + case HASH_ALG_SHA512: + return EVP_sha512(); + default: + return NULL; + } +} + +int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value) +{ + X509_EXTENSION *ex; + X509V3_CTX ctx; + + /* No configuration database */ + X509V3_set_ctx_nodb(&ctx); + + /* Set issuer and subject certificates in the context */ + X509V3_set_ctx(&ctx, issuer, subject, NULL, NULL, 0); + ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); + if (!ex) { + ERR_print_errors_fp(stdout); + return 0; + } + + X509_add_ext(subject, ex, -1); + X509_EXTENSION_free(ex); + + return 1; +} + +int cert_new( + int md_alg, + cert_t *cert, + int days, + int ca, + STACK_OF(X509_EXTENSION) * sk) +{ + EVP_PKEY *pkey = keys[cert->key].key; + cert_t *issuer_cert = &certs[cert->issuer]; + EVP_PKEY *ikey = keys[issuer_cert->key].key; + X509 *issuer = issuer_cert->x; + X509 *x; + X509_EXTENSION *ex; + X509_NAME *name; + ASN1_INTEGER *sno; + int i, num, rc = 0; + EVP_MD_CTX *mdCtx; + EVP_PKEY_CTX *pKeyCtx = NULL; + + /* Create the certificate structure */ + x = X509_new(); + if (!x) { + return 0; + } + + /* If we do not have a key, use the issuer key (the certificate will + * become self signed). This happens in content certificates. */ + if (!pkey) { + pkey = ikey; + } + + /* If we do not have an issuer certificate, use our own (the certificate + * will become self signed) */ + if (!issuer) { + issuer = x; + } + + mdCtx = EVP_MD_CTX_create(); + if (mdCtx == NULL) { + ERR_print_errors_fp(stdout); + goto END; + } + + /* Sign the certificate with the issuer key */ + if (!EVP_DigestSignInit(mdCtx, &pKeyCtx, get_digest(md_alg), NULL, ikey)) { + ERR_print_errors_fp(stdout); + goto END; + } + + /* + * Set additional parameters if issuing public key algorithm is RSA. + * This is not required for ECDSA. + */ + if (EVP_PKEY_base_id(ikey) == EVP_PKEY_RSA) { + if (!EVP_PKEY_CTX_set_rsa_padding(pKeyCtx, RSA_PKCS1_PSS_PADDING)) { + ERR_print_errors_fp(stdout); + goto END; + } + + if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(pKeyCtx, RSA_SALT_LEN)) { + ERR_print_errors_fp(stdout); + goto END; + } + + if (!EVP_PKEY_CTX_set_rsa_mgf1_md(pKeyCtx, get_digest(md_alg))) { + ERR_print_errors_fp(stdout); + goto END; + } + } + + /* x509.v3 */ + X509_set_version(x, 2); + + /* Random serial number */ + sno = ASN1_INTEGER_new(); + rand_serial(NULL, sno); + X509_set_serialNumber(x, sno); + ASN1_INTEGER_free(sno); + + X509_gmtime_adj(X509_get_notBefore(x), 0); + X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*days); + X509_set_pubkey(x, pkey); + + /* Subject name */ + name = X509_get_subject_name(x); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (const unsigned char *)cert->cn, -1, -1, 0); + X509_set_subject_name(x, name); + + /* Issuer name */ + name = X509_get_issuer_name(x); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (const unsigned char *)issuer_cert->cn, -1, -1, 0); + X509_set_issuer_name(x, name); + + /* Add various extensions: standard extensions */ + cert_add_ext(issuer, x, NID_subject_key_identifier, "hash"); + cert_add_ext(issuer, x, NID_authority_key_identifier, "keyid:always"); + if (ca) { + cert_add_ext(issuer, x, NID_basic_constraints, "CA:TRUE"); + cert_add_ext(issuer, x, NID_key_usage, "keyCertSign"); + } else { + cert_add_ext(issuer, x, NID_basic_constraints, "CA:FALSE"); + } + + /* Add custom extensions */ + if (sk != NULL) { + num = sk_X509_EXTENSION_num(sk); + for (i = 0; i < num; i++) { + ex = sk_X509_EXTENSION_value(sk, i); + X509_add_ext(x, ex, -1); + } + } + + if (!X509_sign_ctx(x, mdCtx)) { + ERR_print_errors_fp(stdout); + goto END; + } + + /* X509 certificate signed successfully */ + rc = 1; + cert->x = x; + +END: + EVP_MD_CTX_destroy(mdCtx); + return rc; +} + +int cert_init(void) +{ + cmd_opt_t cmd_opt; + cert_t *cert; + unsigned int i; + + certs = malloc((num_def_certs * sizeof(def_certs[0])) +#ifdef PDEF_CERTS + + (num_pdef_certs * sizeof(pdef_certs[0])) +#endif + ); + if (certs == NULL) { + ERROR("%s:%d Failed to allocate memory.\n", __func__, __LINE__); + return 1; + } + + memcpy(&certs[0], &def_certs[0], + (num_def_certs * sizeof(def_certs[0]))); + +#ifdef PDEF_CERTS + memcpy(&certs[num_def_certs], &pdef_certs[0], + (num_pdef_certs * sizeof(pdef_certs[0]))); + + num_certs = num_def_certs + num_pdef_certs; +#else + num_certs = num_def_certs; +#endif + + for (i = 0; i < num_certs; i++) { + cert = &certs[i]; + cmd_opt.long_opt.name = cert->opt; + cmd_opt.long_opt.has_arg = required_argument; + cmd_opt.long_opt.flag = NULL; + cmd_opt.long_opt.val = CMD_OPT_CERT; + cmd_opt.help_msg = cert->help_msg; + cmd_opt_add(&cmd_opt); + } + + return 0; +} + +cert_t *cert_get_by_opt(const char *opt) +{ + cert_t *cert; + unsigned int i; + + for (i = 0; i < num_certs; i++) { + cert = &certs[i]; + if (0 == strcmp(cert->opt, opt)) { + return cert; + } + } + + return NULL; +} + +void cert_cleanup(void) +{ + unsigned int i; + + for (i = 0; i < num_certs; i++) { + if (certs[i].fn != NULL) { + void *ptr = (void *)certs[i].fn; + + certs[i].fn = NULL; + free(ptr); + } + } + free(certs); +} + diff --git a/tools/cert_create/src/cmd_opt.c b/tools/cert_create/src/cmd_opt.c new file mode 100644 index 0000000..64180d1 --- /dev/null +++ b/tools/cert_create/src/cmd_opt.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <cmd_opt.h> +#include <getopt.h> +#include <stddef.h> +#include <stdlib.h> +#include "debug.h" + +/* Command line options */ +static struct option long_opt[CMD_OPT_MAX_NUM+1]; +static const char *help_msg[CMD_OPT_MAX_NUM+1]; +static int num_reg_opt; + +void cmd_opt_add(const cmd_opt_t *cmd_opt) +{ + assert(cmd_opt != NULL); + + if (num_reg_opt >= CMD_OPT_MAX_NUM) { + ERROR("Out of memory. Please increase CMD_OPT_MAX_NUM\n"); + exit(1); + } + + long_opt[num_reg_opt].name = cmd_opt->long_opt.name; + long_opt[num_reg_opt].has_arg = cmd_opt->long_opt.has_arg; + long_opt[num_reg_opt].flag = 0; + long_opt[num_reg_opt].val = cmd_opt->long_opt.val; + + help_msg[num_reg_opt] = cmd_opt->help_msg; + + num_reg_opt++; +} + +const struct option *cmd_opt_get_array(void) +{ + return long_opt; +} + +const char *cmd_opt_get_name(int idx) +{ + if (idx >= num_reg_opt) { + return NULL; + } + + return long_opt[idx].name; +} + +const char *cmd_opt_get_help_msg(int idx) +{ + if (idx >= num_reg_opt) { + return NULL; + } + + return help_msg[idx]; +} diff --git a/tools/cert_create/src/dualroot/cot.c b/tools/cert_create/src/dualroot/cot.c new file mode 100644 index 0000000..81a7d75 --- /dev/null +++ b/tools/cert_create/src/dualroot/cot.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2020, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <dualroot_oid.h> + +#include "cert.h" +#include "ext.h" +#include "key.h" + +#include "dualroot/cot.h" + +/* + * Certificates used in the chain of trust. + * + * All certificates are self-signed so the issuer certificate field points to + * itself. + */ +static cert_t cot_certs[] = { + [TRUSTED_BOOT_FW_CERT] = { + .id = TRUSTED_BOOT_FW_CERT, + .opt = "tb-fw-cert", + .help_msg = "Trusted Boot FW Certificate (output file)", + .cn = "Trusted Boot FW Certificate", + .key = ROT_KEY, + .issuer = TRUSTED_BOOT_FW_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_BOOT_FW_HASH_EXT, + TRUSTED_BOOT_FW_CONFIG_HASH_EXT, + HW_CONFIG_HASH_EXT, + FW_CONFIG_HASH_EXT + }, + .num_ext = 5 + }, + + [TRUSTED_KEY_CERT] = { + .id = TRUSTED_KEY_CERT, + .opt = "trusted-key-cert", + .help_msg = "Trusted Key Certificate (output file)", + .cn = "Trusted Key Certificate", + .key = ROT_KEY, + .issuer = TRUSTED_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_WORLD_PK_EXT, + }, + .num_ext = 2 + }, + + [SCP_FW_KEY_CERT] = { + .id = SCP_FW_KEY_CERT, + .opt = "scp-fw-key-cert", + .help_msg = "SCP Firmware Key Certificate (output file)", + .cn = "SCP Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = SCP_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SCP_FW_CONTENT_CERT_PK_EXT + }, + .num_ext = 2 + }, + + [SCP_FW_CONTENT_CERT] = { + .id = SCP_FW_CONTENT_CERT, + .opt = "scp-fw-cert", + .help_msg = "SCP Firmware Content Certificate (output file)", + .cn = "SCP Firmware Content Certificate", + .key = SCP_FW_CONTENT_CERT_KEY, + .issuer = SCP_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SCP_FW_HASH_EXT + }, + .num_ext = 2 + }, + + [SOC_FW_KEY_CERT] = { + .id = SOC_FW_KEY_CERT, + .opt = "soc-fw-key-cert", + .help_msg = "SoC Firmware Key Certificate (output file)", + .cn = "SoC Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = SOC_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SOC_FW_CONTENT_CERT_PK_EXT + }, + .num_ext = 2 + }, + + [SOC_FW_CONTENT_CERT] = { + .id = SOC_FW_CONTENT_CERT, + .opt = "soc-fw-cert", + .help_msg = "SoC Firmware Content Certificate (output file)", + .cn = "SoC Firmware Content Certificate", + .key = SOC_FW_CONTENT_CERT_KEY, + .issuer = SOC_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SOC_AP_FW_HASH_EXT, + SOC_FW_CONFIG_HASH_EXT, + }, + .num_ext = 3 + }, + + [TRUSTED_OS_FW_KEY_CERT] = { + .id = TRUSTED_OS_FW_KEY_CERT, + .opt = "tos-fw-key-cert", + .help_msg = "Trusted OS Firmware Key Certificate (output file)", + .cn = "Trusted OS Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = TRUSTED_OS_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_OS_FW_CONTENT_CERT_PK_EXT + }, + .num_ext = 2 + }, + + [TRUSTED_OS_FW_CONTENT_CERT] = { + .id = TRUSTED_OS_FW_CONTENT_CERT, + .opt = "tos-fw-cert", + .help_msg = "Trusted OS Firmware Content Certificate (output file)", + .cn = "Trusted OS Firmware Content Certificate", + .key = TRUSTED_OS_FW_CONTENT_CERT_KEY, + .issuer = TRUSTED_OS_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_OS_FW_HASH_EXT, + TRUSTED_OS_FW_EXTRA1_HASH_EXT, + TRUSTED_OS_FW_EXTRA2_HASH_EXT, + TRUSTED_OS_FW_CONFIG_HASH_EXT, + }, + .num_ext = 5 + }, + + [SIP_SECURE_PARTITION_CONTENT_CERT] = { + .id = SIP_SECURE_PARTITION_CONTENT_CERT, + .opt = "sip-sp-cert", + .help_msg = "SiP owned Secure Partition Content Certificate (output file)", + .fn = NULL, + .cn = "SiP owned Secure Partition Content Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = SIP_SECURE_PARTITION_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SP_PKG1_HASH_EXT, + SP_PKG2_HASH_EXT, + SP_PKG3_HASH_EXT, + SP_PKG4_HASH_EXT, + }, + .num_ext = 5 + }, + + [PLAT_SECURE_PARTITION_CONTENT_CERT] = { + .id = PLAT_SECURE_PARTITION_CONTENT_CERT, + .opt = "plat-sp-cert", + .help_msg = "Platform owned Secure Partition Content Certificate (output file)", + .fn = NULL, + .cn = "Platform owned Secure Partition Content Certificate", + .key = PROT_KEY, + .issuer = PLAT_SECURE_PARTITION_CONTENT_CERT, + .ext = { + NON_TRUSTED_FW_NVCOUNTER_EXT, + SP_PKG5_HASH_EXT, + SP_PKG6_HASH_EXT, + SP_PKG7_HASH_EXT, + SP_PKG8_HASH_EXT, + PROT_PK_EXT, + }, + .num_ext = 6 + }, + + [FWU_CERT] = { + .id = FWU_CERT, + .opt = "fwu-cert", + .help_msg = "Firmware Update Certificate (output file)", + .cn = "Firmware Update Certificate", + .key = ROT_KEY, + .issuer = FWU_CERT, + .ext = { + SCP_FWU_CFG_HASH_EXT, + AP_FWU_CFG_HASH_EXT, + FWU_HASH_EXT + }, + .num_ext = 3 + }, + + [NON_TRUSTED_FW_CONTENT_CERT] = { + .id = NON_TRUSTED_FW_CONTENT_CERT, + .opt = "nt-fw-cert", + .help_msg = "Non-Trusted Firmware Content Certificate (output file)", + .cn = "Non-Trusted Firmware Content Certificate", + .key = PROT_KEY, + .issuer = NON_TRUSTED_FW_CONTENT_CERT, + .ext = { + NON_TRUSTED_FW_NVCOUNTER_EXT, + NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT, + NON_TRUSTED_FW_CONFIG_HASH_EXT, + PROT_PK_EXT, + }, + .num_ext = 4 + }, +}; + +REGISTER_COT(cot_certs); + + +/* Certificate extensions. */ +static ext_t cot_ext[] = { + [TRUSTED_FW_NVCOUNTER_EXT] = { + .oid = TRUSTED_FW_NVCOUNTER_OID, + .opt = "tfw-nvctr", + .help_msg = "Trusted Firmware Non-Volatile counter value", + .sn = "TrustedWorldNVCounter", + .ln = "Trusted World Non-Volatile counter", + .asn1_type = V_ASN1_INTEGER, + .type = EXT_TYPE_NVCOUNTER, + .attr.nvctr_type = NVCTR_TYPE_TFW + }, + + [TRUSTED_BOOT_FW_HASH_EXT] = { + .oid = TRUSTED_BOOT_FW_HASH_OID, + .opt = "tb-fw", + .help_msg = "Trusted Boot Firmware image file", + .sn = "TrustedBootFirmwareHash", + .ln = "Trusted Boot Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [TRUSTED_BOOT_FW_CONFIG_HASH_EXT] = { + .oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID, + .opt = "tb-fw-config", + .help_msg = "Trusted Boot Firmware Config file", + .sn = "TrustedBootFirmwareConfigHash", + .ln = "Trusted Boot Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [HW_CONFIG_HASH_EXT] = { + .oid = HW_CONFIG_HASH_OID, + .opt = "hw-config", + .help_msg = "HW Config file", + .sn = "HWConfigHash", + .ln = "HW Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [FW_CONFIG_HASH_EXT] = { + .oid = FW_CONFIG_HASH_OID, + .opt = "fw-config", + .help_msg = "Firmware Config file", + .sn = "FirmwareConfigHash", + .ln = "Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [TRUSTED_WORLD_PK_EXT] = { + .oid = TRUSTED_WORLD_PK_OID, + .sn = "TrustedWorldPublicKey", + .ln = "Trusted World Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = TRUSTED_WORLD_KEY + }, + + [SCP_FW_CONTENT_CERT_PK_EXT] = { + .oid = SCP_FW_CONTENT_CERT_PK_OID, + .sn = "SCPFirmwareContentCertPK", + .ln = "SCP Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = SCP_FW_CONTENT_CERT_KEY + }, + + [SCP_FW_HASH_EXT] = { + .oid = SCP_FW_HASH_OID, + .opt = "scp-fw", + .help_msg = "SCP Firmware image file", + .sn = "SCPFirmwareHash", + .ln = "SCP Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [SOC_FW_CONTENT_CERT_PK_EXT] = { + .oid = SOC_FW_CONTENT_CERT_PK_OID, + .sn = "SoCFirmwareContentCertPK", + .ln = "SoC Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = SOC_FW_CONTENT_CERT_KEY + }, + + [SOC_AP_FW_HASH_EXT] = { + .oid = SOC_AP_FW_HASH_OID, + .opt = "soc-fw", + .help_msg = "SoC AP Firmware image file", + .sn = "SoCAPFirmwareHash", + .ln = "SoC AP Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [SOC_FW_CONFIG_HASH_EXT] = { + .oid = SOC_FW_CONFIG_HASH_OID, + .opt = "soc-fw-config", + .help_msg = "SoC Firmware Config file", + .sn = "SocFirmwareConfigHash", + .ln = "SoC Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [TRUSTED_OS_FW_CONTENT_CERT_PK_EXT] = { + .oid = TRUSTED_OS_FW_CONTENT_CERT_PK_OID, + .sn = "TrustedOSFirmwareContentCertPK", + .ln = "Trusted OS Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = TRUSTED_OS_FW_CONTENT_CERT_KEY + }, + + [TRUSTED_OS_FW_HASH_EXT] = { + .oid = TRUSTED_OS_FW_HASH_OID, + .opt = "tos-fw", + .help_msg = "Trusted OS image file", + .sn = "TrustedOSHash", + .ln = "Trusted OS hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [TRUSTED_OS_FW_EXTRA1_HASH_EXT] = { + .oid = TRUSTED_OS_FW_EXTRA1_HASH_OID, + .opt = "tos-fw-extra1", + .help_msg = "Trusted OS Extra1 image file", + .sn = "TrustedOSExtra1Hash", + .ln = "Trusted OS Extra1 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [TRUSTED_OS_FW_EXTRA2_HASH_EXT] = { + .oid = TRUSTED_OS_FW_EXTRA2_HASH_OID, + .opt = "tos-fw-extra2", + .help_msg = "Trusted OS Extra2 image file", + .sn = "TrustedOSExtra2Hash", + .ln = "Trusted OS Extra2 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [TRUSTED_OS_FW_CONFIG_HASH_EXT] = { + .oid = TRUSTED_OS_FW_CONFIG_HASH_OID, + .opt = "tos-fw-config", + .help_msg = "Trusted OS Firmware Config file", + .sn = "TrustedOSFirmwareConfigHash", + .ln = "Trusted OS Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [SP_PKG1_HASH_EXT] = { + .oid = SP_PKG1_HASH_OID, + .opt = "sp-pkg1", + .help_msg = "Secure Partition Package1 file", + .sn = "SPPkg1Hash", + .ln = "SP Pkg1 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG2_HASH_EXT] = { + .oid = SP_PKG2_HASH_OID, + .opt = "sp-pkg2", + .help_msg = "Secure Partition Package2 file", + .sn = "SPPkg2Hash", + .ln = "SP Pkg2 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG3_HASH_EXT] = { + .oid = SP_PKG3_HASH_OID, + .opt = "sp-pkg3", + .help_msg = "Secure Partition Package3 file", + .sn = "SPPkg3Hash", + .ln = "SP Pkg3 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG4_HASH_EXT] = { + .oid = SP_PKG4_HASH_OID, + .opt = "sp-pkg4", + .help_msg = "Secure Partition Package4 file", + .sn = "SPPkg4Hash", + .ln = "SP Pkg4 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG5_HASH_EXT] = { + .oid = SP_PKG5_HASH_OID, + .opt = "sp-pkg5", + .help_msg = "Secure Partition Package5 file", + .sn = "SPPkg5Hash", + .ln = "SP Pkg5 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG6_HASH_EXT] = { + .oid = SP_PKG6_HASH_OID, + .opt = "sp-pkg6", + .help_msg = "Secure Partition Package6 file", + .sn = "SPPkg6Hash", + .ln = "SP Pkg6 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG7_HASH_EXT] = { + .oid = SP_PKG7_HASH_OID, + .opt = "sp-pkg7", + .help_msg = "Secure Partition Package7 file", + .sn = "SPPkg7Hash", + .ln = "SP Pkg7 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG8_HASH_EXT] = { + .oid = SP_PKG8_HASH_OID, + .opt = "sp-pkg8", + .help_msg = "Secure Partition Package8 file", + .sn = "SPPkg8Hash", + .ln = "SP Pkg8 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [SCP_FWU_CFG_HASH_EXT] = { + .oid = SCP_FWU_CFG_HASH_OID, + .opt = "scp-fwu-cfg", + .help_msg = "SCP Firmware Update Config image file", + .sn = "SCPFWUpdateConfig", + .ln = "SCP Firmware Update Config hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [AP_FWU_CFG_HASH_EXT] = { + .oid = AP_FWU_CFG_HASH_OID, + .opt = "ap-fwu-cfg", + .help_msg = "AP Firmware Update Config image file", + .sn = "APFWUpdateConfig", + .ln = "AP Firmware Update Config hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [FWU_HASH_EXT] = { + .oid = FWU_HASH_OID, + .opt = "fwu", + .help_msg = "Firmware Updater image file", + .sn = "FWUpdaterHash", + .ln = "Firmware Updater hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + + [PROT_PK_EXT] = { + .oid = PROT_PK_OID, + .sn = "PlatformRoTKey", + .ln = "Platform Root of Trust Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = PROT_KEY + }, + + [NON_TRUSTED_FW_NVCOUNTER_EXT] = { + .oid = NON_TRUSTED_FW_NVCOUNTER_OID, + .opt = "ntfw-nvctr", + .help_msg = "Non-Trusted Firmware Non-Volatile counter value", + .sn = "NormalWorldNVCounter", + .ln = "Non-Trusted Firmware Non-Volatile counter", + .asn1_type = V_ASN1_INTEGER, + .type = EXT_TYPE_NVCOUNTER, + .attr.nvctr_type = NVCTR_TYPE_NTFW + }, + + [NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT] = { + .oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID, + .opt = "nt-fw", + .help_msg = "Non-Trusted World Bootloader image file", + .sn = "NonTrustedWorldBootloaderHash", + .ln = "Non-Trusted World hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + + [NON_TRUSTED_FW_CONFIG_HASH_EXT] = { + .oid = NON_TRUSTED_FW_CONFIG_HASH_OID, + .opt = "nt-fw-config", + .help_msg = "Non Trusted OS Firmware Config file", + .sn = "NonTrustedOSFirmwareConfigHash", + .ln = "Non-Trusted OS Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, +}; + +REGISTER_EXTENSIONS(cot_ext); + + +/* Keys used to establish the chain of trust. */ +static key_t cot_keys[] = { + [ROT_KEY] = { + .id = ROT_KEY, + .opt = "rot-key", + .help_msg = "Root Of Trust key file or PKCS11 URI", + .desc = "Root Of Trust key" + }, + + [TRUSTED_WORLD_KEY] = { + .id = TRUSTED_WORLD_KEY, + .opt = "trusted-world-key", + .help_msg = "Trusted World key file or PKCS11 URI", + .desc = "Trusted World key" + }, + + [SCP_FW_CONTENT_CERT_KEY] = { + .id = SCP_FW_CONTENT_CERT_KEY, + .opt = "scp-fw-key", + .help_msg = "SCP Firmware Content Certificate key file or PKCS11 URI", + .desc = "SCP Firmware Content Certificate key" + }, + + [SOC_FW_CONTENT_CERT_KEY] = { + .id = SOC_FW_CONTENT_CERT_KEY, + .opt = "soc-fw-key", + .help_msg = "SoC Firmware Content Certificate key file or PKCS11 URI", + .desc = "SoC Firmware Content Certificate key" + }, + + [TRUSTED_OS_FW_CONTENT_CERT_KEY] = { + .id = TRUSTED_OS_FW_CONTENT_CERT_KEY, + .opt = "tos-fw-key", + .help_msg = "Trusted OS Firmware Content Certificate key file or PKCS11 URI", + .desc = "Trusted OS Firmware Content Certificate key" + }, + + [PROT_KEY] = { + .id = PROT_KEY, + .opt = "prot-key", + .help_msg = "Platform Root of Trust key file or PKCS11 URI", + .desc = "Platform Root of Trust key" + }, +}; + +REGISTER_KEYS(cot_keys); diff --git a/tools/cert_create/src/dualroot/cot.mk b/tools/cert_create/src/dualroot/cot.mk new file mode 100644 index 0000000..a572484 --- /dev/null +++ b/tools/cert_create/src/dualroot/cot.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2020, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +PLAT_MSG := Dual root of trust +PLAT_INCLUDE := ../../include/tools_share + +OBJECTS += src/dualroot/cot.o diff --git a/tools/cert_create/src/ext.c b/tools/cert_create/src/ext.c new file mode 100644 index 0000000..acf57a4 --- /dev/null +++ b/tools/cert_create/src/ext.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/err.h> +#include <openssl/x509v3.h> + +#include "cmd_opt.h" +#include "debug.h" +#include "ext.h" + +ext_t *extensions; +unsigned int num_extensions; + +DECLARE_ASN1_ITEM(ASN1_INTEGER) +DECLARE_ASN1_ITEM(X509_ALGOR) +DECLARE_ASN1_ITEM(ASN1_OCTET_STRING) + +typedef struct { + X509_ALGOR *hashAlgorithm; + ASN1_OCTET_STRING *dataHash; +} HASH; + +ASN1_SEQUENCE(HASH) = { + ASN1_SIMPLE(HASH, hashAlgorithm, X509_ALGOR), + ASN1_SIMPLE(HASH, dataHash, ASN1_OCTET_STRING), +} ASN1_SEQUENCE_END(HASH) + +DECLARE_ASN1_FUNCTIONS(HASH) +IMPLEMENT_ASN1_FUNCTIONS(HASH) + +/* + * This function adds the CoT extensions to the internal extension list + * maintained by OpenSSL so they can be used later. + * + * It also initializes the methods to print the contents of the extension. If an + * alias is specified in the CoT extension, we reuse the methods of the alias. + * Otherwise, only methods for V_ASN1_INTEGER and V_ASN1_OCTET_STRING are + * provided. Any other type will be printed as a raw ascii string. + * + * Return: 0 = success, Otherwise: error + */ +int ext_init(void) +{ + cmd_opt_t cmd_opt; + ext_t *ext; + X509V3_EXT_METHOD *m; + int nid, ret; + unsigned int i; + + extensions = malloc((num_def_extensions * sizeof(def_extensions[0])) +#ifdef PDEF_EXTS + + (num_pdef_extensions * sizeof(pdef_extensions[0])) +#endif + ); + if (extensions == NULL) { + ERROR("%s:%d Failed to allocate memory.\n", __func__, __LINE__); + return 1; + } + + memcpy(&extensions[0], &def_extensions[0], + (num_def_extensions * sizeof(def_extensions[0]))); +#ifdef PDEF_EXTS + memcpy(&extensions[num_def_extensions], &pdef_extensions[0], + (num_pdef_extensions * sizeof(pdef_extensions[0]))); + num_extensions = num_def_extensions + num_pdef_extensions; +#else + num_extensions = num_def_extensions; +#endif + + for (i = 0; i < num_extensions; i++) { + ext = &extensions[i]; + /* Register command line option */ + if (ext->opt) { + cmd_opt.long_opt.name = ext->opt; + cmd_opt.long_opt.has_arg = required_argument; + cmd_opt.long_opt.flag = NULL; + cmd_opt.long_opt.val = CMD_OPT_EXT; + cmd_opt.help_msg = ext->help_msg; + cmd_opt_add(&cmd_opt); + } + /* Register the extension OID in OpenSSL */ + if (ext->oid == NULL) { + continue; + } + nid = OBJ_create(ext->oid, ext->sn, ext->ln); + if (ext->alias) { + X509V3_EXT_add_alias(nid, ext->alias); + } else { + m = &ext->method; + memset(m, 0x0, sizeof(X509V3_EXT_METHOD)); + switch (ext->asn1_type) { + case V_ASN1_INTEGER: + m->it = ASN1_ITEM_ref(ASN1_INTEGER); + m->i2s = (X509V3_EXT_I2S)i2s_ASN1_INTEGER; + m->s2i = (X509V3_EXT_S2I)s2i_ASN1_INTEGER; + break; + case V_ASN1_OCTET_STRING: + m->it = ASN1_ITEM_ref(ASN1_OCTET_STRING); + m->i2s = (X509V3_EXT_I2S)i2s_ASN1_OCTET_STRING; + m->s2i = (X509V3_EXT_S2I)s2i_ASN1_OCTET_STRING; + break; + default: + continue; + } + m->ext_nid = nid; + ret = X509V3_EXT_add(m); + if (!ret) { + ERR_print_errors_fp(stdout); + return 1; + } + } + } + return 0; +} + +/* + * Create a new extension + * + * Extension ::= SEQUENCE { + * id OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * value OCTET STRING } + * + * Parameters: + * pex: OpenSSL extension pointer (output parameter) + * nid: extension identifier + * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) + * data: extension data. This data will be encapsulated in an Octet String + * + * Return: Extension address, NULL if error + */ +static +X509_EXTENSION *ext_new(int nid, int crit, unsigned char *data, int len) +{ + X509_EXTENSION *ex; + ASN1_OCTET_STRING *ext_data; + + /* Octet string containing the extension data */ + ext_data = ASN1_OCTET_STRING_new(); + ASN1_OCTET_STRING_set(ext_data, data, len); + + /* Create the extension */ + ex = X509_EXTENSION_create_by_NID(NULL, nid, crit, ext_data); + + /* The extension makes a copy of the data, so we can free this object */ + ASN1_OCTET_STRING_free(ext_data); + + return ex; +} + +/* + * Creates a x509v3 extension containing a hash + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING + * } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * + * Parameters: + * nid: extension identifier + * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) + * md: hash algorithm + * buf: pointer to the buffer that contains the hash + * len: size of the hash in bytes + * + * Return: Extension address, NULL if error + */ +X509_EXTENSION *ext_new_hash(int nid, int crit, const EVP_MD *md, + unsigned char *buf, size_t len) +{ + X509_EXTENSION *ex; + HASH *hash; + ASN1_OBJECT *algorithm; + unsigned char *p = NULL; + int sz; + + /* HASH structure containing algorithm + hash */ + hash = HASH_new(); + if (hash == NULL) { + return NULL; + } + + /* OBJECT_IDENTIFIER with hash algorithm */ + algorithm = OBJ_nid2obj(EVP_MD_type(md)); + if (algorithm == NULL) { + HASH_free(hash); + return NULL; + } + + /* Create X509_ALGOR */ + hash->hashAlgorithm->algorithm = algorithm; + hash->hashAlgorithm->parameter = ASN1_TYPE_new(); + ASN1_TYPE_set(hash->hashAlgorithm->parameter, V_ASN1_NULL, NULL); + + /* OCTET_STRING with the actual hash */ + ASN1_OCTET_STRING_set(hash->dataHash, buf, len); + + /* DER encoded HASH */ + sz = i2d_HASH(hash, &p); + if ((sz <= 0) || (p == NULL)) { + HASH_free(hash); + return NULL; + } + + /* Create the extension */ + ex = ext_new(nid, crit, p, sz); + + /* Clean up */ + OPENSSL_free(p); + HASH_free(hash); + + return ex; +} + +/* + * Creates a x509v3 extension containing a nvcounter encapsulated in an ASN1 + * Integer + * + * Parameters: + * pex: OpenSSL extension pointer (output parameter) + * nid: extension identifier + * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) + * value: nvcounter value + * + * Return: Extension address, NULL if error + */ +X509_EXTENSION *ext_new_nvcounter(int nid, int crit, int value) +{ + X509_EXTENSION *ex; + ASN1_INTEGER *counter; + unsigned char *p = NULL; + int sz; + + /* Encode counter */ + counter = ASN1_INTEGER_new(); + ASN1_INTEGER_set(counter, value); + sz = i2d_ASN1_INTEGER(counter, &p); + + /* Create the extension */ + ex = ext_new(nid, crit, p, sz); + + /* Free objects */ + OPENSSL_free(p); + ASN1_INTEGER_free(counter); + + return ex; +} + +/* + * Creates a x509v3 extension containing a public key in DER format: + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * Parameters: + * pex: OpenSSL extension pointer (output parameter) + * nid: extension identifier + * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) + * k: key + * + * Return: Extension address, NULL if error + */ +X509_EXTENSION *ext_new_key(int nid, int crit, EVP_PKEY *k) +{ + X509_EXTENSION *ex; + unsigned char *p; + int sz; + + /* Encode key */ + BIO *mem = BIO_new(BIO_s_mem()); + if (i2d_PUBKEY_bio(mem, k) <= 0) { + ERR_print_errors_fp(stderr); + return NULL; + } + p = (unsigned char *)OPENSSL_malloc(4096); + sz = BIO_read(mem, p, 4096); + + /* Create the extension */ + ex = ext_new(nid, crit, p, sz); + + /* Clean up */ + BIO_free(mem); + OPENSSL_free(p); + + return ex; +} + +ext_t *ext_get_by_opt(const char *opt) +{ + ext_t *ext; + unsigned int i; + + /* Sequential search. This is not a performance concern since the number + * of extensions is bounded and the code runs on a host machine */ + for (i = 0; i < num_extensions; i++) { + ext = &extensions[i]; + if (ext->opt && !strcmp(ext->opt, opt)) { + return ext; + } + } + + return NULL; +} + +void ext_cleanup(void) +{ + unsigned int i; + + for (i = 0; i < num_extensions; i++) { + if (extensions[i].arg != NULL) { + void *ptr = (void *)extensions[i].arg; + + extensions[i].arg = NULL; + free(ptr); + } + } + free(extensions); + X509V3_EXT_cleanup(); +} + diff --git a/tools/cert_create/src/key.c b/tools/cert_create/src/key.c new file mode 100644 index 0000000..04214aa --- /dev/null +++ b/tools/cert_create/src/key.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* Suppress OpenSSL engine deprecation warnings */ +#define OPENSSL_SUPPRESS_DEPRECATED + +#include <openssl/conf.h> +#include <openssl/engine.h> +#include <openssl/evp.h> +#include <openssl/pem.h> + +#include "cert.h" +#include "cmd_opt.h" +#include "debug.h" +#include "key.h" +#include "sha.h" + +#define MAX_FILENAME_LEN 1024 + +key_t *keys; +unsigned int num_keys; + +#if !USING_OPENSSL3 +/* + * Create a new key container + */ +int key_new(key_t *key) +{ + /* Create key pair container */ + key->key = EVP_PKEY_new(); + if (key->key == NULL) { + return 0; + } + + return 1; +} +#endif + +static int key_create_rsa(key_t *key, int key_bits) +{ +#if USING_OPENSSL3 + EVP_PKEY *rsa = EVP_RSA_gen(key_bits); + if (rsa == NULL) { + printf("Cannot generate RSA key\n"); + return 0; + } + key->key = rsa; + return 1; +#else + BIGNUM *e; + RSA *rsa = NULL; + + e = BN_new(); + if (e == NULL) { + printf("Cannot create RSA exponent\n"); + return 0; + } + + if (!BN_set_word(e, RSA_F4)) { + printf("Cannot assign RSA exponent\n"); + goto err2; + } + + rsa = RSA_new(); + if (rsa == NULL) { + printf("Cannot create RSA key\n"); + goto err2; + } + + if (!RSA_generate_key_ex(rsa, key_bits, e, NULL)) { + printf("Cannot generate RSA key\n"); + goto err; + } + + if (!EVP_PKEY_assign_RSA(key->key, rsa)) { + printf("Cannot assign RSA key\n"); + goto err; + } + + BN_free(e); + return 1; + +err: + RSA_free(rsa); +err2: + BN_free(e); + return 0; +#endif +} + +#ifndef OPENSSL_NO_EC +#if USING_OPENSSL3 +static int key_create_ecdsa(key_t *key, int key_bits, const char *curve) +{ + EVP_PKEY *ec = EVP_EC_gen(curve); + if (ec == NULL) { + printf("Cannot generate EC key\n"); + return 0; + } + + key->key = ec; + return 1; +} + +static int key_create_ecdsa_nist(key_t *key, int key_bits) +{ + if (key_bits == 384) { + return key_create_ecdsa(key, key_bits, "secp384r1"); + } else { + assert(key_bits == 256); + return key_create_ecdsa(key, key_bits, "prime256v1"); + } +} + +static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits) +{ + return key_create_ecdsa(key, key_bits, "brainpoolP256r1"); +} + +static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits) +{ + return key_create_ecdsa(key, key_bits, "brainpoolP256t1"); +} +#else +static int key_create_ecdsa(key_t *key, int key_bits, const int curve_id) +{ + EC_KEY *ec; + + ec = EC_KEY_new_by_curve_name(curve_id); + if (ec == NULL) { + printf("Cannot create EC key\n"); + return 0; + } + if (!EC_KEY_generate_key(ec)) { + printf("Cannot generate EC key\n"); + goto err; + } + EC_KEY_set_flags(ec, EC_PKEY_NO_PARAMETERS); + EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); + if (!EVP_PKEY_assign_EC_KEY(key->key, ec)) { + printf("Cannot assign EC key\n"); + goto err; + } + + return 1; + +err: + EC_KEY_free(ec); + return 0; +} + +static int key_create_ecdsa_nist(key_t *key, int key_bits) +{ + if (key_bits == 384) { + return key_create_ecdsa(key, key_bits, NID_secp384r1); + } else { + assert(key_bits == 256); + return key_create_ecdsa(key, key_bits, NID_X9_62_prime256v1); + } +} + +static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits) +{ + return key_create_ecdsa(key, key_bits, NID_brainpoolP256r1); +} + +static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits) +{ + return key_create_ecdsa(key, key_bits, NID_brainpoolP256t1); +} +#endif /* USING_OPENSSL3 */ +#endif /* OPENSSL_NO_EC */ + +typedef int (*key_create_fn_t)(key_t *key, int key_bits); +static const key_create_fn_t key_create_fn[KEY_ALG_MAX_NUM] = { + [KEY_ALG_RSA] = key_create_rsa, +#ifndef OPENSSL_NO_EC + [KEY_ALG_ECDSA_NIST] = key_create_ecdsa_nist, + [KEY_ALG_ECDSA_BRAINPOOL_R] = key_create_ecdsa_brainpool_r, + [KEY_ALG_ECDSA_BRAINPOOL_T] = key_create_ecdsa_brainpool_t, +#endif /* OPENSSL_NO_EC */ +}; + +int key_create(key_t *key, int type, int key_bits) +{ + if (type >= KEY_ALG_MAX_NUM) { + printf("Invalid key type\n"); + return 0; + } + + if (key_create_fn[type]) { + return key_create_fn[type](key, key_bits); + } + + return 0; +} + +static EVP_PKEY *key_load_pkcs11(const char *uri) +{ + char *key_pass; + EVP_PKEY *pkey; + ENGINE *e; + + ENGINE_load_builtin_engines(); + e = ENGINE_by_id("pkcs11"); + if (!e) { + fprintf(stderr, "Cannot Load PKCS#11 ENGINE\n"); + return NULL; + } + + if (!ENGINE_init(e)) { + fprintf(stderr, "Cannot ENGINE_init\n"); + goto err; + } + + key_pass = getenv("PKCS11_PIN"); + if (key_pass) { + if (!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0)) { + fprintf(stderr, "Cannot Set PKCS#11 PIN\n"); + goto err; + } + } + + pkey = ENGINE_load_private_key(e, uri, NULL, NULL); + if (pkey) + return pkey; +err: + ENGINE_free(e); + return NULL; + +} + +unsigned int key_load(key_t *key) +{ + if (key->fn == NULL) { + VERBOSE("Key not specified\n"); + return KEY_ERR_FILENAME; + } + + if (strncmp(key->fn, "pkcs11:", 7) == 0) { + /* Load key through pkcs11 */ + key->key = key_load_pkcs11(key->fn); + } else { + /* Load key from file */ + FILE *fp = fopen(key->fn, "r"); + if (fp == NULL) { + WARN("Cannot open file %s\n", key->fn); + return KEY_ERR_OPEN; + } + + key->key = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + fclose(fp); + } + + if (key->key == NULL) { + ERROR("Cannot load key from %s\n", key->fn); + return KEY_ERR_LOAD; + } + + return KEY_ERR_NONE; +} + +int key_store(key_t *key) +{ + FILE *fp; + + if (key->fn) { + if (!strncmp(key->fn, "pkcs11:", 7)) { + ERROR("PKCS11 URI provided instead of a file"); + return 0; + } + fp = fopen(key->fn, "w"); + if (fp) { + PEM_write_PrivateKey(fp, key->key, + NULL, NULL, 0, NULL, NULL); + fclose(fp); + return 1; + } else { + ERROR("Cannot create file %s\n", key->fn); + } + } else { + ERROR("Key filename not specified\n"); + } + + return 0; +} + +int key_init(void) +{ + cmd_opt_t cmd_opt; + key_t *key; + unsigned int i; + + keys = malloc((num_def_keys * sizeof(def_keys[0])) +#ifdef PDEF_KEYS + + (num_pdef_keys * sizeof(pdef_keys[0])) +#endif + ); + + if (keys == NULL) { + ERROR("%s:%d Failed to allocate memory.\n", __func__, __LINE__); + return 1; + } + + memcpy(&keys[0], &def_keys[0], (num_def_keys * sizeof(def_keys[0]))); +#ifdef PDEF_KEYS + memcpy(&keys[num_def_keys], &pdef_keys[0], + (num_pdef_keys * sizeof(pdef_keys[0]))); + + num_keys = num_def_keys + num_pdef_keys; +#else + num_keys = num_def_keys; +#endif + ; + + for (i = 0; i < num_keys; i++) { + key = &keys[i]; + if (key->opt != NULL) { + cmd_opt.long_opt.name = key->opt; + cmd_opt.long_opt.has_arg = required_argument; + cmd_opt.long_opt.flag = NULL; + cmd_opt.long_opt.val = CMD_OPT_KEY; + cmd_opt.help_msg = key->help_msg; + cmd_opt_add(&cmd_opt); + } + } + + return 0; +} + +key_t *key_get_by_opt(const char *opt) +{ + key_t *key; + unsigned int i; + + /* Sequential search. This is not a performance concern since the number + * of keys is bounded and the code runs on a host machine */ + for (i = 0; i < num_keys; i++) { + key = &keys[i]; + if (0 == strcmp(key->opt, opt)) { + return key; + } + } + + return NULL; +} + +void key_cleanup(void) +{ + unsigned int i; + + for (i = 0; i < num_keys; i++) { + EVP_PKEY_free(keys[i].key); + if (keys[i].fn != NULL) { + void *ptr = keys[i].fn; + + free(ptr); + keys[i].fn = NULL; + } + } + free(keys); +} + diff --git a/tools/cert_create/src/main.c b/tools/cert_create/src/main.c new file mode 100644 index 0000000..f10a768 --- /dev/null +++ b/tools/cert_create/src/main.c @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include <openssl/conf.h> +#include <openssl/engine.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/sha.h> +#include <openssl/x509v3.h> + +#include "cert.h" +#include "cmd_opt.h" +#include "debug.h" +#include "ext.h" +#include "key.h" +#include "sha.h" + +/* + * Helper macros to simplify the code. This macro assigns the return value of + * the 'fn' function to 'v' and exits if the value is NULL. + */ +#define CHECK_NULL(v, fn) \ + do { \ + v = fn; \ + if (v == NULL) { \ + ERROR("NULL object at %s:%d\n", __FILE__, __LINE__); \ + exit(1); \ + } \ + } while (0) + +/* + * This macro assigns the NID corresponding to 'oid' to 'v' and exits if the + * NID is undefined. + */ +#define CHECK_OID(v, oid) \ + do { \ + v = OBJ_txt2nid(oid); \ + if (v == NID_undef) { \ + ERROR("Cannot find extension %s\n", oid); \ + exit(1); \ + } \ + } while (0) + +#define MAX_FILENAME_LEN 1024 +#define VAL_DAYS 7300 +#define ID_TO_BIT_MASK(id) (1 << id) +#define NUM_ELEM(x) ((sizeof(x)) / (sizeof(x[0]))) +#define HELP_OPT_MAX_LEN 128 + +/* Global options */ +static int key_alg; +static int hash_alg; +static int key_size; +static int new_keys; +static int save_keys; +static int print_cert; + +/* Info messages created in the Makefile */ +extern const char build_msg[]; +extern const char platform_msg[]; + + +static char *strdup(const char *str) +{ + int n = strlen(str) + 1; + char *dup = malloc(n); + if (dup) { + strcpy(dup, str); + } + return dup; +} + +static const char *key_algs_str[] = { + [KEY_ALG_RSA] = "rsa", +#ifndef OPENSSL_NO_EC + [KEY_ALG_ECDSA_NIST] = "ecdsa", + [KEY_ALG_ECDSA_BRAINPOOL_R] = "ecdsa-brainpool-regular", + [KEY_ALG_ECDSA_BRAINPOOL_T] = "ecdsa-brainpool-twisted", +#endif /* OPENSSL_NO_EC */ +}; + +static const char *hash_algs_str[] = { + [HASH_ALG_SHA256] = "sha256", + [HASH_ALG_SHA384] = "sha384", + [HASH_ALG_SHA512] = "sha512", +}; + +static void print_help(const char *cmd, const struct option *long_opt) +{ + int rem, i = 0; + const struct option *opt; + char line[HELP_OPT_MAX_LEN]; + char *p; + + assert(cmd != NULL); + assert(long_opt != NULL); + + printf("\n\n"); + printf("The certificate generation tool loads the binary images and\n" + "optionally the RSA or ECC keys, and outputs the key and content\n" + "certificates properly signed to implement the chain of trust.\n" + "If keys are provided, they must be in PEM format.\n" + "Certificates are generated in DER format.\n"); + printf("\n"); + printf("Usage:\n"); + printf("\t%s [OPTIONS]\n\n", cmd); + + printf("Available options:\n"); + opt = long_opt; + while (opt->name) { + p = line; + rem = HELP_OPT_MAX_LEN; + if (isalpha(opt->val)) { + /* Short format */ + sprintf(p, "-%c,", (char)opt->val); + p += 3; + rem -= 3; + } + snprintf(p, rem, "--%s %s", opt->name, + (opt->has_arg == required_argument) ? "<arg>" : ""); + printf("\t%-32s %s\n", line, cmd_opt_get_help_msg(i)); + opt++; + i++; + } + printf("\n"); +} + +static int get_key_alg(const char *key_alg_str) +{ + int i; + + for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) { + if (0 == strcmp(key_alg_str, key_algs_str[i])) { + return i; + } + } + + return -1; +} + +static int get_key_size(const char *key_size_str) +{ + char *end; + long key_size; + + key_size = strtol(key_size_str, &end, 10); + if (*end != '\0') + return -1; + + return key_size; +} + +static int get_hash_alg(const char *hash_alg_str) +{ + int i; + + for (i = 0 ; i < NUM_ELEM(hash_algs_str) ; i++) { + if (0 == strcmp(hash_alg_str, hash_algs_str[i])) { + return i; + } + } + + return -1; +} + +static void check_cmd_params(void) +{ + cert_t *cert; + ext_t *ext; + key_t *key; + int i, j; + bool valid_size; + + /* Only save new keys */ + if (save_keys && !new_keys) { + ERROR("Only new keys can be saved to disk\n"); + exit(1); + } + + /* Validate key-size */ + valid_size = false; + for (i = 0; i < KEY_SIZE_MAX_NUM; i++) { + if (key_size == KEY_SIZES[key_alg][i]) { + valid_size = true; + break; + } + } + if (!valid_size) { + ERROR("'%d' is not a valid key size for '%s'\n", + key_size, key_algs_str[key_alg]); + NOTICE("Valid sizes are: "); + for (i = 0; i < KEY_SIZE_MAX_NUM && + KEY_SIZES[key_alg][i] != 0; i++) { + printf("%d ", KEY_SIZES[key_alg][i]); + } + printf("\n"); + exit(1); + } + + /* Check that all required options have been specified in the + * command line */ + for (i = 0; i < num_certs; i++) { + cert = &certs[i]; + if (cert->fn == NULL) { + /* Certificate not requested. Skip to the next one */ + continue; + } + + /* Check that all parameters required to create this certificate + * have been specified in the command line */ + for (j = 0; j < cert->num_ext; j++) { + ext = &extensions[cert->ext[j]]; + switch (ext->type) { + case EXT_TYPE_NVCOUNTER: + /* Counter value must be specified */ + if ((!ext->optional) && (ext->arg == NULL)) { + ERROR("Value for '%s' not specified\n", + ext->ln); + exit(1); + } + break; + case EXT_TYPE_PKEY: + /* Key filename must be specified */ + key = &keys[ext->attr.key]; + if (!new_keys && key->fn == NULL) { + ERROR("Key '%s' required by '%s' not " + "specified\n", key->desc, + cert->cn); + exit(1); + } + break; + case EXT_TYPE_HASH: + /* + * Binary image must be specified + * unless it is explicitly made optional. + */ + if ((!ext->optional) && (ext->arg == NULL)) { + ERROR("Image for '%s' not specified\n", + ext->ln); + exit(1); + } + break; + default: + ERROR("Unknown extension type '%d' in '%s'\n", + ext->type, ext->ln); + exit(1); + break; + } + } + } +} + +/* Common command line options */ +static const cmd_opt_t common_cmd_opt[] = { + { + { "help", no_argument, NULL, 'h' }, + "Print this message and exit" + }, + { + { "key-alg", required_argument, NULL, 'a' }, + "Key algorithm: 'rsa' (default)- RSAPSS scheme as per PKCS#1 v2.1, " \ + "'ecdsa', 'ecdsa-brainpool-regular', 'ecdsa-brainpool-twisted'" + }, + { + { "key-size", required_argument, NULL, 'b' }, + "Key size (for supported algorithms)." + }, + { + { "hash-alg", required_argument, NULL, 's' }, + "Hash algorithm : 'sha256' (default), 'sha384', 'sha512'" + }, + { + { "save-keys", no_argument, NULL, 'k' }, + "Save key pairs into files. Filenames must be provided" + }, + { + { "new-keys", no_argument, NULL, 'n' }, + "Generate new key pairs if no key files are provided" + }, + { + { "print-cert", no_argument, NULL, 'p' }, + "Print the certificates in the standard output" + } +}; + +int main(int argc, char *argv[]) +{ + STACK_OF(X509_EXTENSION) * sk; + X509_EXTENSION *cert_ext = NULL; + ext_t *ext; + key_t *key; + cert_t *cert; + FILE *file; + int i, j, ext_nid, nvctr; + int c, opt_idx = 0; + const struct option *cmd_opt; + const char *cur_opt; + unsigned int err_code; + unsigned char md[SHA512_DIGEST_LENGTH]; + unsigned int md_len; + const EVP_MD *md_info; + + NOTICE("CoT Generation Tool: %s\n", build_msg); + NOTICE("Target platform: %s\n", platform_msg); + + /* Set default options */ + key_alg = KEY_ALG_RSA; + hash_alg = HASH_ALG_SHA256; + key_size = -1; + + /* Add common command line options */ + for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) { + cmd_opt_add(&common_cmd_opt[i]); + } + + /* Initialize the certificates */ + if (cert_init() != 0) { + ERROR("Cannot initialize certificates\n"); + exit(1); + } + + /* Initialize the keys */ + if (key_init() != 0) { + ERROR("Cannot initialize keys\n"); + exit(1); + } + + /* Initialize the new types and register OIDs for the extensions */ + if (ext_init() != 0) { + ERROR("Cannot initialize extensions\n"); + exit(1); + } + + /* Get the command line options populated during the initialization */ + cmd_opt = cmd_opt_get_array(); + + while (1) { + /* getopt_long stores the option index here. */ + c = getopt_long(argc, argv, "a:b:hknps:", cmd_opt, &opt_idx); + + /* Detect the end of the options. */ + if (c == -1) { + break; + } + + switch (c) { + case 'a': + key_alg = get_key_alg(optarg); + if (key_alg < 0) { + ERROR("Invalid key algorithm '%s'\n", optarg); + exit(1); + } + break; + case 'b': + key_size = get_key_size(optarg); + if (key_size <= 0) { + ERROR("Invalid key size '%s'\n", optarg); + exit(1); + } + break; + case 'h': + print_help(argv[0], cmd_opt); + exit(0); + case 'k': + save_keys = 1; + break; + case 'n': + new_keys = 1; + break; + case 'p': + print_cert = 1; + break; + case 's': + hash_alg = get_hash_alg(optarg); + if (hash_alg < 0) { + ERROR("Invalid hash algorithm '%s'\n", optarg); + exit(1); + } + break; + case CMD_OPT_EXT: + cur_opt = cmd_opt_get_name(opt_idx); + ext = ext_get_by_opt(cur_opt); + ext->arg = strdup(optarg); + break; + case CMD_OPT_KEY: + cur_opt = cmd_opt_get_name(opt_idx); + key = key_get_by_opt(cur_opt); + key->fn = strdup(optarg); + break; + case CMD_OPT_CERT: + cur_opt = cmd_opt_get_name(opt_idx); + cert = cert_get_by_opt(cur_opt); + cert->fn = strdup(optarg); + break; + case '?': + default: + print_help(argv[0], cmd_opt); + exit(1); + } + } + + /* Select a reasonable default key-size */ + if (key_size == -1) { + key_size = KEY_SIZES[key_alg][0]; + } + + /* Check command line arguments */ + check_cmd_params(); + + /* Indicate SHA as image hash algorithm in the certificate + * extension */ + if (hash_alg == HASH_ALG_SHA384) { + md_info = EVP_sha384(); + md_len = SHA384_DIGEST_LENGTH; + } else if (hash_alg == HASH_ALG_SHA512) { + md_info = EVP_sha512(); + md_len = SHA512_DIGEST_LENGTH; + } else { + md_info = EVP_sha256(); + md_len = SHA256_DIGEST_LENGTH; + } + + /* Load private keys from files (or generate new ones) */ + for (i = 0 ; i < num_keys ; i++) { +#if !USING_OPENSSL3 + if (!key_new(&keys[i])) { + ERROR("Failed to allocate key container\n"); + exit(1); + } +#endif + + /* First try to load the key from disk */ + err_code = key_load(&keys[i]); + if (err_code == KEY_ERR_NONE) { + /* Key loaded successfully */ + continue; + } + + /* Key not loaded. Check the error code */ + if (err_code == KEY_ERR_LOAD) { + /* File exists, but it does not contain a valid private + * key. Abort. */ + ERROR("Error loading '%s'\n", keys[i].fn); + exit(1); + } + + /* File does not exist, could not be opened or no filename was + * given */ + if (new_keys) { + /* Try to create a new key */ + NOTICE("Creating new key for '%s'\n", keys[i].desc); + if (!key_create(&keys[i], key_alg, key_size)) { + ERROR("Error creating key '%s'\n", keys[i].desc); + exit(1); + } + } else { + if (err_code == KEY_ERR_OPEN) { + ERROR("Error opening '%s'\n", keys[i].fn); + } else { + ERROR("Key '%s' not specified\n", keys[i].desc); + } + exit(1); + } + } + + /* Create the certificates */ + for (i = 0 ; i < num_certs ; i++) { + + cert = &certs[i]; + + if (cert->fn == NULL) { + /* Certificate not requested. Skip to the next one */ + continue; + } + + /* Create a new stack of extensions. This stack will be used + * to create the certificate */ + CHECK_NULL(sk, sk_X509_EXTENSION_new_null()); + + for (j = 0 ; j < cert->num_ext ; j++) { + + ext = &extensions[cert->ext[j]]; + + /* Get OpenSSL internal ID for this extension */ + CHECK_OID(ext_nid, ext->oid); + + /* + * Three types of extensions are currently supported: + * - EXT_TYPE_NVCOUNTER + * - EXT_TYPE_HASH + * - EXT_TYPE_PKEY + */ + switch (ext->type) { + case EXT_TYPE_NVCOUNTER: + if (ext->optional && ext->arg == NULL) { + /* Skip this NVCounter */ + continue; + } else { + /* Checked by `check_cmd_params` */ + assert(ext->arg != NULL); + nvctr = atoi(ext->arg); + CHECK_NULL(cert_ext, ext_new_nvcounter(ext_nid, + EXT_CRIT, nvctr)); + } + break; + case EXT_TYPE_HASH: + if (ext->arg == NULL) { + if (ext->optional) { + /* Include a hash filled with zeros */ + memset(md, 0x0, SHA512_DIGEST_LENGTH); + } else { + /* Do not include this hash in the certificate */ + continue; + } + } else { + /* Calculate the hash of the file */ + if (!sha_file(hash_alg, ext->arg, md)) { + ERROR("Cannot calculate hash of %s\n", + ext->arg); + exit(1); + } + } + CHECK_NULL(cert_ext, ext_new_hash(ext_nid, + EXT_CRIT, md_info, md, + md_len)); + break; + case EXT_TYPE_PKEY: + CHECK_NULL(cert_ext, ext_new_key(ext_nid, + EXT_CRIT, keys[ext->attr.key].key)); + break; + default: + ERROR("Unknown extension type '%d' in %s\n", + ext->type, cert->cn); + exit(1); + } + + /* Push the extension into the stack */ + sk_X509_EXTENSION_push(sk, cert_ext); + } + + /* Create certificate. Signed with corresponding key */ + if (!cert_new(hash_alg, cert, VAL_DAYS, 0, sk)) { + ERROR("Cannot create %s\n", cert->cn); + exit(1); + } + + for (cert_ext = sk_X509_EXTENSION_pop(sk); cert_ext != NULL; + cert_ext = sk_X509_EXTENSION_pop(sk)) { + X509_EXTENSION_free(cert_ext); + } + + sk_X509_EXTENSION_free(sk); + } + + + /* Print the certificates */ + if (print_cert) { + for (i = 0 ; i < num_certs ; i++) { + if (!certs[i].x) { + continue; + } + printf("\n\n=====================================\n\n"); + X509_print_fp(stdout, certs[i].x); + } + } + + /* Save created certificates to files */ + for (i = 0 ; i < num_certs ; i++) { + if (certs[i].x && certs[i].fn) { + file = fopen(certs[i].fn, "w"); + if (file != NULL) { + i2d_X509_fp(file, certs[i].x); + fclose(file); + } else { + ERROR("Cannot create file %s\n", certs[i].fn); + } + } + } + + /* Save keys */ + if (save_keys) { + for (i = 0 ; i < num_keys ; i++) { + if (!key_store(&keys[i])) { + ERROR("Cannot save %s\n", keys[i].desc); + } + } + } + + /* If we got here, then we must have filled the key array completely. + * We can then safely call free on all of the keys in the array + */ + key_cleanup(); + +#ifndef OPENSSL_NO_ENGINE + ENGINE_cleanup(); +#endif + CRYPTO_cleanup_all_ex_data(); + + + /* We allocated strings through strdup, so now we have to free them */ + + ext_cleanup(); + + cert_cleanup(); + + return 0; +} diff --git a/tools/cert_create/src/sha.c b/tools/cert_create/src/sha.c new file mode 100644 index 0000000..bb750d4 --- /dev/null +++ b/tools/cert_create/src/sha.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include "debug.h" +#include "key.h" +#if USING_OPENSSL3 +#include <openssl/evp.h> +#include <openssl/obj_mac.h> +#else +#include <openssl/sha.h> +#endif + +#define BUFFER_SIZE 256 + +#if USING_OPENSSL3 +static int get_algorithm_nid(int hash_alg) +{ + int nids[] = {NID_sha256, NID_sha384, NID_sha512}; + if (hash_alg < 0 || hash_alg >= sizeof(nids) / sizeof(*nids)) { + return NID_undef; + } + return nids[hash_alg]; +} +#endif + +int sha_file(int md_alg, const char *filename, unsigned char *md) +{ + FILE *inFile; + int bytes; + unsigned char data[BUFFER_SIZE]; +#if USING_OPENSSL3 + EVP_MD_CTX *mdctx; + const EVP_MD *md_type; + int alg_nid; + unsigned int total_bytes; +#else + SHA256_CTX shaContext; + SHA512_CTX sha512Context; +#endif + + if ((filename == NULL) || (md == NULL)) { + ERROR("%s(): NULL argument\n", __func__); + return 0; + } + + inFile = fopen(filename, "rb"); + if (inFile == NULL) { + ERROR("Cannot read %s\n", filename); + return 0; + } + +#if USING_OPENSSL3 + + mdctx = EVP_MD_CTX_new(); + if (mdctx == NULL) { + fclose(inFile); + ERROR("%s(): Could not create EVP MD context\n", __func__); + return 0; + } + + alg_nid = get_algorithm_nid(md_alg); + if (alg_nid == NID_undef) { + ERROR("%s(): Invalid hash algorithm\n", __func__); + goto err; + } + + md_type = EVP_get_digestbynid(alg_nid); + if (EVP_DigestInit_ex(mdctx, md_type, NULL) == 0) { + ERROR("%s(): Could not initialize EVP MD digest\n", __func__); + goto err; + } + + while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { + EVP_DigestUpdate(mdctx, data, bytes); + } + EVP_DigestFinal_ex(mdctx, md, &total_bytes); + + fclose(inFile); + EVP_MD_CTX_free(mdctx); + return 1; + +err: + fclose(inFile); + EVP_MD_CTX_free(mdctx); + return 0; + +#else + + if (md_alg == HASH_ALG_SHA384) { + SHA384_Init(&sha512Context); + while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { + SHA384_Update(&sha512Context, data, bytes); + } + SHA384_Final(md, &sha512Context); + } else if (md_alg == HASH_ALG_SHA512) { + SHA512_Init(&sha512Context); + while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { + SHA512_Update(&sha512Context, data, bytes); + } + SHA512_Final(md, &sha512Context); + } else { + SHA256_Init(&shaContext); + while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) { + SHA256_Update(&shaContext, data, bytes); + } + SHA256_Final(md, &shaContext); + } + + fclose(inFile); + return 1; + +#endif +} + diff --git a/tools/cert_create/src/tbbr/tbb_cert.c b/tools/cert_create/src/tbbr/tbb_cert.c new file mode 100644 index 0000000..f4fe63d --- /dev/null +++ b/tools/cert_create/src/tbbr/tbb_cert.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "tbbr/tbb_cert.h" +#include "tbbr/tbb_ext.h" +#include "tbbr/tbb_key.h" + +/* + * Certificates used in the chain of trust + * + * The order of the certificates must follow the enumeration specified in + * tbb_cert.h. All certificates are self-signed, so the issuer certificate + * field points to itself. + */ +static cert_t tbb_certs[] = { + [TRUSTED_BOOT_FW_CERT] = { + .id = TRUSTED_BOOT_FW_CERT, + .opt = "tb-fw-cert", + .help_msg = "Trusted Boot FW Certificate (output file)", + .fn = NULL, + .cn = "Trusted Boot FW Certificate", + .key = ROT_KEY, + .issuer = TRUSTED_BOOT_FW_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_BOOT_FW_HASH_EXT, + TRUSTED_BOOT_FW_CONFIG_HASH_EXT, + HW_CONFIG_HASH_EXT, + FW_CONFIG_HASH_EXT + }, + .num_ext = 5 + }, + [TRUSTED_KEY_CERT] = { + .id = TRUSTED_KEY_CERT, + .opt = "trusted-key-cert", + .help_msg = "Trusted Key Certificate (output file)", + .fn = NULL, + .cn = "Trusted Key Certificate", + .key = ROT_KEY, + .issuer = TRUSTED_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_WORLD_PK_EXT, + NON_TRUSTED_WORLD_PK_EXT + }, + .num_ext = 3 + }, + [SCP_FW_KEY_CERT] = { + .id = SCP_FW_KEY_CERT, + .opt = "scp-fw-key-cert", + .help_msg = "SCP Firmware Key Certificate (output file)", + .fn = NULL, + .cn = "SCP Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = SCP_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SCP_FW_CONTENT_CERT_PK_EXT + }, + .num_ext = 2 + }, + [SCP_FW_CONTENT_CERT] = { + .id = SCP_FW_CONTENT_CERT, + .opt = "scp-fw-cert", + .help_msg = "SCP Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "SCP Firmware Content Certificate", + .key = SCP_FW_CONTENT_CERT_KEY, + .issuer = SCP_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SCP_FW_HASH_EXT + }, + .num_ext = 2 + }, + [SOC_FW_KEY_CERT] = { + .id = SOC_FW_KEY_CERT, + .opt = "soc-fw-key-cert", + .help_msg = "SoC Firmware Key Certificate (output file)", + .fn = NULL, + .cn = "SoC Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = SOC_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SOC_FW_CONTENT_CERT_PK_EXT + }, + .num_ext = 2 + }, + [SOC_FW_CONTENT_CERT] = { + .id = SOC_FW_CONTENT_CERT, + .opt = "soc-fw-cert", + .help_msg = "SoC Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "SoC Firmware Content Certificate", + .key = SOC_FW_CONTENT_CERT_KEY, + .issuer = SOC_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SOC_AP_FW_HASH_EXT, + SOC_FW_CONFIG_HASH_EXT, + }, + .num_ext = 3 + }, + [TRUSTED_OS_FW_KEY_CERT] = { + .id = TRUSTED_OS_FW_KEY_CERT, + .opt = "tos-fw-key-cert", + .help_msg = "Trusted OS Firmware Key Certificate (output file)", + .fn = NULL, + .cn = "Trusted OS Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = TRUSTED_OS_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_OS_FW_CONTENT_CERT_PK_EXT + }, + .num_ext = 2 + }, + [TRUSTED_OS_FW_CONTENT_CERT] = { + .id = TRUSTED_OS_FW_CONTENT_CERT, + .opt = "tos-fw-cert", + .help_msg = "Trusted OS Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "Trusted OS Firmware Content Certificate", + .key = TRUSTED_OS_FW_CONTENT_CERT_KEY, + .issuer = TRUSTED_OS_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + TRUSTED_OS_FW_HASH_EXT, + TRUSTED_OS_FW_EXTRA1_HASH_EXT, + TRUSTED_OS_FW_EXTRA2_HASH_EXT, + TRUSTED_OS_FW_CONFIG_HASH_EXT, + }, + .num_ext = 5 + }, + [NON_TRUSTED_FW_KEY_CERT] = { + .id = NON_TRUSTED_FW_KEY_CERT, + .opt = "nt-fw-key-cert", + .help_msg = "Non-Trusted Firmware Key Certificate (output file)", + .fn = NULL, + .cn = "Non-Trusted Firmware Key Certificate", + .key = NON_TRUSTED_WORLD_KEY, + .issuer = NON_TRUSTED_FW_KEY_CERT, + .ext = { + NON_TRUSTED_FW_NVCOUNTER_EXT, + NON_TRUSTED_FW_CONTENT_CERT_PK_EXT + }, + .num_ext = 2 + }, + [NON_TRUSTED_FW_CONTENT_CERT] = { + .id = NON_TRUSTED_FW_CONTENT_CERT, + .opt = "nt-fw-cert", + .help_msg = "Non-Trusted Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "Non-Trusted Firmware Content Certificate", + .key = NON_TRUSTED_FW_CONTENT_CERT_KEY, + .issuer = NON_TRUSTED_FW_CONTENT_CERT, + .ext = { + NON_TRUSTED_FW_NVCOUNTER_EXT, + NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT, + NON_TRUSTED_FW_CONFIG_HASH_EXT, + }, + .num_ext = 3 + }, + [SIP_SECURE_PARTITION_CONTENT_CERT] = { + .id = SIP_SECURE_PARTITION_CONTENT_CERT, + .opt = "sip-sp-cert", + .help_msg = "SiP owned Secure Partition Content Certificate (output file)", + .fn = NULL, + .cn = "SiP owned Secure Partition Content Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = SIP_SECURE_PARTITION_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + SP_PKG1_HASH_EXT, + SP_PKG2_HASH_EXT, + SP_PKG3_HASH_EXT, + SP_PKG4_HASH_EXT, + SP_PKG5_HASH_EXT, + SP_PKG6_HASH_EXT, + SP_PKG7_HASH_EXT, + SP_PKG8_HASH_EXT, + }, + .num_ext = 9 + }, + [FWU_CERT] = { + .id = FWU_CERT, + .opt = "fwu-cert", + .help_msg = "Firmware Update Certificate (output file)", + .fn = NULL, + .cn = "Firmware Update Certificate", + .key = ROT_KEY, + .issuer = FWU_CERT, + .ext = { + SCP_FWU_CFG_HASH_EXT, + AP_FWU_CFG_HASH_EXT, + FWU_HASH_EXT + }, + .num_ext = 3 + } +}; + +REGISTER_COT(tbb_certs); diff --git a/tools/cert_create/src/tbbr/tbb_ext.c b/tools/cert_create/src/tbbr/tbb_ext.c new file mode 100644 index 0000000..60bafb4 --- /dev/null +++ b/tools/cert_create/src/tbbr/tbb_ext.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <string.h> +#include <openssl/err.h> +#include <openssl/x509v3.h> + +#if USE_TBBR_DEFS +#include <tbbr_oid.h> +#else +#include <platform_oid.h> +#endif + +#include "ext.h" +#include "tbbr/tbb_ext.h" +#include "tbbr/tbb_key.h" + +static ext_t tbb_ext[] = { + [TRUSTED_FW_NVCOUNTER_EXT] = { + .oid = TRUSTED_FW_NVCOUNTER_OID, + .opt = "tfw-nvctr", + .help_msg = "Trusted Firmware Non-Volatile counter value", + .sn = "TrustedWorldNVCounter", + .ln = "Trusted World Non-Volatile counter", + .asn1_type = V_ASN1_INTEGER, + .type = EXT_TYPE_NVCOUNTER, + .attr.nvctr_type = NVCTR_TYPE_TFW + }, + [NON_TRUSTED_FW_NVCOUNTER_EXT] = { + .oid = NON_TRUSTED_FW_NVCOUNTER_OID, + .opt = "ntfw-nvctr", + .help_msg = "Non-Trusted Firmware Non-Volatile counter value", + .sn = "NormalWorldNVCounter", + .ln = "Non-Trusted Firmware Non-Volatile counter", + .asn1_type = V_ASN1_INTEGER, + .type = EXT_TYPE_NVCOUNTER, + .attr.nvctr_type = NVCTR_TYPE_NTFW + }, + [TRUSTED_BOOT_FW_HASH_EXT] = { + .oid = TRUSTED_BOOT_FW_HASH_OID, + .opt = "tb-fw", + .help_msg = "Trusted Boot Firmware image file", + .sn = "TrustedBootFirmwareHash", + .ln = "Trusted Boot Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [TRUSTED_BOOT_FW_CONFIG_HASH_EXT] = { + .oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID, + .opt = "tb-fw-config", + .help_msg = "Trusted Boot Firmware Config file", + .sn = "TrustedBootFirmwareConfigHash", + .ln = "Trusted Boot Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [HW_CONFIG_HASH_EXT] = { + .oid = HW_CONFIG_HASH_OID, + .opt = "hw-config", + .help_msg = "HW Config file", + .sn = "HWConfigHash", + .ln = "HW Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [FW_CONFIG_HASH_EXT] = { + .oid = FW_CONFIG_HASH_OID, + .opt = "fw-config", + .help_msg = "Firmware Config file", + .sn = "FirmwareConfigHash", + .ln = "Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [TRUSTED_WORLD_PK_EXT] = { + .oid = TRUSTED_WORLD_PK_OID, + .sn = "TrustedWorldPublicKey", + .ln = "Trusted World Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = TRUSTED_WORLD_KEY + }, + [NON_TRUSTED_WORLD_PK_EXT] = { + .oid = NON_TRUSTED_WORLD_PK_OID, + .sn = "NonTrustedWorldPublicKey", + .ln = "Non-Trusted World Public Key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = NON_TRUSTED_WORLD_KEY + }, + [SCP_FW_CONTENT_CERT_PK_EXT] = { + .oid = SCP_FW_CONTENT_CERT_PK_OID, + .sn = "SCPFirmwareContentCertPK", + .ln = "SCP Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = SCP_FW_CONTENT_CERT_KEY + }, + [SCP_FW_HASH_EXT] = { + .oid = SCP_FW_HASH_OID, + .opt = "scp-fw", + .help_msg = "SCP Firmware image file", + .sn = "SCPFirmwareHash", + .ln = "SCP Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [SOC_FW_CONTENT_CERT_PK_EXT] = { + .oid = SOC_FW_CONTENT_CERT_PK_OID, + .sn = "SoCFirmwareContentCertPK", + .ln = "SoC Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = SOC_FW_CONTENT_CERT_KEY + }, + [SOC_AP_FW_HASH_EXT] = { + .oid = SOC_AP_FW_HASH_OID, + .opt = "soc-fw", + .help_msg = "SoC AP Firmware image file", + .sn = "SoCAPFirmwareHash", + .ln = "SoC AP Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [SOC_FW_CONFIG_HASH_EXT] = { + .oid = SOC_FW_CONFIG_HASH_OID, + .opt = "soc-fw-config", + .help_msg = "SoC Firmware Config file", + .sn = "SocFirmwareConfigHash", + .ln = "SoC Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [TRUSTED_OS_FW_CONTENT_CERT_PK_EXT] = { + .oid = TRUSTED_OS_FW_CONTENT_CERT_PK_OID, + .sn = "TrustedOSFirmwareContentCertPK", + .ln = "Trusted OS Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = TRUSTED_OS_FW_CONTENT_CERT_KEY + }, + [TRUSTED_OS_FW_HASH_EXT] = { + .oid = TRUSTED_OS_FW_HASH_OID, + .opt = "tos-fw", + .help_msg = "Trusted OS image file", + .sn = "TrustedOSHash", + .ln = "Trusted OS hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [TRUSTED_OS_FW_EXTRA1_HASH_EXT] = { + .oid = TRUSTED_OS_FW_EXTRA1_HASH_OID, + .opt = "tos-fw-extra1", + .help_msg = "Trusted OS Extra1 image file", + .sn = "TrustedOSExtra1Hash", + .ln = "Trusted OS Extra1 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [TRUSTED_OS_FW_EXTRA2_HASH_EXT] = { + .oid = TRUSTED_OS_FW_EXTRA2_HASH_OID, + .opt = "tos-fw-extra2", + .help_msg = "Trusted OS Extra2 image file", + .sn = "TrustedOSExtra2Hash", + .ln = "Trusted OS Extra2 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [TRUSTED_OS_FW_CONFIG_HASH_EXT] = { + .oid = TRUSTED_OS_FW_CONFIG_HASH_OID, + .opt = "tos-fw-config", + .help_msg = "Trusted OS Firmware Config file", + .sn = "TrustedOSFirmwareConfigHash", + .ln = "Trusted OS Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [NON_TRUSTED_FW_CONTENT_CERT_PK_EXT] = { + .oid = NON_TRUSTED_FW_CONTENT_CERT_PK_OID, + .sn = "NonTrustedFirmwareContentCertPK", + .ln = "Non-Trusted Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = NON_TRUSTED_FW_CONTENT_CERT_KEY + }, + [NON_TRUSTED_WORLD_BOOTLOADER_HASH_EXT] = { + .oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID, + .opt = "nt-fw", + .help_msg = "Non-Trusted World Bootloader image file", + .sn = "NonTrustedWorldBootloaderHash", + .ln = "Non-Trusted World hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [NON_TRUSTED_FW_CONFIG_HASH_EXT] = { + .oid = NON_TRUSTED_FW_CONFIG_HASH_OID, + .opt = "nt-fw-config", + .help_msg = "Non Trusted OS Firmware Config file", + .sn = "NonTrustedOSFirmwareConfigHash", + .ln = "Non-Trusted OS Firmware Config hash", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG1_HASH_EXT] = { + .oid = SP_PKG1_HASH_OID, + .opt = "sp-pkg1", + .help_msg = "Secure Partition Package1 file", + .sn = "SPPkg1Hash", + .ln = "SP Pkg1 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG2_HASH_EXT] = { + .oid = SP_PKG2_HASH_OID, + .opt = "sp-pkg2", + .help_msg = "Secure Partition Package2 file", + .sn = "SPPkg2Hash", + .ln = "SP Pkg2 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG3_HASH_EXT] = { + .oid = SP_PKG3_HASH_OID, + .opt = "sp-pkg3", + .help_msg = "Secure Partition Package3 file", + .sn = "SPPkg3Hash", + .ln = "SP Pkg3 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG4_HASH_EXT] = { + .oid = SP_PKG4_HASH_OID, + .opt = "sp-pkg4", + .help_msg = "Secure Partition Package4 file", + .sn = "SPPkg4Hash", + .ln = "SP Pkg4 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG5_HASH_EXT] = { + .oid = SP_PKG5_HASH_OID, + .opt = "sp-pkg5", + .help_msg = "Secure Partition Package5 file", + .sn = "SPPkg5Hash", + .ln = "SP Pkg5 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG6_HASH_EXT] = { + .oid = SP_PKG6_HASH_OID, + .opt = "sp-pkg6", + .help_msg = "Secure Partition Package6 file", + .sn = "SPPkg6Hash", + .ln = "SP Pkg6 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG7_HASH_EXT] = { + .oid = SP_PKG7_HASH_OID, + .opt = "sp-pkg7", + .help_msg = "Secure Partition Package7 file", + .sn = "SPPkg7Hash", + .ln = "SP Pkg7 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SP_PKG8_HASH_EXT] = { + .oid = SP_PKG8_HASH_OID, + .opt = "sp-pkg8", + .help_msg = "Secure Partition Package8 file", + .sn = "SPPkg8Hash", + .ln = "SP Pkg8 hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [SCP_FWU_CFG_HASH_EXT] = { + .oid = SCP_FWU_CFG_HASH_OID, + .opt = "scp-fwu-cfg", + .help_msg = "SCP Firmware Update Config image file", + .sn = "SCPFWUpdateConfig", + .ln = "SCP Firmware Update Config hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [AP_FWU_CFG_HASH_EXT] = { + .oid = AP_FWU_CFG_HASH_OID, + .opt = "ap-fwu-cfg", + .help_msg = "AP Firmware Update Config image file", + .sn = "APFWUpdateConfig", + .ln = "AP Firmware Update Config hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + }, + [FWU_HASH_EXT] = { + .oid = FWU_HASH_OID, + .opt = "fwu", + .help_msg = "Firmware Updater image file", + .sn = "FWUpdaterHash", + .ln = "Firmware Updater hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH, + .optional = 1 + } +}; + +REGISTER_EXTENSIONS(tbb_ext); diff --git a/tools/cert_create/src/tbbr/tbb_key.c b/tools/cert_create/src/tbbr/tbb_key.c new file mode 100644 index 0000000..5b84b6e --- /dev/null +++ b/tools/cert_create/src/tbbr/tbb_key.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "tbbr/tbb_key.h" + +/* + * Keys used to establish the chain of trust + * + * The order of the keys must follow the enumeration specified in tbb_key.h + */ +static key_t tbb_keys[] = { + [ROT_KEY] = { + .id = ROT_KEY, + .opt = "rot-key", + .help_msg = "Root Of Trust key file or PKCS11 URI", + .desc = "Root Of Trust key" + }, + [TRUSTED_WORLD_KEY] = { + .id = TRUSTED_WORLD_KEY, + .opt = "trusted-world-key", + .help_msg = "Trusted World key file or PKCS11 URI", + .desc = "Trusted World key" + }, + [NON_TRUSTED_WORLD_KEY] = { + .id = NON_TRUSTED_WORLD_KEY, + .opt = "non-trusted-world-key", + .help_msg = "Non Trusted World key file or PKCS11 URI", + .desc = "Non Trusted World key" + }, + [SCP_FW_CONTENT_CERT_KEY] = { + .id = SCP_FW_CONTENT_CERT_KEY, + .opt = "scp-fw-key", + .help_msg = "SCP Firmware Content Certificate key file or PKCS11 URI", + .desc = "SCP Firmware Content Certificate key" + }, + [SOC_FW_CONTENT_CERT_KEY] = { + .id = SOC_FW_CONTENT_CERT_KEY, + .opt = "soc-fw-key", + .help_msg = "SoC Firmware Content Certificate key file or PKCS11 URI", + .desc = "SoC Firmware Content Certificate key" + }, + [TRUSTED_OS_FW_CONTENT_CERT_KEY] = { + .id = TRUSTED_OS_FW_CONTENT_CERT_KEY, + .opt = "tos-fw-key", + .help_msg = "Trusted OS Firmware Content Certificate key file or PKCS11 URI", + .desc = "Trusted OS Firmware Content Certificate key" + }, + [NON_TRUSTED_FW_CONTENT_CERT_KEY] = { + .id = NON_TRUSTED_FW_CONTENT_CERT_KEY, + .opt = "nt-fw-key", + .help_msg = "Non Trusted Firmware Content Certificate key file or PKCS11 URI", + .desc = "Non Trusted Firmware Content Certificate key" + } +}; + +REGISTER_KEYS(tbb_keys); diff --git a/tools/cert_create/src/tbbr/tbbr.mk b/tools/cert_create/src/tbbr/tbbr.mk new file mode 100644 index 0000000..ee82d31 --- /dev/null +++ b/tools/cert_create/src/tbbr/tbbr.mk @@ -0,0 +1,29 @@ +# +# Copyright (c) 2020, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +USE_TBBR_DEFS := 1 +$(eval $(call add_define,USE_TBBR_DEFS)) + +ifeq (${USE_TBBR_DEFS},1) +# In this case, cert_tool is platform-independent +PLAT_MSG := TBBR Generic +PLAT_INCLUDE := ../../include/tools_share +else +PLAT_MSG := ${PLAT} + +TF_PLATFORM_ROOT := ../../plat/ +include ${MAKE_HELPERS_DIRECTORY}plat_helpers.mk + +PLAT_INCLUDE := $(wildcard ${PLAT_DIR}include) + +ifeq ($(PLAT_INCLUDE),) + $(error "Error: Invalid platform '${PLAT}' has no include directory.") +endif +endif + +OBJECTS += src/tbbr/tbb_cert.o \ + src/tbbr/tbb_ext.o \ + src/tbbr/tbb_key.o diff --git a/tools/conventional-changelog-tf-a/index.js b/tools/conventional-changelog-tf-a/index.js new file mode 100644 index 0000000..7d57c15 --- /dev/null +++ b/tools/conventional-changelog-tf-a/index.js @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2021-2023, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* eslint-env es6 */ + +"use strict"; + +const Handlebars = require("handlebars"); +const Q = require("q"); +const _ = require("lodash"); + +const ccConventionalChangelog = require("conventional-changelog-conventionalcommits/conventional-changelog"); +const ccParserOpts = require("conventional-changelog-conventionalcommits/parser-opts"); +const ccRecommendedBumpOpts = require("conventional-changelog-conventionalcommits/conventional-recommended-bump"); +const ccWriterOpts = require("conventional-changelog-conventionalcommits/writer-opts"); + +const execa = require("execa"); + +const readFileSync = require("fs").readFileSync; +const resolve = require("path").resolve; + +/* + * Register a Handlebars helper that lets us generate Markdown lists that can support multi-line + * strings. This is driven by inconsistent formatting of breaking changes, which may be multiple + * lines long and can terminate the list early unintentionally. + */ +Handlebars.registerHelper("tf-a-mdlist", function (indent, options) { + const spaces = new Array(indent + 1).join(" "); + const first = spaces + "- "; + const nth = spaces + " "; + + return first + options.fn(this).replace(/\n(?!\s*\n)/gm, `\n${nth}`).trim() + "\n"; +}); + +/* + * Register a Handlebars helper that concatenates multiple variables. We use this to generate the + * title for the section partials. + */ +Handlebars.registerHelper("tf-a-concat", function () { + let argv = Array.prototype.slice.call(arguments, 0); + + argv.pop(); + + return argv.join(""); +}); + +function writerOpts(config) { + /* + * Flatten the configuration's sections list. This helps us iterate over all of the sections + * when we don't care about the hierarchy. + */ + + const flattenSections = function (sections) { + return sections.flatMap(section => { + const subsections = flattenSections(section.sections || []); + + return [section].concat(subsections); + }) + }; + + const flattenedSections = flattenSections(config.sections); + + /* + * Register a helper to return a restructured version of the note groups that includes notes + * categorized by their section. + */ + Handlebars.registerHelper("tf-a-notes", function (noteGroups, options) { + const generateTemplateData = function (sections, notes) { + return (sections || []).flatMap(section => { + const templateData = { + title: section.title, + sections: generateTemplateData(section.sections, notes), + notes: notes.filter(note => section.scopes?.includes(note.commit.scope)), + }; + + /* + * Don't return a section if it contains no notes and no sub-sections. + */ + if ((templateData.sections.length == 0) && (templateData.notes.length == 0)) { + return []; + } + + return [templateData]; + }); + }; + + return noteGroups.map(noteGroup => { + return { + title: noteGroup.title, + sections: generateTemplateData(config.sections, noteGroup.notes), + notes: noteGroup.notes.filter(note => + !flattenedSections.some(section => section.scopes?.includes(note.commit.scope))), + }; + }); + }); + + /* + * Register a helper to return a restructured version of the commit groups that includes commits + * categorized by their section. + */ + Handlebars.registerHelper("tf-a-commits", function (commitGroups, options) { + const generateTemplateData = function (sections, commits) { + return (sections || []).flatMap(section => { + const templateData = { + title: section.title, + sections: generateTemplateData(section.sections, commits), + commits: commits.filter(commit => section.scopes?.includes(commit.scope)), + }; + + /* + * Don't return a section if it contains no notes and no sub-sections. + */ + if ((templateData.sections.length == 0) && (templateData.commits.length == 0)) { + return []; + } + + return [templateData]; + }); + }; + + return commitGroups.map(commitGroup => { + return { + title: commitGroup.title, + sections: generateTemplateData(config.sections, commitGroup.commits), + commits: commitGroup.commits.filter(commit => + !flattenedSections.some(section => section.scopes?.includes(commit.scope))), + }; + }); + }); + + const writerOpts = ccWriterOpts(config) + .then(writerOpts => { + const ccWriterOptsTransform = writerOpts.transform; + + /* + * These configuration properties can't be injected directly into the template because + * they themselves are templates. Instead, we register them as partials, which allows + * them to be evaluated as part of the templates they're used in. + */ + Handlebars.registerPartial("commitUrl", config.commitUrlFormat); + Handlebars.registerPartial("compareUrl", config.compareUrlFormat); + Handlebars.registerPartial("issueUrl", config.issueUrlFormat); + + /* + * Register the partials that allow us to recursively create changelog sections. + */ + + const notePartial = readFileSync(resolve(__dirname, "./templates/note.hbs"), "utf-8"); + const noteSectionPartial = readFileSync(resolve(__dirname, "./templates/note-section.hbs"), "utf-8"); + const commitSectionPartial = readFileSync(resolve(__dirname, "./templates/commit-section.hbs"), "utf-8"); + + Handlebars.registerPartial("tf-a-note", notePartial); + Handlebars.registerPartial("tf-a-note-section", noteSectionPartial); + Handlebars.registerPartial("tf-a-commit-section", commitSectionPartial); + + /* + * Override the base templates so that we can generate a changelog that looks at least + * similar to the pre-Conventional Commits TF-A changelog. + */ + writerOpts.mainTemplate = readFileSync(resolve(__dirname, "./templates/template.hbs"), "utf-8"); + writerOpts.headerPartial = readFileSync(resolve(__dirname, "./templates/header.hbs"), "utf-8"); + writerOpts.commitPartial = readFileSync(resolve(__dirname, "./templates/commit.hbs"), "utf-8"); + writerOpts.footerPartial = readFileSync(resolve(__dirname, "./templates/footer.hbs"), "utf-8"); + + writerOpts.transform = function (commit, context) { + /* + * Feedback on the generated changelog has shown that having build system changes + * appear at the top of a section throws some people off. We make an exception for + * scopeless `build`-type changes and treat them as though they actually have the + * `build` scope. + */ + + if ((commit.type === "build") && (commit.scope == null)) { + commit.scope = "build"; + } + + /* + * Fix up commit trailers, which for some reason are not correctly recognized and + * end up showing up in the breaking changes. + */ + + commit.notes.forEach(note => { + const trailers = execa.sync("git", ["interpret-trailers", "--parse"], { + input: note.text + }).stdout; + + note.text = note.text.replace(trailers, "").trim(); + }); + + return ccWriterOptsTransform(commit, context); + }; + + return writerOpts; + }); + + return writerOpts; +} + +module.exports = function (parameter) { + const config = parameter || {}; + + return Q.all([ + ccConventionalChangelog(config), + ccParserOpts(config), + ccRecommendedBumpOpts(config), + writerOpts(config) + ]).spread(( + conventionalChangelog, + parserOpts, + recommendedBumpOpts, + writerOpts + ) => { + if (_.isFunction(parameter)) { + return parameter(null, { + gitRawCommitsOpts: { noMerges: null }, + conventionalChangelog, + parserOpts, + recommendedBumpOpts, + writerOpts + }); + } else { + return { + conventionalChangelog, + parserOpts, + recommendedBumpOpts, + writerOpts + }; + } + }); +}; diff --git a/tools/conventional-changelog-tf-a/package.json b/tools/conventional-changelog-tf-a/package.json new file mode 100644 index 0000000..d0efab8 --- /dev/null +++ b/tools/conventional-changelog-tf-a/package.json @@ -0,0 +1,13 @@ +{ + "name": "conventional-changelog-tf-a", + "version": "2.10.0", + "license": "BSD-3-Clause", + "private": true, + "main": "index.js", + "dependencies": { + "conventional-changelog-conventionalcommits": "^4.6.1", + "execa": "^5.1.1", + "lodash": "^4.17.21", + "q": "^1.5.1" + } +} diff --git a/tools/conventional-changelog-tf-a/templates/commit-section.hbs b/tools/conventional-changelog-tf-a/templates/commit-section.hbs new file mode 100644 index 0000000..86b3335 --- /dev/null +++ b/tools/conventional-changelog-tf-a/templates/commit-section.hbs @@ -0,0 +1,17 @@ +{{#if title ~}} +{{ header }} + +{{#if commits.length ~}} + {{#each commits ~}} + {{#tf-a-mdlist 0}}{{> commit root=@root showScope=../topLevel }}{{/tf-a-mdlist ~}} + {{/each}} + +{{/if ~}} + +{{#if sections.length ~}} + {{#each sections ~}} + {{#tf-a-mdlist 0}}{{> tf-a-commit-section root=@root header=(tf-a-concat "**" title "**") }}{{/tf-a-mdlist}} + {{/each}} +{{/if ~}} + +{{/if}} diff --git a/tools/conventional-changelog-tf-a/templates/commit.hbs b/tools/conventional-changelog-tf-a/templates/commit.hbs new file mode 100644 index 0000000..faf264a --- /dev/null +++ b/tools/conventional-changelog-tf-a/templates/commit.hbs @@ -0,0 +1,15 @@ +{{#if scope }} + {{~#if showScope }}**{{ scope }}:** {{/if}} +{{~/if}} + +{{~#if subject }} + {{~ subject }} +{{~else}} + {{~ header }} +{{~/if}} + +{{~#if hash }} {{#if @root.linkReferences ~}} + ([{{ shortHash }}]({{> commitUrl root=@root }})) +{{~else}} + {{~ shortHash }} +{{~/if}}{{~/if}} diff --git a/tools/conventional-changelog-tf-a/templates/footer.hbs b/tools/conventional-changelog-tf-a/templates/footer.hbs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/conventional-changelog-tf-a/templates/footer.hbs diff --git a/tools/conventional-changelog-tf-a/templates/header.hbs b/tools/conventional-changelog-tf-a/templates/header.hbs new file mode 100644 index 0000000..67cb297 --- /dev/null +++ b/tools/conventional-changelog-tf-a/templates/header.hbs @@ -0,0 +1,13 @@ +{{#if isPatch~}} + ### +{{~else~}} + ## +{{~/if}} {{#if @root.linkCompare~}} + [{{version}}]({{> compareUrl root=@root}}) +{{~else}} + {{~version}} +{{~/if}} +{{~#if title}} "{{title}}" +{{~/if}} +{{~#if date}} ({{date}}) +{{/if}} diff --git a/tools/conventional-changelog-tf-a/templates/note-section.hbs b/tools/conventional-changelog-tf-a/templates/note-section.hbs new file mode 100644 index 0000000..f501c96 --- /dev/null +++ b/tools/conventional-changelog-tf-a/templates/note-section.hbs @@ -0,0 +1,13 @@ +{{ header }} + +{{#if notes.length ~}} + {{#each notes ~}} + {{#tf-a-mdlist 0}}{{> tf-a-note root=@root showScope=../topLevel }}{{/tf-a-mdlist}} + {{/each ~}} +{{/if ~}} + +{{#if sections.length ~}} + {{#each sections ~}} + {{#tf-a-mdlist 0}}{{> tf-a-note-section root=@root header=(tf-a-concat "**" title "**") }}{{/tf-a-mdlist}} + {{/each~}} +{{/if}} diff --git a/tools/conventional-changelog-tf-a/templates/note.hbs b/tools/conventional-changelog-tf-a/templates/note.hbs new file mode 100644 index 0000000..c780ee8 --- /dev/null +++ b/tools/conventional-changelog-tf-a/templates/note.hbs @@ -0,0 +1,3 @@ +{{ text }} + +**See:** {{#with commit }}{{> commit root=@root showScope=../showScope }}{{/with}} diff --git a/tools/conventional-changelog-tf-a/templates/template.hbs b/tools/conventional-changelog-tf-a/templates/template.hbs new file mode 100644 index 0000000..95fb68c --- /dev/null +++ b/tools/conventional-changelog-tf-a/templates/template.hbs @@ -0,0 +1,9 @@ +{{> header }} + +{{#each (tf-a-notes noteGroups) ~}} +{{> tf-a-note-section root=@root header=(tf-a-concat "### âš " title) topLevel=true }} +{{/each ~}} + +{{#each (tf-a-commits commitGroups) ~}} +{{> tf-a-commit-section root=@root header=(tf-a-concat "### " title) topLevel=true }} +{{/each ~}} diff --git a/tools/encrypt_fw/Makefile b/tools/encrypt_fw/Makefile new file mode 100644 index 0000000..924e5fe --- /dev/null +++ b/tools/encrypt_fw/Makefile @@ -0,0 +1,89 @@ +# +# Copyright (c) 2019-2022, Linaro Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +V ?= 0 +BUILD_INFO ?= 1 +DEBUG := 0 +ENCTOOL ?= encrypt_fw${BIN_EXT} +BINARY := $(notdir ${ENCTOOL}) +OPENSSL_DIR := /usr + + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}defaults.mk + +OBJECTS := src/encrypt.o \ + src/cmd_opt.o \ + src/main.o + +HOSTCCFLAGS := -Wall -std=c99 + +# Select OpenSSL version flag according to the OpenSSL build selected +# from setting the OPENSSL_DIR path. +$(eval $(call SELECT_OPENSSL_API_VERSION)) + +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG -DLOG_LEVEL=40 +else +ifeq (${BUILD_INFO},1) + HOSTCCFLAGS += -O2 -DLOG_LEVEL=20 +else + HOSTCCFLAGS += -O2 -DLOG_LEVEL=10 +endif +endif +ifeq (${V},0) + Q := @ +else + Q := +endif + +HOSTCCFLAGS += ${DEFINES} +# USING_OPENSSL3 flag will be added to the HOSTCCFLAGS variable with the proper +# computed value. +HOSTCCFLAGS += -DUSING_OPENSSL3=$(USING_OPENSSL3) + + +# Make soft links and include from local directory otherwise wrong headers +# could get pulled in from firmware tree. +INC_DIR := -I ./include -I ../../include/tools_share -I ${OPENSSL_DIR}/include + +# Include library directories where OpenSSL library files are located. +# For a normal installation (i.e.: when ${OPENSSL_DIR} = /usr or +# /usr/local), binaries are located under the ${OPENSSL_DIR}/lib/ +# directory. However, for a local build of OpenSSL, the built binaries are +# located under the main project directory (i.e.: ${OPENSSL_DIR}, not +# ${OPENSSL_DIR}/lib/). +LIB_DIR := -L ${OPENSSL_DIR}/lib -L ${OPENSSL_DIR} +LIB := -lssl -lcrypto + +HOSTCC ?= gcc + +.PHONY: all clean realclean --openssl + +all: --openssl ${BINARY} + +${BINARY}: ${OBJECTS} Makefile + @echo " HOSTLD $@" + @echo 'const char build_msg[] = "Built : "__TIME__", "__DATE__;' | \ + ${HOSTCC} -c ${HOSTCCFLAGS} -xc - -o src/build_msg.o + ${Q}${HOSTCC} src/build_msg.o ${OBJECTS} ${LIB_DIR} ${LIB} -o $@ + +%.o: %.c + @echo " HOSTCC $<" + ${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ + +--openssl: +ifeq ($(DEBUG),1) + @echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" +endif + +clean: + $(call SHELL_DELETE_ALL, src/build_msg.o ${OBJECTS}) + +realclean: clean + $(call SHELL_DELETE,${BINARY}) diff --git a/tools/encrypt_fw/include/cmd_opt.h b/tools/encrypt_fw/include/cmd_opt.h new file mode 100644 index 0000000..bd7d31f --- /dev/null +++ b/tools/encrypt_fw/include/cmd_opt.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019, Linaro Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CMD_OPT_H +#define CMD_OPT_H + +#include <getopt.h> + +#define CMD_OPT_MAX_NUM 64 + +/* Supported long command line option types */ +enum { + CMD_OPT_FW +}; + +/* Structure to define a command line option */ +typedef struct cmd_opt_s { + struct option long_opt; + const char *help_msg; +} cmd_opt_t; + +/* Exported API*/ +void cmd_opt_add(const cmd_opt_t *cmd_opt); +const struct option *cmd_opt_get_array(void); +const char *cmd_opt_get_name(int idx); +const char *cmd_opt_get_help_msg(int idx); + +#endif /* CMD_OPT_H */ diff --git a/tools/encrypt_fw/include/debug.h b/tools/encrypt_fw/include/debug.h new file mode 100644 index 0000000..ee8f1f5 --- /dev/null +++ b/tools/encrypt_fw/include/debug.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include <stdio.h> + +/* The log output macros print output to the console. These macros produce + * compiled log output only if the LOG_LEVEL defined in the makefile (or the + * make command line) is greater or equal than the level required for that + * type of log output. + * The format expected is the same as for printf(). For example: + * INFO("Info %s.\n", "message") -> INFO: Info message. + * WARN("Warning %s.\n", "message") -> WARNING: Warning message. + */ + +#define LOG_LEVEL_NONE 0 +#define LOG_LEVEL_ERROR 10 +#define LOG_LEVEL_NOTICE 20 +#define LOG_LEVEL_WARNING 30 +#define LOG_LEVEL_INFO 40 +#define LOG_LEVEL_VERBOSE 50 + + +#if LOG_LEVEL >= LOG_LEVEL_NOTICE +# define NOTICE(...) printf("NOTICE: " __VA_ARGS__) +#else +# define NOTICE(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_ERROR +# define ERROR(...) printf("ERROR: " __VA_ARGS__) +#else +# define ERROR(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_WARNING +# define WARN(...) printf("WARNING: " __VA_ARGS__) +#else +# define WARN(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_INFO +# define INFO(...) printf("INFO: " __VA_ARGS__) +#else +# define INFO(...) +#endif + +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE +# define VERBOSE(...) printf("VERBOSE: " __VA_ARGS__) +#else +# define VERBOSE(...) +#endif + +#endif /* DEBUG_H */ diff --git a/tools/encrypt_fw/include/encrypt.h b/tools/encrypt_fw/include/encrypt.h new file mode 100644 index 0000000..25d3011 --- /dev/null +++ b/tools/encrypt_fw/include/encrypt.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, Linaro Limited. All rights reserved. + * Author: Sumit Garg <sumit.garg@linaro.org> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ENCRYPT_H +#define ENCRYPT_H + +/* Supported key algorithms */ +enum { + KEY_ALG_GCM /* AES-GCM (default) */ +}; + +int encrypt_file(unsigned short fw_enc_status, int enc_alg, char *key_string, + char *nonce_string, const char *ip_name, const char *op_name); + +#endif /* ENCRYPT_H */ diff --git a/tools/encrypt_fw/src/cmd_opt.c b/tools/encrypt_fw/src/cmd_opt.c new file mode 100644 index 0000000..64180d1 --- /dev/null +++ b/tools/encrypt_fw/src/cmd_opt.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <cmd_opt.h> +#include <getopt.h> +#include <stddef.h> +#include <stdlib.h> +#include "debug.h" + +/* Command line options */ +static struct option long_opt[CMD_OPT_MAX_NUM+1]; +static const char *help_msg[CMD_OPT_MAX_NUM+1]; +static int num_reg_opt; + +void cmd_opt_add(const cmd_opt_t *cmd_opt) +{ + assert(cmd_opt != NULL); + + if (num_reg_opt >= CMD_OPT_MAX_NUM) { + ERROR("Out of memory. Please increase CMD_OPT_MAX_NUM\n"); + exit(1); + } + + long_opt[num_reg_opt].name = cmd_opt->long_opt.name; + long_opt[num_reg_opt].has_arg = cmd_opt->long_opt.has_arg; + long_opt[num_reg_opt].flag = 0; + long_opt[num_reg_opt].val = cmd_opt->long_opt.val; + + help_msg[num_reg_opt] = cmd_opt->help_msg; + + num_reg_opt++; +} + +const struct option *cmd_opt_get_array(void) +{ + return long_opt; +} + +const char *cmd_opt_get_name(int idx) +{ + if (idx >= num_reg_opt) { + return NULL; + } + + return long_opt[idx].name; +} + +const char *cmd_opt_get_help_msg(int idx) +{ + if (idx >= num_reg_opt) { + return NULL; + } + + return help_msg[idx]; +} diff --git a/tools/encrypt_fw/src/encrypt.c b/tools/encrypt_fw/src/encrypt.c new file mode 100644 index 0000000..18a514c --- /dev/null +++ b/tools/encrypt_fw/src/encrypt.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2019, Linaro Limited. All rights reserved. + * Author: Sumit Garg <sumit.garg@linaro.org> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <firmware_encrypted.h> +#include <openssl/evp.h> +#include <stdio.h> +#include <string.h> +#include "debug.h" +#include "encrypt.h" + +#define BUFFER_SIZE 256 +#define IV_SIZE 12 +#define IV_STRING_SIZE 24 +#define TAG_SIZE 16 +#define KEY_SIZE 32 +#define KEY_STRING_SIZE 64 + +static int gcm_encrypt(unsigned short fw_enc_status, char *key_string, + char *nonce_string, const char *ip_name, + const char *op_name) +{ + FILE *ip_file; + FILE *op_file; + EVP_CIPHER_CTX *ctx; + unsigned char data[BUFFER_SIZE], enc_data[BUFFER_SIZE]; + unsigned char key[KEY_SIZE], iv[IV_SIZE], tag[TAG_SIZE]; + int bytes, enc_len = 0, i, j, ret = 0; + struct fw_enc_hdr header; + + memset(&header, 0, sizeof(struct fw_enc_hdr)); + + if (strlen(key_string) != KEY_STRING_SIZE) { + ERROR("Unsupported key size: %lu\n", strlen(key_string)); + return -1; + } + + for (i = 0, j = 0; i < KEY_SIZE; i++, j += 2) { + if (sscanf(&key_string[j], "%02hhx", &key[i]) != 1) { + ERROR("Incorrect key format\n"); + return -1; + } + } + + if (strlen(nonce_string) != IV_STRING_SIZE) { + ERROR("Unsupported IV size: %lu\n", strlen(nonce_string)); + return -1; + } + + for (i = 0, j = 0; i < IV_SIZE; i++, j += 2) { + if (sscanf(&nonce_string[j], "%02hhx", &iv[i]) != 1) { + ERROR("Incorrect IV format\n"); + return -1; + } + } + + ip_file = fopen(ip_name, "rb"); + if (ip_file == NULL) { + ERROR("Cannot read %s\n", ip_name); + return -1; + } + + op_file = fopen(op_name, "wb"); + if (op_file == NULL) { + ERROR("Cannot write %s\n", op_name); + fclose(ip_file); + return -1; + } + + ret = fseek(op_file, sizeof(struct fw_enc_hdr), SEEK_SET); + if (ret) { + ERROR("fseek failed\n"); + goto out_file; + } + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + ERROR("EVP_CIPHER_CTX_new failed\n"); + ret = -1; + goto out_file; + } + + ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL); + if (ret != 1) { + ERROR("EVP_EncryptInit_ex failed\n"); + ret = -1; + goto out; + } + + ret = EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv); + if (ret != 1) { + ERROR("EVP_EncryptInit_ex failed\n"); + goto out; + } + + while ((bytes = fread(data, 1, BUFFER_SIZE, ip_file)) != 0) { + ret = EVP_EncryptUpdate(ctx, enc_data, &enc_len, data, bytes); + if (ret != 1) { + ERROR("EVP_EncryptUpdate failed\n"); + ret = -1; + goto out; + } + + fwrite(enc_data, 1, enc_len, op_file); + } + + ret = EVP_EncryptFinal_ex(ctx, enc_data, &enc_len); + if (ret != 1) { + ERROR("EVP_EncryptFinal_ex failed\n"); + ret = -1; + goto out; + } + + ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAG_SIZE, tag); + if (ret != 1) { + ERROR("EVP_CIPHER_CTX_ctrl failed\n"); + ret = -1; + goto out; + } + + header.magic = ENC_HEADER_MAGIC; + header.flags |= fw_enc_status & FW_ENC_STATUS_FLAG_MASK; + header.dec_algo = KEY_ALG_GCM; + header.iv_len = IV_SIZE; + header.tag_len = TAG_SIZE; + memcpy(header.iv, iv, IV_SIZE); + memcpy(header.tag, tag, TAG_SIZE); + + ret = fseek(op_file, 0, SEEK_SET); + if (ret) { + ERROR("fseek failed\n"); + goto out; + } + + fwrite(&header, 1, sizeof(struct fw_enc_hdr), op_file); + +out: + EVP_CIPHER_CTX_free(ctx); + +out_file: + fclose(ip_file); + fclose(op_file); + + /* + * EVP_* APIs returns 1 as success but enctool considers + * 0 as success. + */ + if (ret == 1) + ret = 0; + + return ret; +} + +int encrypt_file(unsigned short fw_enc_status, int enc_alg, char *key_string, + char *nonce_string, const char *ip_name, const char *op_name) +{ + switch (enc_alg) { + case KEY_ALG_GCM: + return gcm_encrypt(fw_enc_status, key_string, nonce_string, + ip_name, op_name); + default: + return -1; + } +} diff --git a/tools/encrypt_fw/src/main.c b/tools/encrypt_fw/src/main.c new file mode 100644 index 0000000..39b7af7 --- /dev/null +++ b/tools/encrypt_fw/src/main.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2019, Linaro Limited. All rights reserved. + * Author: Sumit Garg <sumit.garg@linaro.org> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include <openssl/conf.h> + +#include "cmd_opt.h" +#include "debug.h" +#include "encrypt.h" +#include "firmware_encrypted.h" + +#define NUM_ELEM(x) ((sizeof(x)) / (sizeof(x[0]))) +#define HELP_OPT_MAX_LEN 128 + +/* Global options */ + +/* Info messages created in the Makefile */ +extern const char build_msg[]; + +static char *key_algs_str[] = { + [KEY_ALG_GCM] = "gcm", +}; + +static void print_help(const char *cmd, const struct option *long_opt) +{ + int rem, i = 0; + const struct option *opt; + char line[HELP_OPT_MAX_LEN]; + char *p; + + assert(cmd != NULL); + assert(long_opt != NULL); + + printf("\n\n"); + printf("The firmware encryption tool loads the binary image and\n" + "outputs encrypted binary image using an encryption key\n" + "provided as an input hex string.\n"); + printf("\n"); + printf("Usage:\n"); + printf("\t%s [OPTIONS]\n\n", cmd); + + printf("Available options:\n"); + opt = long_opt; + while (opt->name) { + p = line; + rem = HELP_OPT_MAX_LEN; + if (isalpha(opt->val)) { + /* Short format */ + sprintf(p, "-%c,", (char)opt->val); + p += 3; + rem -= 3; + } + snprintf(p, rem, "--%s %s", opt->name, + (opt->has_arg == required_argument) ? "<arg>" : ""); + printf("\t%-32s %s\n", line, cmd_opt_get_help_msg(i)); + opt++; + i++; + } + printf("\n"); +} + +static int get_key_alg(const char *key_alg_str) +{ + int i; + + for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) { + if (strcmp(key_alg_str, key_algs_str[i]) == 0) { + return i; + } + } + + return -1; +} + +static void parse_fw_enc_status_flag(const char *arg, + unsigned short *fw_enc_status) +{ + unsigned long flag; + char *endptr; + + flag = strtoul(arg, &endptr, 16); + if (*endptr != '\0' || flag > FW_ENC_WITH_BSSK) { + ERROR("Invalid fw_enc_status flag '%s'\n", arg); + exit(1); + } + + *fw_enc_status = flag & FW_ENC_STATUS_FLAG_MASK; +} + +/* Common command line options */ +static const cmd_opt_t common_cmd_opt[] = { + { + { "help", no_argument, NULL, 'h' }, + "Print this message and exit" + }, + { + { "fw-enc-status", required_argument, NULL, 'f' }, + "Firmware encryption status flag (with SSK=0 or BSSK=1)." + }, + { + { "key-alg", required_argument, NULL, 'a' }, + "Encryption key algorithm: 'gcm' (default)" + }, + { + { "key", required_argument, NULL, 'k' }, + "Encryption key (for supported algorithm)." + }, + { + { "nonce", required_argument, NULL, 'n' }, + "Nonce or Initialization Vector (for supported algorithm)." + }, + { + { "in", required_argument, NULL, 'i' }, + "Input filename to be encrypted." + }, + { + { "out", required_argument, NULL, 'o' }, + "Encrypted output filename." + }, +}; + +int main(int argc, char *argv[]) +{ + int i, key_alg, ret; + int c, opt_idx = 0; + const struct option *cmd_opt; + char *key = NULL; + char *nonce = NULL; + char *in_fn = NULL; + char *out_fn = NULL; + unsigned short fw_enc_status = 0; + + NOTICE("Firmware Encryption Tool: %s\n", build_msg); + + /* Set default options */ + key_alg = KEY_ALG_GCM; + + /* Add common command line options */ + for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) { + cmd_opt_add(&common_cmd_opt[i]); + } + + /* Get the command line options populated during the initialization */ + cmd_opt = cmd_opt_get_array(); + + while (1) { + /* getopt_long stores the option index here. */ + c = getopt_long(argc, argv, "a:f:hi:k:n:o:", cmd_opt, &opt_idx); + + /* Detect the end of the options. */ + if (c == -1) { + break; + } + + switch (c) { + case 'a': + key_alg = get_key_alg(optarg); + if (key_alg < 0) { + ERROR("Invalid key algorithm '%s'\n", optarg); + exit(1); + } + break; + case 'f': + parse_fw_enc_status_flag(optarg, &fw_enc_status); + break; + case 'k': + key = optarg; + break; + case 'i': + in_fn = optarg; + break; + case 'o': + out_fn = optarg; + break; + case 'n': + nonce = optarg; + break; + case 'h': + print_help(argv[0], cmd_opt); + exit(0); + case '?': + default: + print_help(argv[0], cmd_opt); + exit(1); + } + } + + if (!key) { + ERROR("Key must not be NULL\n"); + exit(1); + } + + if (!nonce) { + ERROR("Nonce must not be NULL\n"); + exit(1); + } + + if (!in_fn) { + ERROR("Input filename must not be NULL\n"); + exit(1); + } + + if (!out_fn) { + ERROR("Output filename must not be NULL\n"); + exit(1); + } + + ret = encrypt_file(fw_enc_status, key_alg, key, nonce, in_fn, out_fn); + + CRYPTO_cleanup_all_ex_data(); + + return ret; +} diff --git a/tools/fiptool/Makefile b/tools/fiptool/Makefile new file mode 100644 index 0000000..fda7c77 --- /dev/null +++ b/tools/fiptool/Makefile @@ -0,0 +1,103 @@ +# +# Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk +include ${MAKE_HELPERS_DIRECTORY}defaults.mk + +FIPTOOL ?= fiptool${BIN_EXT} +PROJECT := $(notdir ${FIPTOOL}) +OBJECTS := fiptool.o tbbr_config.o +V ?= 0 +STATIC ?= 0 + +override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 +HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG +else + HOSTCCFLAGS += -O2 +endif + +INCLUDE_PATHS := -I../../include/tools_share + +DEFINES += -DSTATIC=$(STATIC) + +ifeq (${STATIC},1) +LDOPTS := -static +else +OPENSSL_DIR := /usr + +# Select OpenSSL version flag according to the OpenSSL build selected +# from setting the OPENSSL_DIR path. +$(eval $(call SELECT_OPENSSL_API_VERSION)) + +# USING_OPENSSL3 flag will be added to the HOSTCCFLAGS variable with the proper +# computed value. +DEFINES += -DUSING_OPENSSL3=$(USING_OPENSSL3) + +# Include library directories where OpenSSL library files are located. +# For a normal installation (i.e.: when ${OPENSSL_DIR} = /usr or +# /usr/local), binaries are located under the ${OPENSSL_DIR}/lib/ +# directory. However, for a local build of OpenSSL, the built binaries are +# located under the main project directory (i.e.: ${OPENSSL_DIR}, not +# ${OPENSSL_DIR}/lib/). +LDOPTS := -L${OPENSSL_DIR}/lib -L${OPENSSL_DIR} -lcrypto +INCLUDE_PATHS += -I${OPENSSL_DIR}/include +endif # STATIC + +HOSTCCFLAGS += ${DEFINES} + +ifeq (${V},0) + Q := @ +else + Q := +endif + +HOSTCC ?= gcc + +ifneq (${PLAT},) +TF_PLATFORM_ROOT := ../../plat/ +include ${MAKE_HELPERS_DIRECTORY}plat_helpers.mk +COMBINED_PATH_FRAG := plat_fiptool/ +PLAT_FIPTOOL_HELPER_MK := $(foreach path_frag,$(subst /, ,$(patsubst ../../plat/%/,%,${PLAT_DIR})),\ + $(eval COMBINED_PATH_FRAG := ${COMBINED_PATH_FRAG}/${path_frag})\ + $(wildcard ${COMBINED_PATH_FRAG}/plat_fiptool.mk)) +endif + +ifneq (,$(wildcard $(lastword ${PLAT_FIPTOOL_HELPER_MK}))) +include ${PLAT_FIPTOOL_HELPER_MK} +endif + +DEPS := $(patsubst %.o,%.d,$(OBJECTS)) + +.PHONY: all clean distclean --openssl + +all: --openssl ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + @echo " HOSTLD $@" + ${Q}${HOSTCC} ${OBJECTS} -o $@ $(LDOPTS) + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} + +%.o: %.c Makefile + @echo " HOSTCC $<" + ${Q}${HOSTCC} -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} -MD -MP $< -o $@ + +-include $(DEPS) + +--openssl: +ifeq ($(STATIC),0) +ifeq ($(DEBUG),1) + @echo "Selected OpenSSL version: ${OPENSSL_CURRENT_VER}" +endif +endif # STATIC + +clean: + $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS} $(DEPS)) diff --git a/tools/fiptool/Makefile.msvc b/tools/fiptool/Makefile.msvc new file mode 100644 index 0000000..9081bc6 --- /dev/null +++ b/tools/fiptool/Makefile.msvc @@ -0,0 +1,37 @@ +# +# Copyright (c) 2019-2020, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +CC = cl.exe +LD = link.exe + +# FIPTOOLPATH and FIPTOOL are passed from the main makefile. + +OBJECTS = $(FIPTOOLPATH)\fiptool.obj \ + $(FIPTOOLPATH)\tbbr_config.obj \ + $(FIPTOOLPATH)\win_posix.obj + +INC = -I$(FIPTOOLPATH) -Iinclude\tools_share + +CFLAGS = $(CFLAGS) /nologo /Za /Zi /c /O2 /MT + +all: $(FIPTOOL) + +$(FIPTOOL): $(OBJECTS) + $(LD) /nologo /INCREMENTAL:NO /debug /nodefaultlib:libc.lib /out:$@ $(LIBS) $** + +.PHONY: clean realclean + +clean: + -@del /f /q $(OBJECTS) > nul + -@del /f /q $(FIPTOOLPATH)\*.pdb > nul + +realclean: + -@del /f /q $(OBJECTS) > nul + -@del /f /q $(FIPTOOLPATH)\*.pdb > nul + -@del /f /q $(FIPTOOL) > nul + +.c.obj: + $(CC) -c $(CFLAGS) $(INC) $< -Fo$@ diff --git a/tools/fiptool/fiptool.c b/tools/fiptool/fiptool.c new file mode 100644 index 0000000..6c566ef --- /dev/null +++ b/tools/fiptool/fiptool.c @@ -0,0 +1,1288 @@ +/* + * Copyright (c) 2016-2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _MSC_VER +#include <sys/mount.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "fiptool.h" +#include "tbbr_config.h" + +#define OPT_TOC_ENTRY 0 +#define OPT_PLAT_TOC_FLAGS 1 +#define OPT_ALIGN 2 + +static int info_cmd(int argc, char *argv[]); +static void info_usage(int); +static int create_cmd(int argc, char *argv[]); +static void create_usage(int); +static int update_cmd(int argc, char *argv[]); +static void update_usage(int); +static int unpack_cmd(int argc, char *argv[]); +static void unpack_usage(int); +static int remove_cmd(int argc, char *argv[]); +static void remove_usage(int); +static int version_cmd(int argc, char *argv[]); +static void version_usage(int); +static int help_cmd(int argc, char *argv[]); +static void usage(void); + +/* Available subcommands. */ +static cmd_t cmds[] = { + { .name = "info", .handler = info_cmd, .usage = info_usage }, + { .name = "create", .handler = create_cmd, .usage = create_usage }, + { .name = "update", .handler = update_cmd, .usage = update_usage }, + { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage }, + { .name = "remove", .handler = remove_cmd, .usage = remove_usage }, + { .name = "version", .handler = version_cmd, .usage = version_usage }, + { .name = "help", .handler = help_cmd, .usage = NULL }, +}; + +static image_desc_t *image_desc_head; +static size_t nr_image_descs; +static const uuid_t uuid_null; +static int verbose; + +static void vlog(int prio, const char *msg, va_list ap) +{ + char *prefix[] = { "DEBUG", "WARN", "ERROR" }; + + fprintf(stderr, "%s: ", prefix[prio]); + vfprintf(stderr, msg, ap); + fputc('\n', stderr); +} + +static void log_dbgx(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + vlog(LOG_DBG, msg, ap); + va_end(ap); +} + +static void log_warnx(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + vlog(LOG_WARN, msg, ap); + va_end(ap); +} + +static void log_err(const char *msg, ...) +{ + char buf[512]; + va_list ap; + + va_start(ap, msg); + snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno)); + vlog(LOG_ERR, buf, ap); + va_end(ap); + exit(1); +} + +static void log_errx(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + vlog(LOG_ERR, msg, ap); + va_end(ap); + exit(1); +} + +static char *xstrdup(const char *s, const char *msg) +{ + char *d; + + d = strdup(s); + if (d == NULL) + log_errx("strdup: %s", msg); + return d; +} + +static void *xmalloc(size_t size, const char *msg) +{ + void *d; + + d = malloc(size); + if (d == NULL) + log_errx("malloc: %s", msg); + return d; +} + +static void *xzalloc(size_t size, const char *msg) +{ + return memset(xmalloc(size, msg), 0, size); +} + +static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename) +{ + if (fwrite(buf, 1, size, fp) != size) + log_errx("Failed to write %s", filename); +} + +static image_desc_t *new_image_desc(const uuid_t *uuid, + const char *name, const char *cmdline_name) +{ + image_desc_t *desc; + + desc = xzalloc(sizeof(*desc), + "failed to allocate memory for image descriptor"); + memcpy(&desc->uuid, uuid, sizeof(uuid_t)); + desc->name = xstrdup(name, + "failed to allocate memory for image name"); + desc->cmdline_name = xstrdup(cmdline_name, + "failed to allocate memory for image command line name"); + desc->action = DO_UNSPEC; + return desc; +} + +static void set_image_desc_action(image_desc_t *desc, int action, + const char *arg) +{ + assert(desc != NULL); + + if (desc->action_arg != (char *)DO_UNSPEC) + free(desc->action_arg); + desc->action = action; + desc->action_arg = NULL; + if (arg != NULL) + desc->action_arg = xstrdup(arg, + "failed to allocate memory for argument"); +} + +static void free_image_desc(image_desc_t *desc) +{ + free(desc->name); + free(desc->cmdline_name); + free(desc->action_arg); + if (desc->image) { + free(desc->image->buffer); + free(desc->image); + } + free(desc); +} + +static void add_image_desc(image_desc_t *desc) +{ + image_desc_t **p = &image_desc_head; + + while (*p) + p = &(*p)->next; + + assert(*p == NULL); + *p = desc; + nr_image_descs++; +} + +static void free_image_descs(void) +{ + image_desc_t *desc = image_desc_head, *tmp; + + while (desc != NULL) { + tmp = desc->next; + free_image_desc(desc); + desc = tmp; + nr_image_descs--; + } + assert(nr_image_descs == 0); +} + +static void fill_image_descs(void) +{ + toc_entry_t *toc_entry; + + for (toc_entry = toc_entries; + toc_entry->cmdline_name != NULL; + toc_entry++) { + image_desc_t *desc; + + desc = new_image_desc(&toc_entry->uuid, + toc_entry->name, + toc_entry->cmdline_name); + add_image_desc(desc); + } +#ifdef PLAT_DEF_FIP_UUID + for (toc_entry = plat_def_toc_entries; + toc_entry->cmdline_name != NULL; + toc_entry++) { + image_desc_t *desc; + + desc = new_image_desc(&toc_entry->uuid, + toc_entry->name, + toc_entry->cmdline_name); + add_image_desc(desc); + } +#endif +} + +static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid) +{ + image_desc_t *desc; + + for (desc = image_desc_head; desc != NULL; desc = desc->next) + if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0) + return desc; + return NULL; +} + +static image_desc_t *lookup_image_desc_from_opt(const char *opt) +{ + image_desc_t *desc; + + for (desc = image_desc_head; desc != NULL; desc = desc->next) + if (strcmp(desc->cmdline_name, opt) == 0) + return desc; + return NULL; +} + +static void uuid_to_str(char *s, size_t len, const uuid_t *u) +{ + assert(len >= (_UUID_STR_LEN + 1)); + + snprintf(s, len, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%04X-%04X%04X%04X", + u->time_low[0], u->time_low[1], u->time_low[2], u->time_low[3], + u->time_mid[0], u->time_mid[1], + u->time_hi_and_version[0], u->time_hi_and_version[1], + (u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low, + (u->node[0] << 8) | u->node[1], + (u->node[2] << 8) | u->node[3], + (u->node[4] << 8) | u->node[5]); +} + +static void uuid_from_str(uuid_t *u, const char *s) +{ + int n; + + if (s == NULL) + log_errx("UUID cannot be NULL"); + if (strlen(s) != _UUID_STR_LEN) + log_errx("Invalid UUID: %s", s); + + n = sscanf(s, + "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", + &u->time_low[0], &u->time_low[1], &u->time_low[2], &u->time_low[3], + &u->time_mid[0], &u->time_mid[1], + &u->time_hi_and_version[0], &u->time_hi_and_version[1], + &u->clock_seq_hi_and_reserved, &u->clock_seq_low, + &u->node[0], &u->node[1], + &u->node[2], &u->node[3], + &u->node[4], &u->node[5]); + /* + * Given the format specifier above, we expect 16 items to be scanned + * for a properly formatted UUID. + */ + if (n != 16) + log_errx("Invalid UUID: %s", s); +} + +static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out) +{ + struct BLD_PLAT_STAT st; + FILE *fp; + char *buf, *bufend; + fip_toc_header_t *toc_header; + fip_toc_entry_t *toc_entry; + int terminated = 0; + size_t st_size; + + fp = fopen(filename, "rb"); + if (fp == NULL) + log_err("fopen %s", filename); + + if (fstat(fileno(fp), &st) == -1) + log_err("fstat %s", filename); + + st_size = st.st_size; + +#ifdef BLKGETSIZE64 + if ((st.st_mode & S_IFBLK) != 0) + if (ioctl(fileno(fp), BLKGETSIZE64, &st_size) == -1) + log_err("ioctl %s", filename); +#endif + + buf = xmalloc(st_size, "failed to load file into memory"); + if (fread(buf, 1, st_size, fp) != st_size) + log_errx("Failed to read %s", filename); + bufend = buf + st_size; + fclose(fp); + + if (st_size < sizeof(fip_toc_header_t)) + log_errx("FIP %s is truncated", filename); + + toc_header = (fip_toc_header_t *)buf; + toc_entry = (fip_toc_entry_t *)(toc_header + 1); + + if (toc_header->name != TOC_HEADER_NAME) + log_errx("%s is not a FIP file", filename); + + /* Return the ToC header if the caller wants it. */ + if (toc_header_out != NULL) + *toc_header_out = *toc_header; + + /* Walk through each ToC entry in the file. */ + while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) { + image_t *image; + image_desc_t *desc; + + /* Found the ToC terminator, we are done. */ + if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) { + terminated = 1; + break; + } + + /* + * Build a new image out of the ToC entry and add it to the + * table of images. + */ + image = xzalloc(sizeof(*image), + "failed to allocate memory for image"); + image->toc_e = *toc_entry; + image->buffer = xmalloc(toc_entry->size, + "failed to allocate image buffer, is FIP file corrupted?"); + /* Overflow checks before memory copy. */ + if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address) + log_errx("FIP %s is corrupted: entry size exceeds 64 bit address space", + filename); + if (toc_entry->size + toc_entry->offset_address > st_size) + log_errx("FIP %s is corrupted: entry size exceeds FIP file size", + filename); + + memcpy(image->buffer, buf + toc_entry->offset_address, + toc_entry->size); + + /* If this is an unknown image, create a descriptor for it. */ + desc = lookup_image_desc_from_uuid(&toc_entry->uuid); + if (desc == NULL) { + char name[_UUID_STR_LEN + 1], filename[PATH_MAX]; + + uuid_to_str(name, sizeof(name), &toc_entry->uuid); + snprintf(filename, sizeof(filename), "%s%s", + name, ".bin"); + desc = new_image_desc(&toc_entry->uuid, name, "blob"); + desc->action = DO_UNPACK; + desc->action_arg = xstrdup(filename, + "failed to allocate memory for blob filename"); + add_image_desc(desc); + } + + assert(desc->image == NULL); + desc->image = image; + + toc_entry++; + } + + if (terminated == 0) + log_errx("FIP %s does not have a ToC terminator entry", + filename); + free(buf); + return 0; +} + +static image_t *read_image_from_file(const uuid_t *uuid, const char *filename) +{ + struct BLD_PLAT_STAT st; + image_t *image; + FILE *fp; + + assert(uuid != NULL); + assert(filename != NULL); + + fp = fopen(filename, "rb"); + if (fp == NULL) + log_err("fopen %s", filename); + + if (fstat(fileno(fp), &st) == -1) + log_errx("fstat %s", filename); + + image = xzalloc(sizeof(*image), "failed to allocate memory for image"); + image->toc_e.uuid = *uuid; + image->buffer = xmalloc(st.st_size, "failed to allocate image buffer"); + if (fread(image->buffer, 1, st.st_size, fp) != st.st_size) + log_errx("Failed to read %s", filename); + image->toc_e.size = st.st_size; + + fclose(fp); + return image; +} + +static int write_image_to_file(const image_t *image, const char *filename) +{ + FILE *fp; + + fp = fopen(filename, "wb"); + if (fp == NULL) + log_err("fopen"); + xfwrite(image->buffer, image->toc_e.size, fp, filename); + fclose(fp); + return 0; +} + +static struct option *add_opt(struct option *opts, size_t *nr_opts, + const char *name, int has_arg, int val) +{ + opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts)); + if (opts == NULL) + log_err("realloc"); + opts[*nr_opts].name = name; + opts[*nr_opts].has_arg = has_arg; + opts[*nr_opts].flag = NULL; + opts[*nr_opts].val = val; + ++*nr_opts; + return opts; +} + +static struct option *fill_common_opts(struct option *opts, size_t *nr_opts, + int has_arg) +{ + image_desc_t *desc; + + for (desc = image_desc_head; desc != NULL; desc = desc->next) + opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg, + OPT_TOC_ENTRY); + return opts; +} + +#if !STATIC +static void md_print(const unsigned char *md, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + printf("%02x", md[i]); +} +#endif + +static int info_cmd(int argc, char *argv[]) +{ + image_desc_t *desc; + fip_toc_header_t toc_header; + + if (argc != 2) + info_usage(EXIT_FAILURE); + argc--, argv++; + + parse_fip(argv[0], &toc_header); + + if (verbose) { + log_dbgx("toc_header[name]: 0x%llX", + (unsigned long long)toc_header.name); + log_dbgx("toc_header[serial_number]: 0x%llX", + (unsigned long long)toc_header.serial_number); + log_dbgx("toc_header[flags]: 0x%llX", + (unsigned long long)toc_header.flags); + } + + for (desc = image_desc_head; desc != NULL; desc = desc->next) { + image_t *image = desc->image; + + if (image == NULL) + continue; + printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"", + desc->name, + (unsigned long long)image->toc_e.offset_address, + (unsigned long long)image->toc_e.size, + desc->cmdline_name); + + /* + * Omit this informative code portion for: + * Visual Studio missing SHA256. + * Statically linked builds. + */ +#if !defined(_MSC_VER) && !STATIC + if (verbose) { + unsigned char md[SHA256_DIGEST_LENGTH]; + + SHA256(image->buffer, image->toc_e.size, md); + printf(", sha256="); + md_print(md, sizeof(md)); + } +#endif + putchar('\n'); + } + + return 0; +} + +static void info_usage(int exit_status) +{ + printf("fiptool info FIP_FILENAME\n"); + exit(exit_status); +} + +static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align) +{ + FILE *fp; + image_desc_t *desc; + fip_toc_header_t *toc_header; + fip_toc_entry_t *toc_entry; + char *buf; + uint64_t entry_offset, buf_size, payload_size = 0, pad_size; + size_t nr_images = 0; + + for (desc = image_desc_head; desc != NULL; desc = desc->next) + if (desc->image != NULL) + nr_images++; + + buf_size = sizeof(fip_toc_header_t) + + sizeof(fip_toc_entry_t) * (nr_images + 1); + buf = calloc(1, buf_size); + if (buf == NULL) + log_err("calloc"); + + /* Build up header and ToC entries from the image table. */ + toc_header = (fip_toc_header_t *)buf; + toc_header->name = TOC_HEADER_NAME; + toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER; + toc_header->flags = toc_flags; + + toc_entry = (fip_toc_entry_t *)(toc_header + 1); + + entry_offset = buf_size; + for (desc = image_desc_head; desc != NULL; desc = desc->next) { + image_t *image = desc->image; + + if (image == NULL || (image->toc_e.size == 0ULL)) + continue; + payload_size += image->toc_e.size; + entry_offset = (entry_offset + align - 1) & ~(align - 1); + image->toc_e.offset_address = entry_offset; + *toc_entry++ = image->toc_e; + entry_offset += image->toc_e.size; + } + + /* + * Append a null uuid entry to mark the end of ToC entries. + * NOTE the offset address for the last toc_entry must match the fip + * size. + */ + memset(toc_entry, 0, sizeof(*toc_entry)); + toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1); + + /* Generate the FIP file. */ + fp = fopen(filename, "wb"); + if (fp == NULL) + log_err("fopen %s", filename); + + if (verbose) + log_dbgx("Metadata size: %zu bytes", buf_size); + + xfwrite(buf, buf_size, fp, filename); + + if (verbose) + log_dbgx("Payload size: %zu bytes", payload_size); + + for (desc = image_desc_head; desc != NULL; desc = desc->next) { + image_t *image = desc->image; + + if (image == NULL) + continue; + if (fseek(fp, image->toc_e.offset_address, SEEK_SET)) + log_errx("Failed to set file position"); + + xfwrite(image->buffer, image->toc_e.size, fp, filename); + } + + if (fseek(fp, entry_offset, SEEK_SET)) + log_errx("Failed to set file position"); + + pad_size = toc_entry->offset_address - entry_offset; + while (pad_size--) + fputc(0x0, fp); + + free(buf); + fclose(fp); + return 0; +} + +/* + * This function is shared between the create and update subcommands. + * The difference between the two subcommands is that when the FIP file + * is created, the parsing of an existing FIP is skipped. This results + * in update_fip() creating the new FIP file from scratch because the + * internal image table is not populated. + */ +static void update_fip(void) +{ + image_desc_t *desc; + + /* Add or replace images in the FIP file. */ + for (desc = image_desc_head; desc != NULL; desc = desc->next) { + image_t *image; + + if (desc->action != DO_PACK) + continue; + + image = read_image_from_file(&desc->uuid, + desc->action_arg); + if (desc->image != NULL) { + if (verbose) { + log_dbgx("Replacing %s with %s", + desc->cmdline_name, + desc->action_arg); + } + free(desc->image); + desc->image = image; + } else { + if (verbose) + log_dbgx("Adding image %s", + desc->action_arg); + desc->image = image; + } + } +} + +static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags) +{ + unsigned long long flags; + char *endptr; + + errno = 0; + flags = strtoull(arg, &endptr, 16); + if (*endptr != '\0' || flags > UINT16_MAX || errno != 0) + log_errx("Invalid platform ToC flags: %s", arg); + /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */ + *toc_flags |= flags << 32; +} + +static int is_power_of_2(unsigned long x) +{ + return x && !(x & (x - 1)); +} + +static unsigned long get_image_align(char *arg) +{ + char *endptr; + unsigned long align; + + errno = 0; + align = strtoul(arg, &endptr, 0); + if (*endptr != '\0' || !is_power_of_2(align) || errno != 0) + log_errx("Invalid alignment: %s", arg); + + return align; +} + +static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len) +{ + char *p; + + for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) { + if (strncmp(p, "uuid=", strlen("uuid=")) == 0) { + p += strlen("uuid="); + uuid_from_str(uuid, p); + } else if (strncmp(p, "file=", strlen("file=")) == 0) { + p += strlen("file="); + snprintf(filename, len, "%s", p); + } + } +} + +static int create_cmd(int argc, char *argv[]) +{ + struct option *opts = NULL; + size_t nr_opts = 0; + unsigned long long toc_flags = 0; + unsigned long align = 1; + + if (argc < 2) + create_usage(EXIT_FAILURE); + + opts = fill_common_opts(opts, &nr_opts, required_argument); + opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument, + OPT_PLAT_TOC_FLAGS); + opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); + opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); + opts = add_opt(opts, &nr_opts, NULL, 0, 0); + + while (1) { + int c, opt_index = 0; + + c = getopt_long(argc, argv, "b:", opts, &opt_index); + if (c == -1) + break; + + switch (c) { + case OPT_TOC_ENTRY: { + image_desc_t *desc; + + desc = lookup_image_desc_from_opt(opts[opt_index].name); + set_image_desc_action(desc, DO_PACK, optarg); + break; + } + case OPT_PLAT_TOC_FLAGS: + parse_plat_toc_flags(optarg, &toc_flags); + break; + case OPT_ALIGN: + align = get_image_align(optarg); + break; + case 'b': { + char name[_UUID_STR_LEN + 1]; + char filename[PATH_MAX] = { 0 }; + uuid_t uuid = uuid_null; + image_desc_t *desc; + + parse_blob_opt(optarg, &uuid, + filename, sizeof(filename)); + + if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || + filename[0] == '\0') + create_usage(EXIT_FAILURE); + + desc = lookup_image_desc_from_uuid(&uuid); + if (desc == NULL) { + uuid_to_str(name, sizeof(name), &uuid); + desc = new_image_desc(&uuid, name, "blob"); + add_image_desc(desc); + } + set_image_desc_action(desc, DO_PACK, filename); + break; + } + default: + create_usage(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + free(opts); + + if (argc == 0) + create_usage(EXIT_SUCCESS); + + update_fip(); + + pack_images(argv[0], toc_flags, align); + return 0; +} + +static void create_usage(int exit_status) +{ + toc_entry_t *toc_entry = toc_entries; + + printf("fiptool create [opts] FIP_FILENAME\n"); + printf("\n"); + printf("Options:\n"); + printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n"); + printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n"); + printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n"); + printf("\n"); + printf("Specific images are packed with the following options:\n"); + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#ifdef PLAT_DEF_FIP_UUID + toc_entry = plat_def_toc_entries; + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#endif + exit(exit_status); +} + +static int update_cmd(int argc, char *argv[]) +{ + struct option *opts = NULL; + size_t nr_opts = 0; + char outfile[PATH_MAX] = { 0 }; + fip_toc_header_t toc_header = { 0 }; + unsigned long long toc_flags = 0; + unsigned long align = 1; + int pflag = 0; + + if (argc < 2) + update_usage(EXIT_FAILURE); + + opts = fill_common_opts(opts, &nr_opts, required_argument); + opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); + opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); + opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); + opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument, + OPT_PLAT_TOC_FLAGS); + opts = add_opt(opts, &nr_opts, NULL, 0, 0); + + while (1) { + int c, opt_index = 0; + + c = getopt_long(argc, argv, "b:o:", opts, &opt_index); + if (c == -1) + break; + + switch (c) { + case OPT_TOC_ENTRY: { + image_desc_t *desc; + + desc = lookup_image_desc_from_opt(opts[opt_index].name); + set_image_desc_action(desc, DO_PACK, optarg); + break; + } + case OPT_PLAT_TOC_FLAGS: + parse_plat_toc_flags(optarg, &toc_flags); + pflag = 1; + break; + case 'b': { + char name[_UUID_STR_LEN + 1]; + char filename[PATH_MAX] = { 0 }; + uuid_t uuid = uuid_null; + image_desc_t *desc; + + parse_blob_opt(optarg, &uuid, + filename, sizeof(filename)); + + if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || + filename[0] == '\0') + update_usage(EXIT_FAILURE); + + desc = lookup_image_desc_from_uuid(&uuid); + if (desc == NULL) { + uuid_to_str(name, sizeof(name), &uuid); + desc = new_image_desc(&uuid, name, "blob"); + add_image_desc(desc); + } + set_image_desc_action(desc, DO_PACK, filename); + break; + } + case OPT_ALIGN: + align = get_image_align(optarg); + break; + case 'o': + snprintf(outfile, sizeof(outfile), "%s", optarg); + break; + default: + update_usage(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + free(opts); + + if (argc == 0) + update_usage(EXIT_SUCCESS); + + if (outfile[0] == '\0') + snprintf(outfile, sizeof(outfile), "%s", argv[0]); + + if (access(argv[0], F_OK) == 0) + parse_fip(argv[0], &toc_header); + + if (pflag) + toc_header.flags &= ~(0xffffULL << 32); + toc_flags = (toc_header.flags |= toc_flags); + + update_fip(); + + pack_images(outfile, toc_flags, align); + return 0; +} + +static void update_usage(int exit_status) +{ + toc_entry_t *toc_entry = toc_entries; + + printf("fiptool update [opts] FIP_FILENAME\n"); + printf("\n"); + printf("Options:\n"); + printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n"); + printf(" --blob uuid=...,file=...\tAdd or update an image with the given UUID pointed to by file.\n"); + printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n"); + printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n"); + printf("\n"); + printf("Specific images are packed with the following options:\n"); + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#ifdef PLAT_DEF_FIP_UUID + toc_entry = plat_def_toc_entries; + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#endif + exit(exit_status); +} + +static int unpack_cmd(int argc, char *argv[]) +{ + struct option *opts = NULL; + size_t nr_opts = 0; + char outdir[PATH_MAX] = { 0 }; + image_desc_t *desc; + int fflag = 0; + int unpack_all = 1; + + if (argc < 2) + unpack_usage(EXIT_FAILURE); + + opts = fill_common_opts(opts, &nr_opts, required_argument); + opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); + opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); + opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); + opts = add_opt(opts, &nr_opts, NULL, 0, 0); + + while (1) { + int c, opt_index = 0; + + c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); + if (c == -1) + break; + + switch (c) { + case OPT_TOC_ENTRY: { + image_desc_t *desc; + + desc = lookup_image_desc_from_opt(opts[opt_index].name); + set_image_desc_action(desc, DO_UNPACK, optarg); + unpack_all = 0; + break; + } + case 'b': { + char name[_UUID_STR_LEN + 1]; + char filename[PATH_MAX] = { 0 }; + uuid_t uuid = uuid_null; + image_desc_t *desc; + + parse_blob_opt(optarg, &uuid, + filename, sizeof(filename)); + + if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || + filename[0] == '\0') + unpack_usage(EXIT_FAILURE); + + desc = lookup_image_desc_from_uuid(&uuid); + if (desc == NULL) { + uuid_to_str(name, sizeof(name), &uuid); + desc = new_image_desc(&uuid, name, "blob"); + add_image_desc(desc); + } + set_image_desc_action(desc, DO_UNPACK, filename); + unpack_all = 0; + break; + } + case 'f': + fflag = 1; + break; + case 'o': + snprintf(outdir, sizeof(outdir), "%s", optarg); + break; + default: + unpack_usage(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + free(opts); + + if (argc == 0) + unpack_usage(EXIT_SUCCESS); + + parse_fip(argv[0], NULL); + + if (outdir[0] != '\0') + if (chdir(outdir) == -1) + log_err("chdir %s", outdir); + + /* Unpack all specified images. */ + for (desc = image_desc_head; desc != NULL; desc = desc->next) { + char file[PATH_MAX]; + image_t *image = desc->image; + + if (!unpack_all && desc->action != DO_UNPACK) + continue; + + /* Build filename. */ + if (desc->action_arg == NULL) + snprintf(file, sizeof(file), "%s.bin", + desc->cmdline_name); + else + snprintf(file, sizeof(file), "%s", + desc->action_arg); + + if (image == NULL) { + if (!unpack_all) + log_warnx("%s does not exist in %s", + file, argv[0]); + continue; + } + + if (access(file, F_OK) != 0 || fflag) { + if (verbose) + log_dbgx("Unpacking %s", file); + write_image_to_file(image, file); + } else { + log_warnx("File %s already exists, use --force to overwrite it", + file); + } + } + + return 0; +} + +static void unpack_usage(int exit_status) +{ + toc_entry_t *toc_entry = toc_entries; + + printf("fiptool unpack [opts] FIP_FILENAME\n"); + printf("\n"); + printf("Options:\n"); + printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n"); + printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n"); + printf(" --out path\t\t\tSet the output directory path.\n"); + printf("\n"); + printf("Specific images are unpacked with the following options:\n"); + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#ifdef PLAT_DEF_FIP_UUID + toc_entry = plat_def_toc_entries; + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#endif + printf("\n"); + printf("If no options are provided, all images will be unpacked.\n"); + exit(exit_status); +} + +static int remove_cmd(int argc, char *argv[]) +{ + struct option *opts = NULL; + size_t nr_opts = 0; + char outfile[PATH_MAX] = { 0 }; + fip_toc_header_t toc_header; + image_desc_t *desc; + unsigned long align = 1; + int fflag = 0; + + if (argc < 2) + remove_usage(EXIT_FAILURE); + + opts = fill_common_opts(opts, &nr_opts, no_argument); + opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); + opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); + opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); + opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); + opts = add_opt(opts, &nr_opts, NULL, 0, 0); + + while (1) { + int c, opt_index = 0; + + c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); + if (c == -1) + break; + + switch (c) { + case OPT_TOC_ENTRY: { + image_desc_t *desc; + + desc = lookup_image_desc_from_opt(opts[opt_index].name); + set_image_desc_action(desc, DO_REMOVE, NULL); + break; + } + case OPT_ALIGN: + align = get_image_align(optarg); + break; + case 'b': { + char name[_UUID_STR_LEN + 1], filename[PATH_MAX]; + uuid_t uuid = uuid_null; + image_desc_t *desc; + + parse_blob_opt(optarg, &uuid, + filename, sizeof(filename)); + + if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0) + remove_usage(EXIT_FAILURE); + + desc = lookup_image_desc_from_uuid(&uuid); + if (desc == NULL) { + uuid_to_str(name, sizeof(name), &uuid); + desc = new_image_desc(&uuid, name, "blob"); + add_image_desc(desc); + } + set_image_desc_action(desc, DO_REMOVE, NULL); + break; + } + case 'f': + fflag = 1; + break; + case 'o': + snprintf(outfile, sizeof(outfile), "%s", optarg); + break; + default: + remove_usage(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + free(opts); + + if (argc == 0) + remove_usage(EXIT_SUCCESS); + + if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag) + log_errx("File %s already exists, use --force to overwrite it", + outfile); + + if (outfile[0] == '\0') + snprintf(outfile, sizeof(outfile), "%s", argv[0]); + + parse_fip(argv[0], &toc_header); + + for (desc = image_desc_head; desc != NULL; desc = desc->next) { + if (desc->action != DO_REMOVE) + continue; + + if (desc->image != NULL) { + if (verbose) + log_dbgx("Removing %s", + desc->cmdline_name); + free(desc->image); + desc->image = NULL; + } else { + log_warnx("%s does not exist in %s", + desc->cmdline_name, argv[0]); + } + } + + pack_images(outfile, toc_header.flags, align); + return 0; +} + +static void remove_usage(int exit_status) +{ + toc_entry_t *toc_entry = toc_entries; + + printf("fiptool remove [opts] FIP_FILENAME\n"); + printf("\n"); + printf("Options:\n"); + printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n"); + printf(" --blob uuid=...\tRemove an image with the given UUID.\n"); + printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n"); + printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n"); + printf("\n"); + printf("Specific images are removed with the following options:\n"); + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#ifdef PLAT_DEF_FIP_UUID + toc_entry = plat_def_toc_entries; + for (; toc_entry->cmdline_name != NULL; toc_entry++) + printf(" --%-16s\t%s\n", toc_entry->cmdline_name, + toc_entry->name); +#endif + exit(exit_status); +} + +static int version_cmd(int argc, char *argv[]) +{ +#ifdef VERSION + puts(VERSION); +#else + /* If built from fiptool directory, VERSION is not set. */ + puts("Unknown version"); +#endif + return 0; +} + +static void version_usage(int exit_status) +{ + printf("fiptool version\n"); + exit(exit_status); +} + +static int help_cmd(int argc, char *argv[]) +{ + int i; + + if (argc < 2) + usage(); + argc--, argv++; + + for (i = 0; i < NELEM(cmds); i++) { + if (strcmp(cmds[i].name, argv[0]) == 0 && + cmds[i].usage != NULL) + cmds[i].usage(EXIT_SUCCESS); + } + if (i == NELEM(cmds)) + printf("No help for subcommand '%s'\n", argv[0]); + return 0; +} + +static void usage(void) +{ + printf("usage: fiptool [--verbose] <command> [<args>]\n"); + printf("Global options supported:\n"); + printf(" --verbose\tEnable verbose output for all commands.\n"); + printf("\n"); + printf("Commands supported:\n"); + printf(" info\t\tList images contained in FIP.\n"); + printf(" create\tCreate a new FIP with the given images.\n"); + printf(" update\tUpdate an existing FIP with the given images.\n"); + printf(" unpack\tUnpack images from FIP.\n"); + printf(" remove\tRemove images from FIP.\n"); + printf(" version\tShow fiptool version.\n"); + printf(" help\t\tShow help for given command.\n"); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + int i, ret = 0; + + while (1) { + int c, opt_index = 0; + static struct option opts[] = { + { "verbose", no_argument, NULL, 'v' }, + { NULL, no_argument, NULL, 0 } + }; + + /* + * Set POSIX mode so getopt stops at the first non-option + * which is the subcommand. + */ + c = getopt_long(argc, argv, "+v", opts, &opt_index); + if (c == -1) + break; + + switch (c) { + case 'v': + verbose = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + /* Reset optind for subsequent getopt processing. */ + optind = 0; + + if (argc == 0) + usage(); + + fill_image_descs(); + for (i = 0; i < NELEM(cmds); i++) { + if (strcmp(cmds[i].name, argv[0]) == 0) { + ret = cmds[i].handler(argc, argv); + break; + } + } + if (i == NELEM(cmds)) + usage(); + free_image_descs(); + return ret; +} diff --git a/tools/fiptool/fiptool.h b/tools/fiptool/fiptool.h new file mode 100644 index 0000000..88c4a7e --- /dev/null +++ b/tools/fiptool/fiptool.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FIPTOOL_H +#define FIPTOOL_H + +#include <stddef.h> +#include <stdint.h> + +#include <firmware_image_package.h> +#include <uuid.h> + +#include "fiptool_platform.h" + +#define NELEM(x) (sizeof (x) / sizeof *(x)) + +enum { + DO_UNSPEC = 0, + DO_PACK = 1, + DO_UNPACK = 2, + DO_REMOVE = 3 +}; + +enum { + LOG_DBG, + LOG_WARN, + LOG_ERR +}; + +typedef struct image_desc { + uuid_t uuid; + char *name; + char *cmdline_name; + int action; + char *action_arg; + struct image *image; + struct image_desc *next; +} image_desc_t; + +typedef struct image { + struct fip_toc_entry toc_e; + void *buffer; +} image_t; + +typedef struct cmd { + char *name; + int (*handler)(int, char **); + void (*usage)(int); +} cmd_t; + +#endif /* FIPTOOL_H */ diff --git a/tools/fiptool/fiptool_platform.h b/tools/fiptool/fiptool_platform.h new file mode 100644 index 0000000..9bfa298 --- /dev/null +++ b/tools/fiptool/fiptool_platform.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +/* + * Build platform specific handling. + * This allows for builds on non-Posix platforms + * e.g. Visual Studio on Windows + */ + +#ifndef FIPTOOL_PLATFORM_H +#define FIPTOOL_PLATFORM_H + +#ifndef _MSC_VER + +/* Not Visual Studio, so include Posix Headers. */ +# include <getopt.h> +# include <openssl/sha.h> +# include <unistd.h> + +# define BLD_PLAT_STAT stat + +#else + +/* Visual Studio. */ +# include "win_posix.h" + +#endif + +#endif /* FIPTOOL_PLATFORM_H */ diff --git a/tools/fiptool/plat_fiptool/arm/board/juno/plat_fiptool.mk b/tools/fiptool/plat_fiptool/arm/board/juno/plat_fiptool.mk new file mode 100644 index 0000000..fef2116 --- /dev/null +++ b/tools/fiptool/plat_fiptool/arm/board/juno/plat_fiptool.mk @@ -0,0 +1,16 @@ +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +PLAT_DEF_UUID := yes + +ifeq (${PLAT_DEF_UUID}, yes) +HOSTCCFLAGS += -DPLAT_DEF_FIP_UUID +ifeq (${ETHOSN_NPU_TZMP1},1) +HOSTCCFLAGS += -DETHOSN_NPU_TZMP1 +endif +INCLUDE_PATHS += -I./ -I${PLAT_DIR}fip -I../../include/ +OBJECTS += ${PLAT_DIR}fip/plat_def_uuid_config.o +endif diff --git a/tools/fiptool/plat_fiptool/arm/board/tc/plat_def_uuid_config.c b/tools/fiptool/plat_fiptool/arm/board/tc/plat_def_uuid_config.c new file mode 100644 index 0000000..903310b --- /dev/null +++ b/tools/fiptool/plat_fiptool/arm/board/tc/plat_def_uuid_config.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <stddef.h> + +#include <firmware_image_package.h> + +#include "tbbr_config.h" + +toc_entry_t plat_def_toc_entries[] = { + { + .name = "RSS Firmware BL1_2 image", + .uuid = UUID_RSS_FIRMWARE_BL1_2, + .cmdline_name = "rss-bl1_2" + }, + { + .name = "RSS Firmware BL2 image", + .uuid = UUID_RSS_FIRMWARE_BL2, + .cmdline_name = "rss-bl2" + }, + { + .name = "RSS Firmware SCP BL1 image", + .uuid = UUID_RSS_FIRMWARE_SCP_BL1, + .cmdline_name = "rss-scp-bl1" + }, + { + .name = "RSS Firmware AP BL1 image", + .uuid = UUID_RSS_FIRMWARE_AP_BL1, + .cmdline_name = "rss-ap-bl1" + }, + { + .name = "RSS Firmware non-secure image", + .uuid = UUID_RSS_FIRMWARE_NS, + .cmdline_name = "rss-ns" + }, + { + .name = "RSS Firmware secure image", + .uuid = UUID_RSS_FIRMWARE_S, + .cmdline_name = "rss-s" + }, + { + .name = "RSS Firmware non-secure SIC tables", + .uuid = UUID_RSS_SIC_TABLES_NS, + .cmdline_name = "rss-sic-tables-ns" + }, + { + .name = "RSS Firmware secure SIC tables", + .uuid = UUID_RSS_SIC_TABLES_S, + .cmdline_name = "rss-sic-tables-s" + }, + + { + .name = NULL, + .uuid = { {0} }, + .cmdline_name = NULL, + } +}; diff --git a/tools/fiptool/plat_fiptool/arm/board/tc/plat_fiptool.mk b/tools/fiptool/plat_fiptool/arm/board/tc/plat_fiptool.mk new file mode 100644 index 0000000..70ccfc5 --- /dev/null +++ b/tools/fiptool/plat_fiptool/arm/board/tc/plat_fiptool.mk @@ -0,0 +1,12 @@ +# +# Copyright (c) 2021, NXP. All rights reserved. +# Copyright (c) 2022-2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +INCLUDE_PATHS += -I./ \ + -I../../plat/arm/board/tc + +HOSTCCFLAGS += -DPLAT_DEF_FIP_UUID +OBJECTS += plat_fiptool/arm/board/tc/plat_def_uuid_config.o diff --git a/tools/fiptool/plat_fiptool/nxp/plat_def_uuid_config.c b/tools/fiptool/plat_fiptool/nxp/plat_def_uuid_config.c new file mode 100644 index 0000000..fdb4b93 --- /dev/null +++ b/tools/fiptool/plat_fiptool/nxp/plat_def_uuid_config.c @@ -0,0 +1,90 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> + +#include <firmware_image_package.h> + +#include "tbbr_config.h" + +toc_entry_t plat_def_toc_entries[] = { + /* DDR PHY firmwares */ + { + .name = "DDR UDIMM PHY IMEM 1d FW", + .uuid = UUID_DDR_IMEM_UDIMM_1D, + .cmdline_name = "ddr-immem-udimm-1d" + }, + { + .name = "DDR UDIMM PHY IMEM 2d FW", + .uuid = UUID_DDR_IMEM_UDIMM_2D, + .cmdline_name = "ddr-immem-udimm-2d" + }, + { + .name = "DDR UDIMM PHY DMEM 1d FW", + .uuid = UUID_DDR_DMEM_UDIMM_1D, + .cmdline_name = "ddr-dmmem-udimm-1d" + }, + { + .name = "DDR UDIMM PHY DMEM 2d FW", + .uuid = UUID_DDR_DMEM_UDIMM_2D, + .cmdline_name = "ddr-dmmem-udimm-2d" + }, + { + .name = "DDR RDIMM PHY IMEM 1d FW", + .uuid = UUID_DDR_IMEM_RDIMM_1D, + .cmdline_name = "ddr-immem-rdimm-1d" + }, + { + .name = "DDR RDIMM PHY IMEM 2d FW", + .uuid = UUID_DDR_IMEM_RDIMM_2D, + .cmdline_name = "ddr-immem-rdimm-2d" + }, + { + .name = "DDR RDIMM PHY DMEM 1d FW", + .uuid = UUID_DDR_DMEM_RDIMM_1D, + .cmdline_name = "ddr-dmmem-rdimm-1d" + }, + { + .name = "DDR RDIMM PHY DMEM 2d FW", + .uuid = UUID_DDR_DMEM_RDIMM_2D, + .cmdline_name = "ddr-dmmem-rdimm-2d" + }, + { + .name = "FUSE PROV FW", + .uuid = UUID_FUSE_PROV, + .cmdline_name = "fuse-prov" + }, + { + .name = "FUSE UPGRADE FW", + .uuid = UUID_FUSE_UP, + .cmdline_name = "fuse-upgrade" + }, + + /* Key Certificates */ + { + .name = "DDR Firmware key certificate", + .uuid = UUID_DDR_FW_KEY_CERT, + .cmdline_name = "ddr-fw-key-cert" + }, + + /* Content certificates */ + { + .name = "DDR UDIMM Firmware content certificate", + .uuid = UUID_DDR_UDIMM_FW_CONTENT_CERT, + .cmdline_name = "ddr-udimm-fw-cert" + }, + { + .name = "DDR RDIMM Firmware content certificate", + .uuid = UUID_DDR_RDIMM_FW_CONTENT_CERT, + .cmdline_name = "ddr-rdimm-fw-cert" + }, + + { + .name = NULL, + .uuid = { {0} }, + .cmdline_name = NULL, + } +}; diff --git a/tools/fiptool/plat_fiptool/nxp/plat_fiptool.mk b/tools/fiptool/plat_fiptool/nxp/plat_fiptool.mk new file mode 100644 index 0000000..6d7b07b --- /dev/null +++ b/tools/fiptool/plat_fiptool/nxp/plat_fiptool.mk @@ -0,0 +1,32 @@ +# +# Copyright (c) 2021, NXP. All rights reserved. +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Name of the platform defined source file name, +# which contains platform defined UUID entries populated +# in the plat_def_toc_entries[]. +PLAT_DEF_UUID_CONFIG_FILE_NAME := plat_def_uuid_config + +PLAT_DEF_UUID_CONFIG_FILE_PATH := plat_fiptool/nxp/ + +PLAT_DEF_OID := yes +PLAT_DEF_UUID := yes +PLAT_DEF_UUID_OID_CONFIG_PATH := ../../plat/nxp/common/fip_handler/common + + +INCLUDE_PATHS += -I${PLAT_DEF_UUID_OID_CONFIG_PATH} \ + -I./ + +ifeq (${PLAT_DEF_OID},yes) +HOSTCCFLAGS += -DPLAT_DEF_OID +endif + +ifeq (${PLAT_DEF_UUID},yes) +HOSTCCFLAGS += -DPLAT_DEF_FIP_UUID +PLAT_OBJECTS += ${PLAT_DEF_UUID_CONFIG_FILE_PATH}/${PLAT_DEF_UUID_CONFIG_FILE_NAME}.o +endif + +OBJECTS += ${PLAT_OBJECTS} diff --git a/tools/fiptool/plat_fiptool/st/stm32mp1/plat_def_uuid_config.c b/tools/fiptool/plat_fiptool/st/stm32mp1/plat_def_uuid_config.c new file mode 100644 index 0000000..4df4144 --- /dev/null +++ b/tools/fiptool/plat_fiptool/st/stm32mp1/plat_def_uuid_config.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022-2023, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> + +#include <firmware_image_package.h> + +#include "tbbr_config.h" + +toc_entry_t plat_def_toc_entries[] = { + { + .name = "STM32MP CONFIG CERT", + .uuid = UUID_STM32MP_CONFIG_CERT, + .cmdline_name = "stm32mp-cfg-cert" + }, + + { + .name = NULL, + .uuid = { {0} }, + .cmdline_name = NULL, + } +}; diff --git a/tools/fiptool/plat_fiptool/st/stm32mp1/plat_fiptool.mk b/tools/fiptool/plat_fiptool/st/stm32mp1/plat_fiptool.mk new file mode 100644 index 0000000..1ba47c1 --- /dev/null +++ b/tools/fiptool/plat_fiptool/st/stm32mp1/plat_fiptool.mk @@ -0,0 +1,25 @@ +# +# Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Name of the platform defined source file name, +# which contains platform defined UUID entries populated +# in the plat_def_toc_entries[]. +PLAT_DEF_UUID_FILE_NAME := plat_def_uuid_config + +INCLUDE_PATHS += -I${PLAT_DIR}/include -I./ + +PLAT_DEF_UUID := yes + +ifeq (${PLAT_DEF_UUID},yes) +HOSTCCFLAGS += -DPLAT_DEF_FIP_UUID + +${PLAT_DEF_UUID_FILE_NAME}.o: plat_fiptool/st/stm32mp1/${PLAT_DEF_UUID_FILE_NAME}.c + ${HOSTCC} -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ + +PLAT_OBJECTS += ${PLAT_DEF_UUID_FILE_NAME}.o +endif + +OBJECTS += ${PLAT_OBJECTS} diff --git a/tools/fiptool/tbbr_config.c b/tools/fiptool/tbbr_config.c new file mode 100644 index 0000000..cdbf389 --- /dev/null +++ b/tools/fiptool/tbbr_config.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> + +#include <firmware_image_package.h> + +#include "tbbr_config.h" + +/* The images used depends on the platform. */ +toc_entry_t toc_entries[] = { + { + .name = "SCP Firmware Updater Configuration FWU SCP_BL2U", + .uuid = UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U, + .cmdline_name = "scp-fwu-cfg" + }, + { + .name = "AP Firmware Updater Configuration BL2U", + .uuid = UUID_TRUSTED_UPDATE_FIRMWARE_BL2U, + .cmdline_name = "ap-fwu-cfg" + }, + { + .name = "Firmware Updater NS_BL2U", + .uuid = UUID_TRUSTED_UPDATE_FIRMWARE_NS_BL2U, + .cmdline_name = "fwu" + }, + { + .name = "Non-Trusted Firmware Updater certificate", + .uuid = UUID_TRUSTED_FWU_CERT, + .cmdline_name = "fwu-cert" + }, + { + .name = "Trusted Boot Firmware BL2", + .uuid = UUID_TRUSTED_BOOT_FIRMWARE_BL2, + .cmdline_name = "tb-fw" + }, + { + .name = "SCP Firmware SCP_BL2", + .uuid = UUID_SCP_FIRMWARE_SCP_BL2, + .cmdline_name = "scp-fw" + }, + { + .name = "EL3 Runtime Firmware BL31", + .uuid = UUID_EL3_RUNTIME_FIRMWARE_BL31, + .cmdline_name = "soc-fw" + }, + { + .name = "Secure Payload BL32 (Trusted OS)", + .uuid = UUID_SECURE_PAYLOAD_BL32, + .cmdline_name = "tos-fw" + }, + { + .name = "Secure Payload BL32 Extra1 (Trusted OS Extra1)", + .uuid = UUID_SECURE_PAYLOAD_BL32_EXTRA1, + .cmdline_name = "tos-fw-extra1" + }, + { + .name = "Secure Payload BL32 Extra2 (Trusted OS Extra2)", + .uuid = UUID_SECURE_PAYLOAD_BL32_EXTRA2, + .cmdline_name = "tos-fw-extra2" + }, + { + .name = "Non-Trusted Firmware BL33", + .uuid = UUID_NON_TRUSTED_FIRMWARE_BL33, + .cmdline_name = "nt-fw" + }, + { + .name = "Realm Monitor Management Firmware", + .uuid = UUID_REALM_MONITOR_MGMT_FIRMWARE, + .cmdline_name = "rmm-fw" + }, + /* Dynamic Configs */ + { + .name = "FW_CONFIG", + .uuid = UUID_FW_CONFIG, + .cmdline_name = "fw-config" + }, + { + .name = "HW_CONFIG", + .uuid = UUID_HW_CONFIG, + .cmdline_name = "hw-config" + }, + { + .name = "TB_FW_CONFIG", + .uuid = UUID_TB_FW_CONFIG, + .cmdline_name = "tb-fw-config" + }, + { + .name = "SOC_FW_CONFIG", + .uuid = UUID_SOC_FW_CONFIG, + .cmdline_name = "soc-fw-config" + }, + { + .name = "TOS_FW_CONFIG", + .uuid = UUID_TOS_FW_CONFIG, + .cmdline_name = "tos-fw-config" + }, + { + .name = "NT_FW_CONFIG", + .uuid = UUID_NT_FW_CONFIG, + .cmdline_name = "nt-fw-config" + }, + /* Key Certificates */ + { + .name = "Root Of Trust key certificate", + .uuid = UUID_ROT_KEY_CERT, + .cmdline_name = "rot-cert" + }, + { + .name = "Trusted key certificate", + .uuid = UUID_TRUSTED_KEY_CERT, + .cmdline_name = "trusted-key-cert" + }, + { + .name = "SCP Firmware key certificate", + .uuid = UUID_SCP_FW_KEY_CERT, + .cmdline_name = "scp-fw-key-cert" + }, + { + .name = "SoC Firmware key certificate", + .uuid = UUID_SOC_FW_KEY_CERT, + .cmdline_name = "soc-fw-key-cert" + }, + { + .name = "Trusted OS Firmware key certificate", + .uuid = UUID_TRUSTED_OS_FW_KEY_CERT, + .cmdline_name = "tos-fw-key-cert" + }, + { + .name = "Non-Trusted Firmware key certificate", + .uuid = UUID_NON_TRUSTED_FW_KEY_CERT, + .cmdline_name = "nt-fw-key-cert" + }, + + /* Content certificates */ + { + .name = "Trusted Boot Firmware BL2 certificate", + .uuid = UUID_TRUSTED_BOOT_FW_CERT, + .cmdline_name = "tb-fw-cert" + }, + { + .name = "SCP Firmware content certificate", + .uuid = UUID_SCP_FW_CONTENT_CERT, + .cmdline_name = "scp-fw-cert" + }, + { + .name = "SoC Firmware content certificate", + .uuid = UUID_SOC_FW_CONTENT_CERT, + .cmdline_name = "soc-fw-cert" + }, + { + .name = "Trusted OS Firmware content certificate", + .uuid = UUID_TRUSTED_OS_FW_CONTENT_CERT, + .cmdline_name = "tos-fw-cert" + }, + { + .name = "Non-Trusted Firmware content certificate", + .uuid = UUID_NON_TRUSTED_FW_CONTENT_CERT, + .cmdline_name = "nt-fw-cert" + }, + { + .name = "SiP owned Secure Partition content certificate", + .uuid = UUID_SIP_SECURE_PARTITION_CONTENT_CERT, + .cmdline_name = "sip-sp-cert" + }, + { + .name = "Platform owned Secure Partition content certificate", + .uuid = UUID_PLAT_SECURE_PARTITION_CONTENT_CERT, + .cmdline_name = "plat-sp-cert" + }, + { + .name = "CCA Content Certificate", + .uuid = UUID_CCA_CONTENT_CERT, + .cmdline_name = "cca-cert" + }, + { + .name = "Core Secure World Key Certificate", + .uuid = UUID_CORE_SWD_KEY_CERT, + .cmdline_name = "core-swd-cert" + }, + { + .name = "Platform Key Certificate", + .uuid = UUID_PLAT_KEY_CERT, + .cmdline_name = "plat-key-cert" + }, + { + .name = NULL, + .uuid = { {0} }, + .cmdline_name = NULL, + } +}; diff --git a/tools/fiptool/tbbr_config.h b/tools/fiptool/tbbr_config.h new file mode 100644 index 0000000..b926ff0 --- /dev/null +++ b/tools/fiptool/tbbr_config.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TBBR_CONFIG_H +#define TBBR_CONFIG_H + +#include <stdint.h> + +#include <uuid.h> + +#define TOC_HEADER_SERIAL_NUMBER 0x12345678 + +typedef struct toc_entry { + char *name; + uuid_t uuid; + char *cmdline_name; +} toc_entry_t; + +extern toc_entry_t toc_entries[]; + +#ifdef PLAT_DEF_FIP_UUID +extern toc_entry_t plat_def_toc_entries[]; +#endif + +#endif /* TBBR_CONFIG_H */ diff --git a/tools/fiptool/win_posix.c b/tools/fiptool/win_posix.c new file mode 100644 index 0000000..33b44d4 --- /dev/null +++ b/tools/fiptool/win_posix.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2017 - 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include "win_posix.h" + +/* + * This variable is set by getopt to the index of the next element of the + * argv array to be processed. Once getopt has found all of the option + * arguments, you can use this variable to determine where the remaining + * non-option arguments begin. The initial value of this variable is 1. + */ +int optind = 1; + +/* + * If the value of this variable is nonzero, then getopt prints an error + * message to the standard error stream if it encounters an unknown option + * default character or an option with a missing required argument. + * If you set this variable to zero, getopt does not print any messages, + * but it still returns the character ? to indicate an error. + */ +const int opterr; /* = 0; */ +/* const because we do not implement error printing.*/ +/* Not initialised to conform with the coding standard. */ + +/* + * When getopt encounters an unknown option character or an option with a + * missing required argument, it stores that option character in this + * variable. + */ +int optopt; /* = 0; */ + +/* + * This variable is set by getopt to point at the value of the option + * argument, for those options that accept arguments. + */ +char *optarg; /* = 0; */ + +enum return_flags { + RET_ERROR = -1, + RET_END_OPT_LIST = -1, + RET_NO_PARAM = '?', + RET_NO_PARAM2 = ':', + RET_UNKNOWN_OPT = '?' +}; + +/* + * Common initialisation on entry. + */ +static +void getopt_init(void) +{ + optarg = (char *)0; + optopt = 0; + /* optind may be zero with some POSIX uses. + * For our purposes we just change it to 1. + */ + if (optind == 0) + optind = 1; +} + +/* + * Common handling for a single letter option. + */ +static +int getopt_1char(int argc, + char *const argv[], + const char *const opstring, + const int optchar) +{ + size_t nlen = (opstring == 0) ? 0 : strlen(opstring); + size_t loptn; + + for (loptn = 0; loptn < nlen; loptn++) { + if (optchar == opstring[loptn]) { + if (opstring[loptn + 1] == ':') { + /* Option has argument */ + if (optind < argc) { + /* Found argument. */ + assert(argv != 0); + optind++; + optarg = argv[optind++]; + return optchar; + } + /* Missing argument. */ + if (opstring[loptn + 2] == ':') { + /* OK if optional "x::". */ + optind++; + return optchar; + } + /* Actual missing value. */ + optopt = optchar; + return ((opstring[0] == ':') + ? RET_NO_PARAM2 + : RET_NO_PARAM); + } + /* No argument, just return option char */ + optind++; + return optchar; + } + } + /* + * If getopt finds an option character in argv that was not included in + * options, ... it returns '?' and sets the external variable optopt to + * the actual option character. + */ + optopt = optchar; + return RET_UNKNOWN_OPT; +} + +int getopt(int argc, + char *argv[], + char *opstring) +{ + int result = RET_END_OPT_LIST; + size_t argn = 0; + size_t nlen = strlen(opstring); + + getopt_init(); + /* If we have an argument left to play with */ + if ((argc > optind) && (argv != 0)) { + const char *arg = (const char *)argv[optind]; + + if ((arg != 0) && (arg[0] == '-')) + result = getopt_1char(argc, argv, opstring, arg[1]); + } + + return result; +} + +/* + * Match an argument value against an option name. + * Note that we only match over the shorter length of the pair, to allow + * for abbreviation or say --match=value + * Long option names may be abbreviated if the abbreviation is unique or an + * exact match for some defined option. This function does not check that the + * abbreviations are unique and should be handled by the caller. + * A long option may take a parameter, of the form --opt=param or --opt param. +*/ +static +int optmatch(const char *argval, const char *optname) +{ + int result = 0; + + while ((result == 0) && (*optname != 0) && (*argval != 0)) + result = (*argval++) - (*optname++); + return result; +} + +/* Handling for a single long option. */ +static +int getopt_1long(const int argc, + char *const argv[], + const struct option *const longopts, + const char *const optname, + int *const indexptr) +{ + int result = RET_UNKNOWN_OPT; + size_t loptn = 0; + bool match_found = false; + + /* + * Long option names may be abbreviated if the abbreviation + * is unique or an exact match for some defined option. + * To handle this: + * - First search for an exact match. + * - If exact match was not found search for a abbreviated match. + * By doing this an incorrect option selection can be avoided. + */ + + /* 1. Search for an exact match. */ + while (longopts[loptn].name != NULL) { + if (strcmp(optname, longopts[loptn].name) == 0) { + match_found = true; + break; + } + ++loptn; + } + + /* 2. If exact match was not found search for a abbreviated match. */ + if (!match_found) { + loptn = 0; + while (longopts[loptn].name != NULL) { + if (optmatch(optname, longopts[loptn].name) == 0) { + match_found = true; + break; + } + ++loptn; + } + } + + if (match_found) { + /* We found a match. */ + result = longopts[loptn].val; + if (indexptr != 0) { + *indexptr = loptn; + } + switch (longopts[loptn].has_arg) { + case required_argument: + if ((optind + 1) >= argc) { + /* Missing argument. */ + optopt = result; + return RET_NO_PARAM; + } + /* Fallthrough to get option value. */ + + case optional_argument: + if ((argc - optind) > 0) { + /* Found argument. */ + optarg = argv[++optind]; + } + /* Fallthrough to handle flag. */ + + case no_argument: + optind++; + if (longopts[loptn].flag != 0) { + *longopts[loptn].flag = result; + result = 0; + } + break; + + } + return result; + } + + /* + * If getopt finds an option character in argv that was not included + * in options, ... it returns '?' and sets the external variable + * optopt to the actual option character. + */ + return RET_UNKNOWN_OPT; +} + +/* + * getopt_long gets the next option argument from the argument list + * specified by the argv and argc arguments. Options may be either short + * (single letter) as for getopt, or longer names (preceded by --). + */ +int getopt_long(int argc, + char *argv[], + const char *shortopts, + const struct option *longopts, + int *indexptr) +{ + int result = RET_END_OPT_LIST; + + getopt_init(); + /* If we have an argument left to play with */ + if ((argc > optind) && (argv != 0)) { + const char *arg = argv[optind]; + + if ((arg != 0) && (arg[0] == '-')) { + if (arg[1] == '-') { + /* Looks like a long option. */ + result = getopt_1long(argc, + argv, + longopts, + &arg[2], + indexptr); + } else { + result = getopt_1char(argc, + argv, + shortopts, + arg[1]); + } + } + } + return result; +} + +/* + * getopt_long_only gets the next option argument from the argument list + * specified by the argv and argc arguments. Options may be either short + * or long as for getopt_long, but the long names may have a single '-' + * prefix too. + */ +int getopt_long_only(int argc, + char *argv[], + const char *shortopts, + const struct option *longopts, + int *indexptr) +{ + int result = RET_END_OPT_LIST; + + getopt_init(); + /* If we have an argument left to play with */ + if ((argc > optind) && (argv != 0)) { + const char *arg = argv[optind]; + + if ((arg != 0) && (arg[0] == '-')) { + if (arg[1] == '-') { + /* Looks like a long option. */ + result = getopt_1long(argc, + argv, + longopts, + &arg[2], + indexptr); + } else { + result = getopt_1long(argc, + argv, + longopts, + &arg[1], + indexptr); + if (result == RET_UNKNOWN_OPT) { + result = getopt_1char(argc, + argv, + shortopts, + arg[1]); + } + } + } + } + return result; +} diff --git a/tools/fiptool/win_posix.h b/tools/fiptool/win_posix.h new file mode 100644 index 0000000..1340640 --- /dev/null +++ b/tools/fiptool/win_posix.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef WIN_POSIX_H +#define WIN_POSIX_H + +#define _CRT_SECURE_NO_WARNINGS + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <direct.h> +#include <io.h> + +#include "uuid.h" + +/* Derive or provide Windows equivalents of Posix/GCC/Unix stuff. */ +#ifndef PATH_MAX +# ifdef MAX_PATH +# define PATH_MAX MAX_PATH +# else +# ifdef _MAX_PATH +# define MAX_PATH _MAX_PATH +# define PATH_MAX _MAX_PATH +# else +# define PATH_MAX 260 +# endif +# endif +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS 1 +#endif + +/* + * Platform specific names. + * + * Visual Studio deprecates a number of POSIX functions and only provides + * ISO C++ compliant alternatives (distinguished by their '_' prefix). + * These macros help provide a stopgap for that. + */ + +/* fileno cannot be an inline function, because _fileno is a macro. */ +#define fileno(fileptr) _fileno(fileptr) + +/* _fstat uses the _stat structure, not stat. */ +#define BLD_PLAT_STAT _stat + +/* Define flag values for _access. */ +#define F_OK 0 + + +/* getopt implementation for Windows: Data. */ + +/* Legitimate values for option.has_arg. */ +enum has_arg_values { + no_argument, /* No argument value required */ + required_argument, /* value must be specified. */ + optional_argument /* value may be specified. */ +}; + +/* Long option table entry for get_opt_long. */ +struct option { + /* The name of the option. */ + const char *name; + + /* + * Indicates whether the option takes an argument. + * Possible values: see has_arg_values above. + */ + int has_arg; + + /* If not null, when option present, *flag is set to val. */ + int *flag; + + /* + * The value associated with this option to return + * (and save in *flag when not null) + */ + int val; +}; + +/* + * This variable is set by getopt to point at the value of the option + * argument, for those options that accept arguments. + */ +extern char *optarg; + +/* + * When this variable is not zero, getopt emits an error message to stderr + * if it encounters an unspecified option, or a missing argument. + * Otherwise no message is reported. + */ +extern const int opterr; /* const as NOT used in this implementation. */ + +/* + * This variable is set by getopt to the index of the next element of the + * argv array to be processed. Once getopt has found all of the option + * arguments, you can use this variable to determine where the remaining + * non-option arguments begin. The initial value of this variable is 1. + */ +extern int optind; + +/* + * When getopt encounters an unknown option character or an option with a + * missing required argument, it stores that option character in this + * variable. + */ +extern int optopt; + + +/* + * Platform specific names. + * + * Visual Studio deprecates a number of POSIX functions and only provides + * ISO C++ compliant alternatives (distinguished by their '_' prefix). + * These inline functions provide a stopgap for that. + */ + +inline int access(const char *path, int mode) +{ + return _access(path, mode); +} + +inline int chdir(const char *s) +{ + return _chdir(s); +} + +inline int fstat(int fd, struct _stat *buffer) +{ + return _fstat(fd, buffer); +} + +inline char *strdup(const char *s) +{ + return _strdup(s); +} + +/* + * getopt implementation for Windows: Functions. + * + * Windows does not have the getopt family of functions, as it normally + * uses '/' instead of '-' as the command line option delimiter. + * These functions provide a Windows version that uses '-', which precludes + * using '-' as the initial letter of a program argument. + * This is not seen as a problem in the specific instance of fiptool, + * and enables existing makefiles to work on a Windows build environment. + */ + +/* + * The getopt function gets the next option argument from the argument list + * specified by the argv and argc arguments. + */ +int getopt(int argc, + char *argv[], + char *options); + +/* + * getopt_long gets the next option argument from the argument list + * specified by the argv and argc arguments. Options may be either short + * (single letter) as for getopt, or longer names (preceded by --). + */ +int getopt_long(int argc, + char *argv[], + const char *shortopts, + const struct option *longopts, + int *indexptr); + +/* + * getopt_long_only gets the next option argument from the argument list + * specified by the argv and argc arguments. Options may be either short + * or long as for getopt_long, but the long names may have a single '-' + * prefix, too. + */ +int getopt_long_only(int argc, + char *argv[], + const char *shortopts, + const struct option *longopts, + int *indexptr); + +#endif /* WIN_POSIX_H */ diff --git a/tools/marvell/doimage/Makefile b/tools/marvell/doimage/Makefile new file mode 100644 index 0000000..9f0d89d --- /dev/null +++ b/tools/marvell/doimage/Makefile @@ -0,0 +1,48 @@ +# +# Copyright (C) 2018 Marvell International Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses + +PROJECT = doimage +OBJECTS = doimage.o + +HOSTCCFLAGS = -Wall -Werror +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG +else + HOSTCCFLAGS += -O2 +endif + +ifeq (${MARVELL_SECURE_BOOT},1) +DOIMAGE_CC_FLAGS := -DCONFIG_MVEBU_SECURE_BOOT +DOIMAGE_LD_FLAGS := -lconfig -lmbedtls -lmbedcrypto -lmbedx509 +endif + +HOSTCCFLAGS += ${DOIMAGE_CC_FLAGS} + +# Make soft links and include from local directory otherwise wrong headers +# could get pulled in from firmware tree. +INCLUDE_PATHS = -I. + +HOSTCC ?= gcc +RM := rm -rf + +.PHONY: all clean + +all: ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + @echo " HOSTLD $@" + ${Q}${HOSTCC} ${OBJECTS} ${DOIMAGE_LD_FLAGS} -o $@ + @echo + @echo "Built $@ successfully" + @echo + +%.o: %.c Makefile + @echo " HOSTCC $<" + ${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ + +clean: + ${Q}${RM} ${PROJECT} + ${Q}${RM} ${OBJECTS} diff --git a/tools/marvell/doimage/doimage.c b/tools/marvell/doimage/doimage.c new file mode 100644 index 0000000..513f33f --- /dev/null +++ b/tools/marvell/doimage/doimage.c @@ -0,0 +1,1759 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/time.h> + +#ifdef CONFIG_MVEBU_SECURE_BOOT +#include <libconfig.h> /* for parsing config file */ + +/* mbedTLS stuff */ +#if defined(MBEDTLS_BIGNUM_C) && defined(MBEDTLS_ENTROPY_C) && \ + defined(MBEDTLS_SHA256_C) && \ + defined(MBEDTLS_PK_PARSE_C) && defined(MBEDTLS_FS_IO) && \ + defined(MBEDTLS_CTR_DRBG_C) +#include <mbedtls/error.h> +#include <mbedtls/entropy.h> +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/md.h> +#include <mbedtls/pk.h> +#include <mbedtls/sha256.h> +#include <mbedtls/version.h> +#include <mbedtls/x509.h> +#else +#error "Bad mbedTLS configuration!" +#endif +#endif /* CONFIG_MVEBU_SECURE_BOOT */ + +#define MAX_FILENAME 256 +#define CSK_ARR_SZ 16 +#define CSK_ARR_EMPTY_FILE "*" +#define AES_KEY_BIT_LEN 256 +#define AES_KEY_BYTE_LEN (AES_KEY_BIT_LEN >> 3) +#define AES_BLOCK_SZ 16 +#define RSA_SIGN_BYTE_LEN 256 +#define MAX_RSA_DER_BYTE_LEN 524 +/* Number of address pairs in control array */ +#define CP_CTRL_EL_ARRAY_SZ 32 + +#define VERSION_STRING "Marvell(C) doimage utility version 3.3" + +/* A8K definitions */ + +/* Extension header types */ +#define EXT_TYPE_SECURITY 0x1 +#define EXT_TYPE_BINARY 0x2 + +#define MAIN_HDR_MAGIC 0xB105B002 + +/* PROLOG alignment considerations: + * 128B: To allow supporting XMODEM protocol. + * 8KB: To align the boot image to the largest NAND page size, and simplify + * the read operations from NAND. + * We choose the largest page size, in order to use a single image for all + * NAND page sizes. + */ +#define PROLOG_ALIGNMENT (8 << 10) + +/* UART argument bitfield */ +#define UART_MODE_UNMODIFIED 0x0 +#define UART_MODE_DISABLE 0x1 +#define UART_MODE_UPDATE 0x2 + +typedef struct _main_header { + uint32_t magic; /* 0-3 */ + uint32_t prolog_size; /* 4-7 */ + uint32_t prolog_checksum; /* 8-11 */ + uint32_t boot_image_size; /* 12-15 */ + uint32_t boot_image_checksum; /* 16-19 */ + uint32_t rsrvd0; /* 20-23 */ + uint32_t load_addr; /* 24-27 */ + uint32_t exec_addr; /* 28-31 */ + uint8_t uart_cfg; /* 32 */ + uint8_t baudrate; /* 33 */ + uint8_t ext_count; /* 34 */ + uint8_t aux_flags; /* 35 */ + uint32_t io_arg_0; /* 36-39 */ + uint32_t io_arg_1; /* 40-43 */ + uint32_t io_arg_2; /* 43-47 */ + uint32_t io_arg_3; /* 48-51 */ + uint32_t rsrvd1; /* 52-55 */ + uint32_t rsrvd2; /* 56-59 */ + uint32_t rsrvd3; /* 60-63 */ +} header_t; + +typedef struct _ext_header { + uint8_t type; + uint8_t offset; + uint16_t reserved; + uint32_t size; +} ext_header_t; + +typedef struct _sec_entry { + uint8_t kak_key[MAX_RSA_DER_BYTE_LEN]; + uint32_t jtag_delay; + uint32_t box_id; + uint32_t flash_id; + uint32_t jtag_en; + uint32_t encrypt_en; + uint32_t efuse_dis; + uint8_t header_sign[RSA_SIGN_BYTE_LEN]; + uint8_t image_sign[RSA_SIGN_BYTE_LEN]; + uint8_t csk_keys[CSK_ARR_SZ][MAX_RSA_DER_BYTE_LEN]; + uint8_t csk_sign[RSA_SIGN_BYTE_LEN]; + uint32_t cp_ctrl_arr[CP_CTRL_EL_ARRAY_SZ]; + uint32_t cp_efuse_arr[CP_CTRL_EL_ARRAY_SZ]; +} sec_entry_t; + +/* A8K definitions end */ + +/* UART argument bitfield */ +#define UART_MODE_UNMODIFIED 0x0 +#define UART_MODE_DISABLE 0x1 +#define UART_MODE_UPDATE 0x2 + +#define uart_set_mode(arg, mode) (arg |= (mode & 0x3)) + +typedef struct _sec_options { +#ifdef CONFIG_MVEBU_SECURE_BOOT + char aes_key_file[MAX_FILENAME+1]; + char kak_key_file[MAX_FILENAME+1]; + char csk_key_file[CSK_ARR_SZ][MAX_FILENAME+1]; + uint32_t box_id; + uint32_t flash_id; + uint32_t jtag_delay; + uint8_t csk_index; + uint8_t jtag_enable; + uint8_t efuse_disable; + uint32_t cp_ctrl_arr[CP_CTRL_EL_ARRAY_SZ]; + uint32_t cp_efuse_arr[CP_CTRL_EL_ARRAY_SZ]; + mbedtls_pk_context kak_pk; + mbedtls_pk_context csk_pk[CSK_ARR_SZ]; + uint8_t aes_key[AES_KEY_BYTE_LEN]; + uint8_t *encrypted_image; + uint32_t enc_image_sz; +#endif +} sec_options; + +typedef struct _options { + char bin_ext_file[MAX_FILENAME+1]; + char sec_cfg_file[MAX_FILENAME+1]; + sec_options *sec_opts; + uint32_t load_addr; + uint32_t exec_addr; + uint32_t baudrate; + uint8_t disable_print; + int8_t key_index; /* For header signatures verification only */ + uint32_t nfc_io_args; +} options_t; + +void usage_err(char *msg) +{ + fprintf(stderr, "Error: %s\n", msg); + fprintf(stderr, "run 'doimage -h' to get usage information\n"); + exit(-1); +} + +void usage(void) +{ + printf("\n\n%s\n\n", VERSION_STRING); + printf("Usage: doimage [options] <input_file> [output_file]\n"); + printf("create bootrom image from u-boot and boot extensions\n\n"); + + printf("Arguments\n"); + printf(" input_file name of boot image file.\n"); + printf(" if -p is used, name of the bootrom image file"); + printf(" to parse.\n"); + printf(" output_file name of output bootrom image file\n"); + + printf("\nOptions\n"); + printf(" -s target SOC name. supports a8020,a7020\n"); + printf(" different SOCs may have different boot image\n"); + printf(" format so it's mandatory to know the target SOC\n"); + printf(" -i boot I/F name. supports nand, spi, nor\n"); + printf(" This affects certain parameters coded in the\n"); + printf(" image header\n"); + printf(" -l boot image load address. default is 0x0\n"); + printf(" -e boot image entry address. default is 0x0\n"); + printf(" -b binary extension image file.\n"); + printf(" This image is executed before the boot image.\n"); + printf(" This is typically used to initialize the memory "); + printf(" controller.\n"); + printf(" Currently supports only a single file.\n"); +#ifdef CONFIG_MVEBU_SECURE_BOOT + printf(" -c Make trusted boot image using parameters\n"); + printf(" from the configuration file.\n"); +#endif + printf(" -p Parse and display a pre-built boot image\n"); +#ifdef CONFIG_MVEBU_SECURE_BOOT + printf(" -k Key index for RSA signatures verification\n"); + printf(" when parsing the boot image\n"); +#endif + printf(" -m Disable prints of bootrom and binary extension\n"); + printf(" -u UART baudrate used for bootrom prints.\n"); + printf(" Must be multiple of 1200\n"); + printf(" -h Show this help message\n"); + printf(" IO-ROM NFC-NAND boot parameters:\n"); + printf(" -n NAND device block size in KB [Default is 64KB].\n"); + printf(" -t NAND cell technology (SLC [Default] or MLC)\n"); + + exit(-1); +} + +/* globals */ +static options_t opts = { + .bin_ext_file = "NA", + .sec_cfg_file = "NA", + .sec_opts = 0, + .load_addr = 0x0, + .exec_addr = 0x0, + .disable_print = 0, + .baudrate = 0, + .key_index = -1, +}; + +int get_file_size(char *filename) +{ + struct stat st; + + if (stat(filename, &st) == 0) + return st.st_size; + + return -1; +} + +uint32_t checksum32(uint32_t *start, int len) +{ + uint32_t sum = 0; + uint32_t *startp = start; + + do { + sum += *startp; + startp++; + len -= 4; + } while (len > 0); + + return sum; +} + +/******************************************************************************* + * create_rsa_signature (memory buffer content) + * Create RSASSA-PSS/SHA-256 signature for memory buffer + * using RSA Private Key + * INPUT: + * pk_ctx Private Key context + * input memory buffer + * ilen buffer length + * pers personalization string for seeding the RNG. + * For instance a private key file name. + * OUTPUT: + * signature RSA-2048 signature + * RETURN: + * 0 on success + */ +#ifdef CONFIG_MVEBU_SECURE_BOOT +int create_rsa_signature(mbedtls_pk_context *pk_ctx, + const unsigned char *input, + size_t ilen, + const char *pers, + uint8_t *signature) +{ + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + unsigned char hash[32]; + unsigned char buf[MBEDTLS_MPI_MAX_SIZE]; + int rval; + + /* Not sure this is required, + * but it's safer to start with empty buffers + */ + memset(hash, 0, sizeof(hash)); + memset(buf, 0, sizeof(buf)); + + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + + /* Seed the random number generator */ + rval = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)pers, strlen(pers)); + if (rval != 0) { + fprintf(stderr, " Failed in ctr_drbg_init call (%d)!\n", rval); + goto sign_exit; + } + + /* The PK context should be already initialized. + * Set the padding type for this PK context + */ + mbedtls_rsa_set_padding(mbedtls_pk_rsa(*pk_ctx), + MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); + + /* First compute the SHA256 hash for the input blob */ + mbedtls_sha256_ret(input, ilen, hash, 0); + + /* Then calculate the hash signature */ + rval = mbedtls_rsa_rsassa_pss_sign(mbedtls_pk_rsa(*pk_ctx), + mbedtls_ctr_drbg_random, + &ctr_drbg, + MBEDTLS_RSA_PRIVATE, + MBEDTLS_MD_SHA256, 0, hash, buf); + if (rval != 0) { + fprintf(stderr, + "Failed to create RSA signature for %s. Error %d\n", + pers, rval); + goto sign_exit; + } + memcpy(signature, buf, 256); + +sign_exit: + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + return rval; +} /* end of create_rsa_signature */ + +/******************************************************************************* + * verify_rsa_signature (memory buffer content) + * Verify RSASSA-PSS/SHA-256 signature for memory buffer + * using RSA Public Key + * INPUT: + * pub_key Public Key buffer + * ilen Public Key buffer length + * input memory buffer + * ilen buffer length + * pers personalization string for seeding the RNG. + * signature RSA-2048 signature + * OUTPUT: + * none + * RETURN: + * 0 on success + */ +int verify_rsa_signature(const unsigned char *pub_key, + size_t klen, + const unsigned char *input, + size_t ilen, + const char *pers, + uint8_t *signature) +{ + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_pk_context pk_ctx; + unsigned char hash[32]; + int rval; + unsigned char *pkey = (unsigned char *)pub_key; + + /* Not sure this is required, + * but it's safer to start with empty buffer + */ + memset(hash, 0, sizeof(hash)); + + mbedtls_pk_init(&pk_ctx); + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + + /* Seed the random number generator */ + rval = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)pers, strlen(pers)); + if (rval != 0) { + fprintf(stderr, " Failed in ctr_drbg_init call (%d)!\n", rval); + goto verify_exit; + } + + /* Check ability to read the public key */ + rval = mbedtls_pk_parse_subpubkey(&pkey, pub_key + klen, &pk_ctx); + if (rval != 0) { + fprintf(stderr, " Failed in pk_parse_public_key (%#x)!\n", + rval); + goto verify_exit; + } + + /* Set the padding type for the new PK context */ + mbedtls_rsa_set_padding(mbedtls_pk_rsa(pk_ctx), + MBEDTLS_RSA_PKCS_V21, + MBEDTLS_MD_SHA256); + + /* Compute the SHA256 hash for the input buffer */ + mbedtls_sha256_ret(input, ilen, hash, 0); + + rval = mbedtls_rsa_rsassa_pss_verify(mbedtls_pk_rsa(pk_ctx), + mbedtls_ctr_drbg_random, + &ctr_drbg, + MBEDTLS_RSA_PUBLIC, + MBEDTLS_MD_SHA256, 0, + hash, signature); + if (rval != 0) + fprintf(stderr, "Failed to verify signature (%d)!\n", rval); + +verify_exit: + + mbedtls_pk_free(&pk_ctx); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + return rval; +} /* end of verify_rsa_signature */ + +/******************************************************************************* + * image_encrypt + * Encrypt image buffer using AES-256-CBC scheme. + * The resulting image is saved into opts.sec_opts->encrypted_image + * and the adjusted image size into opts.sec_opts->enc_image_sz + * First AES_BLOCK_SZ bytes of the output image contain IV + * INPUT: + * buf Source buffer to encrypt + * blen Source buffer length + * OUTPUT: + * none + * RETURN: + * 0 on success + */ +int image_encrypt(uint8_t *buf, uint32_t blen) +{ + struct timeval tv; + char *ptmp = (char *)&tv; + unsigned char digest[32]; + unsigned char IV[AES_BLOCK_SZ]; + int i, k; + mbedtls_aes_context aes_ctx; + int rval = -1; + uint8_t *test_img = 0; + + if (AES_BLOCK_SZ > 32) { + fprintf(stderr, "Unsupported AES block size %d\n", + AES_BLOCK_SZ); + return rval; + } + + mbedtls_aes_init(&aes_ctx); + memset(IV, 0, AES_BLOCK_SZ); + memset(digest, 0, 32); + + /* Generate initialization vector and init the AES engine + * Use file name XOR current time and finally SHA-256 + * [0...AES_BLOCK_SZ-1] + */ + k = strlen(opts.sec_opts->aes_key_file); + if (k > AES_BLOCK_SZ) + k = AES_BLOCK_SZ; + memcpy(IV, opts.sec_opts->aes_key_file, k); + gettimeofday(&tv, 0); + + for (i = 0, k = 0; i < AES_BLOCK_SZ; i++, + k = (k+1) % sizeof(struct timeval)) + IV[i] ^= ptmp[k]; + + /* compute SHA-256 digest of the results + * and use it as the init vector (IV) + */ + mbedtls_sha256_ret(IV, AES_BLOCK_SZ, digest, 0); + memcpy(IV, digest, AES_BLOCK_SZ); + mbedtls_aes_setkey_enc(&aes_ctx, opts.sec_opts->aes_key, + AES_KEY_BIT_LEN); + + /* The output image has to include extra space for IV + * and to be aligned to the AES block size. + * The input image buffer has to be already aligned to AES_BLOCK_SZ + * and padded with zeroes + */ + opts.sec_opts->enc_image_sz = (blen + 2 * AES_BLOCK_SZ - 1) & + ~(AES_BLOCK_SZ - 1); + opts.sec_opts->encrypted_image = calloc(opts.sec_opts->enc_image_sz, 1); + if (opts.sec_opts->encrypted_image == 0) { + fprintf(stderr, "Failed to allocate encrypted image!\n"); + goto encrypt_exit; + } + + /* Put IV into the output buffer next to the encrypted image + * Since the IV is modified by the encryption function, + * this should be done now + */ + memcpy(opts.sec_opts->encrypted_image + + opts.sec_opts->enc_image_sz - AES_BLOCK_SZ, + IV, AES_BLOCK_SZ); + rval = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, + opts.sec_opts->enc_image_sz - AES_BLOCK_SZ, + IV, buf, opts.sec_opts->encrypted_image); + if (rval != 0) { + fprintf(stderr, "Failed to encrypt the image! Error %d\n", + rval); + goto encrypt_exit; + } + + mbedtls_aes_free(&aes_ctx); + + /* Try to decrypt the image and compare it with the original data */ + mbedtls_aes_init(&aes_ctx); + mbedtls_aes_setkey_dec(&aes_ctx, opts.sec_opts->aes_key, + AES_KEY_BIT_LEN); + + test_img = calloc(opts.sec_opts->enc_image_sz - AES_BLOCK_SZ, 1); + if (test_img == 0) { + fprintf(stderr, "Failed to allocate test image!d\n"); + rval = -1; + goto encrypt_exit; + } + + memcpy(IV, opts.sec_opts->encrypted_image + + opts.sec_opts->enc_image_sz - AES_BLOCK_SZ, + AES_BLOCK_SZ); + rval = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, + opts.sec_opts->enc_image_sz - AES_BLOCK_SZ, + IV, opts.sec_opts->encrypted_image, test_img); + if (rval != 0) { + fprintf(stderr, "Failed to decrypt the image! Error %d\n", + rval); + goto encrypt_exit; + } + + for (i = 0; i < blen; i++) { + if (buf[i] != test_img[i]) { + fprintf(stderr, "Failed to compare the image after"); + fprintf(stderr, " decryption! Byte count is %d\n", i); + rval = -1; + goto encrypt_exit; + } + } + +encrypt_exit: + + mbedtls_aes_free(&aes_ctx); + if (test_img) + free(test_img); + + return rval; +} /* end of image_encrypt */ + +/******************************************************************************* + * verify_secure_header_signatures + * Verify CSK array, header and image signatures and print results + * INPUT: + * main_hdr Main header + * sec_ext Secure extension + * OUTPUT: + * none + * RETURN: + * 0 on success + */ +int verify_secure_header_signatures(header_t *main_hdr, sec_entry_t *sec_ext) +{ + uint8_t *image = (uint8_t *)main_hdr + main_hdr->prolog_size; + uint8_t signature[RSA_SIGN_BYTE_LEN]; + int rval = -1; + + /* Save headers signature and reset it in the secure header */ + memcpy(signature, sec_ext->header_sign, RSA_SIGN_BYTE_LEN); + memset(sec_ext->header_sign, 0, RSA_SIGN_BYTE_LEN); + + fprintf(stdout, "\nCheck RSA Signatures\n"); + fprintf(stdout, "#########################\n"); + fprintf(stdout, "CSK Block Signature: "); + if (verify_rsa_signature(sec_ext->kak_key, + MAX_RSA_DER_BYTE_LEN, + &sec_ext->csk_keys[0][0], + sizeof(sec_ext->csk_keys), + "CSK Block Signature: ", + sec_ext->csk_sign) != 0) { + fprintf(stdout, "ERROR\n"); + goto ver_error; + } + fprintf(stdout, "OK\n"); + + if (opts.key_index != -1) { + fprintf(stdout, "Image Signature: "); + if (verify_rsa_signature(sec_ext->csk_keys[opts.key_index], + MAX_RSA_DER_BYTE_LEN, + image, main_hdr->boot_image_size, + "Image Signature: ", + sec_ext->image_sign) != 0) { + fprintf(stdout, "ERROR\n"); + goto ver_error; + } + fprintf(stdout, "OK\n"); + + fprintf(stdout, "Header Signature: "); + if (verify_rsa_signature(sec_ext->csk_keys[opts.key_index], + MAX_RSA_DER_BYTE_LEN, + (uint8_t *)main_hdr, + main_hdr->prolog_size, + "Header Signature: ", + signature) != 0) { + fprintf(stdout, "ERROR\n"); + goto ver_error; + } + fprintf(stdout, "OK\n"); + } else { + fprintf(stdout, "SKIP Image and Header Signatures"); + fprintf(stdout, " check (undefined key index)\n"); + } + + rval = 0; + +ver_error: + memcpy(sec_ext->header_sign, signature, RSA_SIGN_BYTE_LEN); + return rval; +} + +/******************************************************************************* + * verify_and_copy_file_name_entry + * INPUT: + * element_name + * element + * OUTPUT: + * copy_to + * RETURN: + * 0 on success + */ +int verify_and_copy_file_name_entry(const char *element_name, + const char *element, char *copy_to) +{ + int element_length = strlen(element); + + if (element_length >= MAX_FILENAME) { + fprintf(stderr, "The file name %s for %s is too long (%d). ", + element, element_name, element_length); + fprintf(stderr, "Maximum allowed %d characters!\n", + MAX_FILENAME); + return -1; + } else if (element_length == 0) { + fprintf(stderr, "The file name for %s is empty!\n", + element_name); + return -1; + } + memcpy(copy_to, element, element_length); + + return 0; +} + +/******************************************************************************* + * parse_sec_config_file + * Read the secure boot configuration from a file + * into internal structures + * INPUT: + * filename File name + * OUTPUT: + * none + * RETURN: + * 0 on success + */ +int parse_sec_config_file(char *filename) +{ + config_t sec_cfg; + int array_sz, element, rval = -1; + const char *cfg_string; + int32_t cfg_int32; + const config_setting_t *csk_array, *control_array; + sec_options *sec_opt = 0; + + config_init(&sec_cfg); + + if (config_read_file(&sec_cfg, filename) != CONFIG_TRUE) { + fprintf(stderr, "Failed to read data from config file "); + fprintf(stderr, "%s\n\t%s at line %d\n", + filename, config_error_text(&sec_cfg), + config_error_line(&sec_cfg)); + goto exit_parse; + } + + sec_opt = (sec_options *)calloc(sizeof(sec_options), 1); + if (sec_opt == 0) { + fprintf(stderr, + "Cannot allocate memory for secure boot options!\n"); + goto exit_parse; + } + + /* KAK file name */ + if (config_lookup_string(&sec_cfg, "kak_key_file", + &cfg_string) != CONFIG_TRUE) { + fprintf(stderr, "The \"kak_key_file\" undefined!\n"); + goto exit_parse; + } + if (verify_and_copy_file_name_entry("kak_key_file", + cfg_string, sec_opt->kak_key_file)) + goto exit_parse; + + + /* AES file name - can be empty/undefined */ + if (config_lookup_string(&sec_cfg, "aes_key_file", + &cfg_string) == CONFIG_TRUE) { + if (verify_and_copy_file_name_entry("aes_key_file", + cfg_string, + sec_opt->aes_key_file)) + goto exit_parse; + } + + /* CSK file names array */ + csk_array = config_lookup(&sec_cfg, "csk_key_file"); + if (csk_array == NULL) { + fprintf(stderr, "The \"csk_key_file\" undefined!\n"); + goto exit_parse; + } + array_sz = config_setting_length(csk_array); + if (array_sz > CSK_ARR_SZ) { + fprintf(stderr, "The \"csk_key_file\" array is too big! "); + fprintf(stderr, "Only first %d elements will be used\n", + CSK_ARR_SZ); + array_sz = CSK_ARR_SZ; + } else if (array_sz == 0) { + fprintf(stderr, "The \"csk_key_file\" array is empty!\n"); + goto exit_parse; + } + + for (element = 0; element < array_sz; element++) { + cfg_string = config_setting_get_string_elem(csk_array, element); + if (verify_and_copy_file_name_entry( + "csk_key_file", cfg_string, + sec_opt->csk_key_file[element])) { + fprintf(stderr, "Bad csk_key_file[%d] entry!\n", + element); + goto exit_parse; + } + } + + /* JTAG options */ + if (config_lookup_bool(&sec_cfg, "jtag.enable", + &cfg_int32) != CONFIG_TRUE) { + fprintf(stderr, "Error obtaining \"jtag.enable\" element. "); + fprintf(stderr, "Using default - FALSE\n"); + cfg_int32 = 0; + } + sec_opt->jtag_enable = cfg_int32; + + if (config_lookup_int(&sec_cfg, "jtag.delay", + &cfg_int32) != CONFIG_TRUE) { + fprintf(stderr, "Error obtaining \"jtag.delay\" element. "); + fprintf(stderr, "Using default - 0us\n"); + cfg_int32 = 0; + } + sec_opt->jtag_delay = cfg_int32; + + /* eFUSE option */ + if (config_lookup_bool(&sec_cfg, "efuse_disable", + &cfg_int32) != CONFIG_TRUE) { + fprintf(stderr, "Error obtaining \"efuse_disable\" element. "); + fprintf(stderr, "Using default - TRUE\n"); + cfg_int32 = 1; + } + sec_opt->efuse_disable = cfg_int32; + + /* Box ID option */ + if (config_lookup_int(&sec_cfg, "box_id", &cfg_int32) != CONFIG_TRUE) { + fprintf(stderr, "Error obtaining \"box_id\" element. "); + fprintf(stderr, "Using default - 0x0\n"); + cfg_int32 = 0; + } + sec_opt->box_id = cfg_int32; + + /* Flash ID option */ + if (config_lookup_int(&sec_cfg, "flash_id", + &cfg_int32) != CONFIG_TRUE) { + fprintf(stderr, "Error obtaining \"flash_id\" element. "); + fprintf(stderr, "Using default - 0x0\n"); + cfg_int32 = 0; + } + sec_opt->flash_id = cfg_int32; + + /* CSK index option */ + if (config_lookup_int(&sec_cfg, "csk_key_index", + &cfg_int32) != CONFIG_TRUE) { + fprintf(stderr, "Error obtaining \"flash_id\" element. "); + fprintf(stderr, "Using default - 0x0\n"); + cfg_int32 = 0; + } + sec_opt->csk_index = cfg_int32; + + /* Secure boot control array */ + control_array = config_lookup(&sec_cfg, "control"); + if (control_array != NULL) { + array_sz = config_setting_length(control_array); + if (array_sz == 0) + fprintf(stderr, "The \"control\" array is empty!\n"); + } else { + fprintf(stderr, "The \"control\" is undefined!\n"); + array_sz = 0; + } + + for (element = 0; element < CP_CTRL_EL_ARRAY_SZ; element++) { + sec_opt->cp_ctrl_arr[element] = + config_setting_get_int_elem(control_array, element * 2); + sec_opt->cp_efuse_arr[element] = + config_setting_get_int_elem(control_array, + element * 2 + 1); + } + + opts.sec_opts = sec_opt; + rval = 0; + +exit_parse: + config_destroy(&sec_cfg); + if (sec_opt && (rval != 0)) + free(sec_opt); + return rval; +} /* end of parse_sec_config_file */ + +int format_sec_ext(char *filename, FILE *out_fd) +{ + ext_header_t header; + sec_entry_t sec_ext; + int index; + int written; + +#define DER_BUF_SZ 1600 + + /* First, parse the configuration file */ + if (parse_sec_config_file(filename)) { + fprintf(stderr, + "failed parsing configuration file %s\n", filename); + return 1; + } + + /* Everything except signatures can be created at this stage */ + header.type = EXT_TYPE_SECURITY; + header.offset = 0; + header.size = sizeof(sec_entry_t); + header.reserved = 0; + + /* Bring up RSA context and read private keys from their files */ + for (index = 0; index < (CSK_ARR_SZ + 1); index++) { + /* for every private key file */ + mbedtls_pk_context *pk_ctx = (index == CSK_ARR_SZ) ? + &opts.sec_opts->kak_pk : + &opts.sec_opts->csk_pk[index]; + char *fname = (index == CSK_ARR_SZ) ? + opts.sec_opts->kak_key_file : + opts.sec_opts->csk_key_file[index]; + uint8_t *out_der_key = (index == CSK_ARR_SZ) ? + sec_ext.kak_key : + sec_ext.csk_keys[index]; + size_t output_len; + unsigned char output_buf[DER_BUF_SZ]; + unsigned char *der_buf_start; + + /* Handle invalid/reserved file names */ + if (strncmp(CSK_ARR_EMPTY_FILE, fname, + strlen(CSK_ARR_EMPTY_FILE)) == 0) { + if (opts.sec_opts->csk_index == index) { + fprintf(stderr, + "CSK file with index %d cannot be %s\n", + index, CSK_ARR_EMPTY_FILE); + return 1; + } else if (index == CSK_ARR_SZ) { + fprintf(stderr, "KAK file name cannot be %s\n", + CSK_ARR_EMPTY_FILE); + return 1; + } + /* this key will be empty in CSK array */ + continue; + } + + mbedtls_pk_init(pk_ctx); + /* Read the private RSA key into the context + * and verify it (no password) + */ + if (mbedtls_pk_parse_keyfile(pk_ctx, fname, "") != 0) { + fprintf(stderr, + "Cannot read RSA private key file %s\n", fname); + return 1; + } + + /* Create a public key out of private one + * and store it in DER format + */ + output_len = mbedtls_pk_write_pubkey_der(pk_ctx, + output_buf, + DER_BUF_SZ); + if (output_len < 0) { + fprintf(stderr, + "Failed to create DER coded PUB key (%s)\n", + fname); + return 1; + } + + /* Data in the output buffer is aligned to the buffer end */ + der_buf_start = output_buf + sizeof(output_buf) - output_len; + /* In the header DER data is aligned + * to the start of appropriate field + */ + bzero(out_der_key, MAX_RSA_DER_BYTE_LEN); + memcpy(out_der_key, der_buf_start, output_len); + + } /* for every private key file */ + + /* The CSK block signature can be created here */ + if (create_rsa_signature(&opts.sec_opts->kak_pk, + &sec_ext.csk_keys[0][0], + sizeof(sec_ext.csk_keys), + opts.sec_opts->csk_key_file[ + opts.sec_opts->csk_index], + sec_ext.csk_sign) != 0) { + fprintf(stderr, "Failed to sign CSK keys block!\n"); + return 1; + } + + /* Check that everything is correct */ + if (verify_rsa_signature(sec_ext.kak_key, + MAX_RSA_DER_BYTE_LEN, + &sec_ext.csk_keys[0][0], + sizeof(sec_ext.csk_keys), + opts.sec_opts->kak_key_file, + sec_ext.csk_sign) != 0) { + fprintf(stderr, "Failed to verify CSK keys block signature!\n"); + return 1; + } + + /* AES encryption stuff */ + if (strlen(opts.sec_opts->aes_key_file) != 0) { + FILE *in_fd; + + in_fd = fopen(opts.sec_opts->aes_key_file, "rb"); + if (in_fd == NULL) { + fprintf(stderr, "Failed to open AES key file %s\n", + opts.sec_opts->aes_key_file); + return 1; + } + + /* Read the AES key in ASCII format byte by byte */ + for (index = 0; index < AES_KEY_BYTE_LEN; index++) { + if (fscanf(in_fd, "%02hhx", + opts.sec_opts->aes_key + index) != 1) { + fprintf(stderr, + "Failed to read AES key byte %d ", + index); + fprintf(stderr, + "from file %s\n", + opts.sec_opts->aes_key_file); + fclose(in_fd); + return 1; + } + } + fclose(in_fd); + sec_ext.encrypt_en = 1; + } else { + sec_ext.encrypt_en = 0; + } + + /* Fill the rest of the trusted boot extension fields */ + sec_ext.box_id = opts.sec_opts->box_id; + sec_ext.flash_id = opts.sec_opts->flash_id; + sec_ext.efuse_dis = opts.sec_opts->efuse_disable; + sec_ext.jtag_delay = opts.sec_opts->jtag_delay; + sec_ext.jtag_en = opts.sec_opts->jtag_enable; + + memcpy(sec_ext.cp_ctrl_arr, + opts.sec_opts->cp_ctrl_arr, + sizeof(uint32_t) * CP_CTRL_EL_ARRAY_SZ); + memcpy(sec_ext.cp_efuse_arr, + opts.sec_opts->cp_efuse_arr, + sizeof(uint32_t) * CP_CTRL_EL_ARRAY_SZ); + + /* Write the resulting extension to file + * (image and header signature fields are still empty) + */ + + /* Write extension header */ + written = fwrite(&header, sizeof(ext_header_t), 1, out_fd); + if (written != 1) { + fprintf(stderr, + "Failed to write SEC extension header to the file\n"); + return 1; + } + /* Write extension body */ + written = fwrite(&sec_ext, sizeof(sec_entry_t), 1, out_fd); + if (written != 1) { + fprintf(stderr, + "Failed to write SEC extension body to the file\n"); + return 1; + } + + return 0; +} + +/******************************************************************************* + * finalize_secure_ext + * Make final changes to secure extension - calculate image and header + * signatures and encrypt the image if needed. + * The main header checksum and image size fields updated accordingly + * INPUT: + * header Main header + * prolog_buf the entire prolog buffer + * prolog_size prolog buffer length + * image_buf buffer containing the input binary image + * image_size image buffer size. + * OUTPUT: + * none + * RETURN: + * 0 on success + */ +int finalize_secure_ext(header_t *header, + uint8_t *prolog_buf, uint32_t prolog_size, + uint8_t *image_buf, int image_size) +{ + int cur_ext, offset; + uint8_t *final_image = image_buf; + uint32_t final_image_sz = image_size; + uint8_t hdr_sign[RSA_SIGN_BYTE_LEN]; + sec_entry_t *sec_ext = 0; + + /* Find the Trusted Boot Header between available extensions */ + for (cur_ext = 0, offset = sizeof(header_t); + cur_ext < header->ext_count; cur_ext++) { + ext_header_t *ext_hdr = (ext_header_t *)(prolog_buf + offset); + + if (ext_hdr->type == EXT_TYPE_SECURITY) { + sec_ext = (sec_entry_t *)(prolog_buf + offset + + sizeof(ext_header_t) + ext_hdr->offset); + break; + } + + offset += sizeof(ext_header_t); + /* If offset is Zero, the extension follows its header */ + if (ext_hdr->offset == 0) + offset += ext_hdr->size; + } + + if (sec_ext == 0) { + fprintf(stderr, "Error: No Trusted Boot extension found!\n"); + return -1; + } + + if (sec_ext->encrypt_en) { + /* Encrypt the image if needed */ + fprintf(stdout, "Encrypting the image...\n"); + + if (image_encrypt(image_buf, image_size) != 0) { + fprintf(stderr, "Failed to encrypt the image!\n"); + return -1; + } + + /* Image size and checksum should be updated after encryption. + * This way the image could be verified by the BootROM + * before decryption. + */ + final_image = opts.sec_opts->encrypted_image; + final_image_sz = opts.sec_opts->enc_image_sz; + + header->boot_image_size = final_image_sz; + header->boot_image_checksum = + checksum32((uint32_t *)final_image, final_image_sz); + } /* AES encryption */ + + /* Create the image signature first, since it will be later + * signed along with the header signature + */ + if (create_rsa_signature(&opts.sec_opts->csk_pk[ + opts.sec_opts->csk_index], + final_image, final_image_sz, + opts.sec_opts->csk_key_file[ + opts.sec_opts->csk_index], + sec_ext->image_sign) != 0) { + fprintf(stderr, "Failed to sign image!\n"); + return -1; + } + /* Check that the image signature is correct */ + if (verify_rsa_signature(sec_ext->csk_keys[opts.sec_opts->csk_index], + MAX_RSA_DER_BYTE_LEN, + final_image, final_image_sz, + opts.sec_opts->csk_key_file[ + opts.sec_opts->csk_index], + sec_ext->image_sign) != 0) { + fprintf(stderr, "Failed to verify image signature!\n"); + return -1; + } + + /* Sign the headers and all the extensions block + * when the header signature field is empty + */ + if (create_rsa_signature(&opts.sec_opts->csk_pk[ + opts.sec_opts->csk_index], + prolog_buf, prolog_size, + opts.sec_opts->csk_key_file[ + opts.sec_opts->csk_index], + hdr_sign) != 0) { + fprintf(stderr, "Failed to sign header!\n"); + return -1; + } + /* Check that the header signature is correct */ + if (verify_rsa_signature(sec_ext->csk_keys[opts.sec_opts->csk_index], + MAX_RSA_DER_BYTE_LEN, + prolog_buf, prolog_size, + opts.sec_opts->csk_key_file[ + opts.sec_opts->csk_index], + hdr_sign) != 0) { + fprintf(stderr, "Failed to verify header signature!\n"); + return -1; + } + + /* Finally, copy the header signature into the trusted boot extension */ + memcpy(sec_ext->header_sign, hdr_sign, RSA_SIGN_BYTE_LEN); + + return 0; +} + +#endif /* CONFIG_MVEBU_SECURE_BOOT */ + + +#define FMT_HEX 0 +#define FMT_DEC 1 +#define FMT_BIN 2 +#define FMT_NONE 3 + +void do_print_field(unsigned int value, char *name, + int start, int size, int format) +{ + fprintf(stdout, "[0x%05x : 0x%05x] %-26s", + start, start + size - 1, name); + + switch (format) { + case FMT_HEX: + printf("0x%x\n", value); + break; + case FMT_DEC: + printf("%d\n", value); + break; + default: + printf("\n"); + break; + } +} + +#define print_field(st, type, field, hex, base) \ + do_print_field((int)st->field, #field, \ + base + offsetof(type, field), sizeof(st->field), hex) + +int print_header(uint8_t *buf, int base) +{ + header_t *main_hdr; + + main_hdr = (header_t *)buf; + + fprintf(stdout, "########### Header ##############\n"); + print_field(main_hdr, header_t, magic, FMT_HEX, base); + print_field(main_hdr, header_t, prolog_size, FMT_DEC, base); + print_field(main_hdr, header_t, prolog_checksum, FMT_HEX, base); + print_field(main_hdr, header_t, boot_image_size, FMT_DEC, base); + print_field(main_hdr, header_t, boot_image_checksum, FMT_HEX, base); + print_field(main_hdr, header_t, rsrvd0, FMT_HEX, base); + print_field(main_hdr, header_t, load_addr, FMT_HEX, base); + print_field(main_hdr, header_t, exec_addr, FMT_HEX, base); + print_field(main_hdr, header_t, uart_cfg, FMT_HEX, base); + print_field(main_hdr, header_t, baudrate, FMT_HEX, base); + print_field(main_hdr, header_t, ext_count, FMT_DEC, base); + print_field(main_hdr, header_t, aux_flags, FMT_HEX, base); + print_field(main_hdr, header_t, io_arg_0, FMT_HEX, base); + print_field(main_hdr, header_t, io_arg_1, FMT_HEX, base); + print_field(main_hdr, header_t, io_arg_2, FMT_HEX, base); + print_field(main_hdr, header_t, io_arg_3, FMT_HEX, base); + print_field(main_hdr, header_t, rsrvd1, FMT_HEX, base); + print_field(main_hdr, header_t, rsrvd2, FMT_HEX, base); + print_field(main_hdr, header_t, rsrvd3, FMT_HEX, base); + + return sizeof(header_t); +} + +int print_ext_hdr(ext_header_t *ext_hdr, int base) +{ + print_field(ext_hdr, ext_header_t, type, FMT_HEX, base); + print_field(ext_hdr, ext_header_t, offset, FMT_HEX, base); + print_field(ext_hdr, ext_header_t, reserved, FMT_HEX, base); + print_field(ext_hdr, ext_header_t, size, FMT_DEC, base); + + return base + sizeof(ext_header_t); +} + +void print_sec_ext(ext_header_t *ext_hdr, int base) +{ + sec_entry_t *sec_entry; + uint32_t new_base; + + fprintf(stdout, "\n########### Secure extension ###########\n"); + + new_base = print_ext_hdr(ext_hdr, base); + + sec_entry = (sec_entry_t *)(ext_hdr + 1); + + do_print_field(0, "KAK key", new_base, MAX_RSA_DER_BYTE_LEN, FMT_NONE); + new_base += MAX_RSA_DER_BYTE_LEN; + print_field(sec_entry, sec_entry_t, jtag_delay, FMT_DEC, base); + print_field(sec_entry, sec_entry_t, box_id, FMT_HEX, base); + print_field(sec_entry, sec_entry_t, flash_id, FMT_HEX, base); + print_field(sec_entry, sec_entry_t, encrypt_en, FMT_DEC, base); + print_field(sec_entry, sec_entry_t, efuse_dis, FMT_DEC, base); + new_base += 6 * sizeof(uint32_t); + do_print_field(0, "header signature", + new_base, RSA_SIGN_BYTE_LEN, FMT_NONE); + new_base += RSA_SIGN_BYTE_LEN; + do_print_field(0, "image signature", + new_base, RSA_SIGN_BYTE_LEN, FMT_NONE); + new_base += RSA_SIGN_BYTE_LEN; + do_print_field(0, "CSK keys", new_base, + CSK_ARR_SZ * MAX_RSA_DER_BYTE_LEN, FMT_NONE); + new_base += CSK_ARR_SZ * MAX_RSA_DER_BYTE_LEN; + do_print_field(0, "CSK block signature", + new_base, RSA_SIGN_BYTE_LEN, FMT_NONE); + new_base += RSA_SIGN_BYTE_LEN; + do_print_field(0, "control", new_base, + CP_CTRL_EL_ARRAY_SZ * 2, FMT_NONE); + +} + +void print_bin_ext(ext_header_t *ext_hdr, int base) +{ + fprintf(stdout, "\n########### Binary extension ###########\n"); + base = print_ext_hdr(ext_hdr, base); + do_print_field(0, "binary image", base, ext_hdr->size, FMT_NONE); +} + +int print_extension(void *buf, int base, int count, int ext_size) +{ + ext_header_t *ext_hdr = buf; + int pad = ext_size; + int curr_size; + + while (count--) { + if (ext_hdr->type == EXT_TYPE_BINARY) + print_bin_ext(ext_hdr, base); + else if (ext_hdr->type == EXT_TYPE_SECURITY) + print_sec_ext(ext_hdr, base); + + curr_size = sizeof(ext_header_t) + ext_hdr->size; + base += curr_size; + pad -= curr_size; + ext_hdr = (ext_header_t *)((uintptr_t)ext_hdr + curr_size); + } + + if (pad) + do_print_field(0, "padding", base, pad, FMT_NONE); + + return ext_size; +} + +int parse_image(uint8_t *buf, int size) +{ + int base = 0; + int ret = 1; + header_t *main_hdr; + uint32_t checksum, prolog_checksum; + + + fprintf(stdout, + "################### Prolog Start ######################\n\n"); + main_hdr = (header_t *)buf; + base += print_header(buf, base); + + if (main_hdr->ext_count) + base += print_extension(buf + base, base, + main_hdr->ext_count, + main_hdr->prolog_size - + sizeof(header_t)); + + if (base < main_hdr->prolog_size) { + fprintf(stdout, "\n########### Padding ##############\n"); + do_print_field(0, "prolog padding", + base, main_hdr->prolog_size - base, FMT_HEX); + base = main_hdr->prolog_size; + } + fprintf(stdout, + "\n################### Prolog End ######################\n"); + + fprintf(stdout, + "\n################### Boot image ######################\n"); + + do_print_field(0, "boot image", base, size - base - 4, FMT_NONE); + + fprintf(stdout, + "################### Image end ########################\n"); + + /* Check sanity for certain values */ + printf("\nChecking values:\n"); + + if (main_hdr->magic == MAIN_HDR_MAGIC) { + fprintf(stdout, "Headers magic: OK!\n"); + } else { + fprintf(stderr, + "\n****** ERROR: HEADER MAGIC 0x%08x != 0x%08x\n", + main_hdr->magic, MAIN_HDR_MAGIC); + goto error; + } + + /* headers checksum */ + /* clear the checksum field in header to calculate checksum */ + prolog_checksum = main_hdr->prolog_checksum; + main_hdr->prolog_checksum = 0; + checksum = checksum32((uint32_t *)buf, main_hdr->prolog_size); + + if (checksum == prolog_checksum) { + fprintf(stdout, "Headers checksum: OK!\n"); + } else { + fprintf(stderr, + "\n***** ERROR: BAD HEADER CHECKSUM 0x%08x != 0x%08x\n", + checksum, prolog_checksum); + goto error; + } + + /* boot image checksum */ + checksum = checksum32((uint32_t *)(buf + main_hdr->prolog_size), + main_hdr->boot_image_size); + if (checksum == main_hdr->boot_image_checksum) { + fprintf(stdout, "Image checksum: OK!\n"); + } else { + fprintf(stderr, + "\n****** ERROR: BAD IMAGE CHECKSUM 0x%08x != 0x%08x\n", + checksum, main_hdr->boot_image_checksum); + goto error; + } + +#ifdef CONFIG_MVEBU_SECURE_BOOT + /* RSA signatures */ + if (main_hdr->ext_count) { + uint8_t ext_num = main_hdr->ext_count; + ext_header_t *ext_hdr = (ext_header_t *)(main_hdr + 1); + unsigned char hash[32]; + int i; + + while (ext_num--) { + if (ext_hdr->type == EXT_TYPE_SECURITY) { + sec_entry_t *sec_entry = + (sec_entry_t *)(ext_hdr + 1); + + ret = verify_secure_header_signatures( + main_hdr, sec_entry); + if (ret != 0) { + fprintf(stderr, + "\n****** FAILED TO VERIFY "); + fprintf(stderr, + "RSA SIGNATURES ********\n"); + goto error; + } + + mbedtls_sha256_ret(sec_entry->kak_key, + MAX_RSA_DER_BYTE_LEN, hash, 0); + fprintf(stdout, + ">>>>>>>>>> KAK KEY HASH >>>>>>>>>>\n"); + fprintf(stdout, "SHA256: "); + for (i = 0; i < 32; i++) + fprintf(stdout, "%02X", hash[i]); + + fprintf(stdout, + "\n<<<<<<<<< KAK KEY HASH <<<<<<<<<\n"); + + break; + } + ext_hdr = + (ext_header_t *)((uint8_t *)(ext_hdr + 1) + + ext_hdr->size); + } + } +#endif + + ret = 0; +error: + return ret; +} + +int format_bin_ext(char *filename, FILE *out_fd) +{ + ext_header_t header; + FILE *in_fd; + int size, written; + int aligned_size, pad_bytes; + char c; + + in_fd = fopen(filename, "rb"); + if (in_fd == NULL) { + fprintf(stderr, "failed to open bin extension file %s\n", + filename); + return 1; + } + + size = get_file_size(filename); + if (size <= 0) { + fprintf(stderr, "bin extension file size is bad\n"); + return 1; + } + + /* Align extension size to 8 bytes */ + aligned_size = (size + 7) & (~7); + pad_bytes = aligned_size - size; + + header.type = EXT_TYPE_BINARY; + header.offset = 0; + header.size = aligned_size; + header.reserved = 0; + + /* Write header */ + written = fwrite(&header, sizeof(ext_header_t), 1, out_fd); + if (written != 1) { + fprintf(stderr, "failed writing header to extension file\n"); + return 1; + } + + /* Write image */ + while (size--) { + c = getc(in_fd); + fputc(c, out_fd); + } + + while (pad_bytes--) + fputc(0, out_fd); + + fclose(in_fd); + + return 0; +} + +/* **************************************** + * + * Write all extensions (binary, secure + * extensions) to file + * + * ****************************************/ + +int format_extensions(char *ext_filename) +{ + FILE *out_fd; + int ret = 0; + + out_fd = fopen(ext_filename, "wb"); + if (out_fd == NULL) { + fprintf(stderr, "failed to open extension output file %s", + ext_filename); + return 1; + } + + if (strncmp(opts.bin_ext_file, "NA", MAX_FILENAME)) { + if (format_bin_ext(opts.bin_ext_file, out_fd)) { + ret = 1; + goto error; + } + } +#ifdef CONFIG_MVEBU_SECURE_BOOT + if (strncmp(opts.sec_cfg_file, "NA", MAX_FILENAME)) { + if (format_sec_ext(opts.sec_cfg_file, out_fd)) { + ret = 1; + goto error; + } + } +#endif + +error: + fflush(out_fd); + fclose(out_fd); + return ret; +} + +void update_uart(header_t *header) +{ + header->uart_cfg = 0; + header->baudrate = 0; + + if (opts.disable_print) + uart_set_mode(header->uart_cfg, UART_MODE_DISABLE); + + if (opts.baudrate) + header->baudrate = (opts.baudrate / 1200); +} + +/* **************************************** + * + * Write the image prolog, i.e. + * main header and extensions, to file + * + * ****************************************/ + +int write_prolog(int ext_cnt, char *ext_filename, + uint8_t *image_buf, int image_size, FILE *out_fd) +{ + header_t *header; + int main_hdr_size = sizeof(header_t); + int prolog_size = main_hdr_size; + FILE *ext_fd; + char *buf; + int written, read; + int ret = 1; + + + if (ext_cnt) + prolog_size += get_file_size(ext_filename); + + prolog_size = ((prolog_size + PROLOG_ALIGNMENT) & + (~(PROLOG_ALIGNMENT-1))); + + /* Allocate a zeroed buffer to zero the padding bytes */ + buf = calloc(prolog_size, 1); + if (buf == NULL) { + fprintf(stderr, "Error: failed allocating checksum buffer\n"); + return 1; + } + + header = (header_t *)buf; + header->magic = MAIN_HDR_MAGIC; + header->prolog_size = prolog_size; + header->load_addr = opts.load_addr; + header->exec_addr = opts.exec_addr; + header->io_arg_0 = opts.nfc_io_args; + header->ext_count = ext_cnt; + header->aux_flags = 0; + header->boot_image_size = (image_size + 3) & (~0x3); + header->boot_image_checksum = checksum32((uint32_t *)image_buf, + image_size); + + update_uart(header); + + /* Populate buffer with main header and extensions */ + if (ext_cnt) { + ext_fd = fopen(ext_filename, "rb"); + if (ext_fd == NULL) { + fprintf(stderr, + "Error: failed to open extensions file\n"); + goto error; + } + + read = fread(&buf[main_hdr_size], + get_file_size(ext_filename), 1, ext_fd); + if (read != 1) { + fprintf(stderr, + "Error: failed to open extensions file\n"); + goto error; + } + +#ifdef CONFIG_MVEBU_SECURE_BOOT + /* Secure boot mode? */ + if (opts.sec_opts != 0) { + ret = finalize_secure_ext(header, (uint8_t *)buf, + prolog_size, image_buf, + image_size); + if (ret != 0) { + fprintf(stderr, "Error: failed to handle "); + fprintf(stderr, "secure extension!\n"); + goto error; + } + } /* secure boot mode */ +#endif + } + + /* Update the total prolog checksum */ + header->prolog_checksum = checksum32((uint32_t *)buf, prolog_size); + + /* Now spill everything to output file */ + written = fwrite(buf, prolog_size, 1, out_fd); + if (written != 1) { + fprintf(stderr, + "Error: failed to write prolog to output file\n"); + goto error; + } + + ret = 0; + +error: + free(buf); + return ret; +} + +int write_boot_image(uint8_t *buf, uint32_t image_size, FILE *out_fd) +{ + int written; + + written = fwrite(buf, image_size, 1, out_fd); + if (written != 1) { + fprintf(stderr, "Error: Failed to write boot image\n"); + goto error; + } + + return 0; +error: + return 1; +} + +int main(int argc, char *argv[]) +{ + char in_file[MAX_FILENAME+1] = { 0 }; + char out_file[MAX_FILENAME+1] = { 0 }; + char ext_file[MAX_FILENAME+1] = { 0 }; + FILE *in_fd = NULL; + FILE *out_fd = NULL; + int parse = 0; + int ext_cnt = 0; + int opt; + int ret = 0; + int image_size, file_size; + uint8_t *image_buf = NULL; + int read; + size_t len; + uint32_t nand_block_size_kb, mlc_nand; + + /* Create temporary file for building extensions + * Use process ID for allowing multiple parallel runs + */ + snprintf(ext_file, MAX_FILENAME, "/tmp/ext_file-%x", getpid()); + + while ((opt = getopt(argc, argv, "hpms:i:l:e:a:b:u:n:t:c:k:")) != -1) { + switch (opt) { + case 'h': + usage(); + break; + case 'l': + opts.load_addr = strtoul(optarg, NULL, 0); + break; + case 'e': + opts.exec_addr = strtoul(optarg, NULL, 0); + break; + case 'm': + opts.disable_print = 1; + break; + case 'u': + opts.baudrate = strtoul(optarg, NULL, 0); + break; + case 'b': + strncpy(opts.bin_ext_file, optarg, MAX_FILENAME); + ext_cnt++; + break; + case 'p': + parse = 1; + break; + case 'n': + nand_block_size_kb = strtoul(optarg, NULL, 0); + opts.nfc_io_args |= (nand_block_size_kb / 64); + break; + case 't': + mlc_nand = 0; + if (!strncmp("MLC", optarg, 3)) + mlc_nand = 1; + opts.nfc_io_args |= (mlc_nand << 8); + break; +#ifdef CONFIG_MVEBU_SECURE_BOOT + case 'c': /* SEC extension */ + strncpy(opts.sec_cfg_file, optarg, MAX_FILENAME); + ext_cnt++; + break; + case 'k': + opts.key_index = strtoul(optarg, NULL, 0); + break; +#endif + default: /* '?' */ + usage_err("Unknown argument"); + exit(EXIT_FAILURE); + } + } + + /* Check validity of inputes */ + if (opts.load_addr % 8) + usage_err("Load address must be 8 bytes aligned"); + + if (opts.baudrate % 1200) + usage_err("Baudrate must be a multiple of 1200"); + + /* The remaining arguments are the input + * and potentially output file + */ + /* Input file must exist so exit if not */ + if (optind >= argc) + usage_err("missing input file name"); + + len = strlen(argv[optind]); + if (len > MAX_FILENAME) + usage_err("file name too long"); + memcpy(in_file, argv[optind], len); + optind++; + + /* Output file must exist in non parse mode */ + if (optind < argc) { + len = strlen(argv[optind]); + if (len > MAX_FILENAME) + usage_err("file name too long"); + memcpy(out_file, argv[optind], len); + } else if (!parse) + usage_err("missing output file name"); + + /* open the input file */ + in_fd = fopen(in_file, "rb"); + if (in_fd == NULL) { + printf("Error: Failed to open input file %s\n", in_file); + goto main_exit; + } + + /* Read the input file to buffer + * Always align the image to 16 byte boundary + */ + file_size = get_file_size(in_file); + image_size = (file_size + AES_BLOCK_SZ - 1) & ~(AES_BLOCK_SZ - 1); + image_buf = calloc(image_size, 1); + if (image_buf == NULL) { + fprintf(stderr, "Error: failed allocating input buffer\n"); + return 1; + } + + read = fread(image_buf, file_size, 1, in_fd); + if (read != 1) { + fprintf(stderr, "Error: failed to read input file\n"); + goto main_exit; + } + + /* Parse the input image and leave */ + if (parse) { + if (opts.key_index >= CSK_ARR_SZ) { + fprintf(stderr, + "Wrong key IDX value. Valid values 0 - %d\n", + CSK_ARR_SZ - 1); + goto main_exit; + } + ret = parse_image(image_buf, image_size); + goto main_exit; + } + + /* Create a blob file from all extensions */ + if (ext_cnt) { + ret = format_extensions(ext_file); + if (ret) + goto main_exit; + } + + out_fd = fopen(out_file, "wb"); + if (out_fd == NULL) { + fprintf(stderr, + "Error: Failed to open output file %s\n", out_file); + goto main_exit; + } + + ret = write_prolog(ext_cnt, ext_file, image_buf, image_size, out_fd); + if (ret) + goto main_exit; + +#ifdef CONFIG_MVEBU_SECURE_BOOT + if (opts.sec_opts && (opts.sec_opts->encrypted_image != 0) && + (opts.sec_opts->enc_image_sz != 0)) { + ret = write_boot_image(opts.sec_opts->encrypted_image, + opts.sec_opts->enc_image_sz, out_fd); + } else +#endif + ret = write_boot_image(image_buf, image_size, out_fd); + if (ret) + goto main_exit; + +main_exit: + if (in_fd) + fclose(in_fd); + + if (out_fd) + fclose(out_fd); + + if (image_buf) + free(image_buf); + + unlink(ext_file); + +#ifdef CONFIG_MVEBU_SECURE_BOOT + if (opts.sec_opts) { + if (opts.sec_opts->encrypted_image) + free(opts.sec_opts->encrypted_image); + free(opts.sec_opts); + } +#endif + exit(ret); +} diff --git a/tools/marvell/doimage/doimage.mk b/tools/marvell/doimage/doimage.mk new file mode 100644 index 0000000..2b751d4 --- /dev/null +++ b/tools/marvell/doimage/doimage.mk @@ -0,0 +1,15 @@ +# +# Copyright (C) 2018 Marvell International Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses + +DOIMAGE_FLAGS ?= -l 0x4100000 -e 0x4100000 + + +#NAND params +#Open and update the below when using NAND as a boot device. + +CONFIG_MVEBU_NAND_BLOCK_SIZE := 256 +CONFIG_MVEBU_NAND_CELL_TYPE := SLC +NAND_DOIMAGE_FLAGS := -t $(CONFIG_MVEBU_NAND_CELL_TYPE) -n $(CONFIG_MVEBU_NAND_BLOCK_SIZE) diff --git a/tools/marvell/doimage/secure/aes_key.txt b/tools/marvell/doimage/secure/aes_key.txt new file mode 100644 index 0000000..3e8a888 --- /dev/null +++ b/tools/marvell/doimage/secure/aes_key.txt @@ -0,0 +1 @@ +ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890 diff --git a/tools/marvell/doimage/secure/csk_priv_pem0.key b/tools/marvell/doimage/secure/csk_priv_pem0.key new file mode 100644 index 0000000..0840c2a --- /dev/null +++ b/tools/marvell/doimage/secure/csk_priv_pem0.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAm6jN6o2zQmtyUlvfkfDbSjPJ7Vlpp/KgK/eznoVBBsDIZakX +cIgf8TSLpNVkc+ZE0f/n8X7mEZIyjuSBObLOm9vbkoZcR7DlKUL7RNNOUCv55Ozl +hQwrzpH/uIyIJTvmek29G5wroi0wGbPoxzhelIRTjVCibleBWhYCmZQ6SIRmTY8L +JT8VkX8I/Mhu62DjvxF3BnV6pXuh/FdgDN7MbldzM8Y+GOxVGi5Kcm5WHY7eyMxl +4Y0Yko31Xv7T1PcXahVBIciT+11w+fLc4wQuCJ6GUf9JbzQ0ZllY/FdRG0AhuRMH +zN0jAc/sKrIFoAErED6qlcoQg0vl7gmWN5x+2wIDAQABAoIBACtnPFOkw1FH6I6y +c3qcMGlWW33FKsLb0nGwFfOjsGgTpU1Dgver3UxCnJWPsvzmPlZYBvK9saVAoLxb +VvUhuJ6ZBXar5FtRJfUFak7cpL+SI5IDxFP++tAUwbtR5DyNoUyFFK/4Mep8sybX +lZbHTwgWhb2nuEMQP09BR+RPAplpcitkIoPkhmbGfbt9Hsd25I3bb5Z9R4S/2Rcf +7tmaxndQamij7/pUI7xtd8L6cMESJGIWrgEt/MaT2z8nNPE3EDctDSlH9yKqA2O7 +/LTfrxNDnw5gGRtOgahloThKljVM6pQa4mi91FufD67pHwnKn8urNbt8/3AWg6uU +x4FzZdECgYEA0k2UYzBM+dU6T1bZZ176YI0cZrP1tbf/JwnZGHicQYS7lPLAqgfO +u5oRQzuDimOXaV4xCPBO2nadd6aBxbZTXaglR7GG2uCHX6w2DnOr8/d66YTErTVV +u7/Bf8gMKT9mM4rWPrOEXfXfF0fvcpkBQ+QDynIB37tx/mj2lXRkLx0CgYEAvXuX +Dbe2QgSK0ajrcH7YJyx3RVx9RonOqL4yjCVCELmaDQd307Ef3j+gkd59XIewm+HA +mPyeWEUd8EzH+UvjckfKFuF2I4lEUUWtVZTa7me7mvsFqeEOu5KusD4+Hs+B9Kqd +3Evqcpj2lcMBI519Hvr9BTKfDBcH1EUos6A9rFcCgYAxsyPeTQvj/wBIv72hMFD7 +gF2159GpoFIsZ6dmoRpMYZHzIWtmw3GX5FEwEmCD1AV0YU41TpVUC7QrEq6Yiv4o +pBQrXUkBcQ6NDaW4xJ1eip4Bkd7pEDGyrR6NlDlLhjAg/i6joskla3XNirKL4pzp +7nj23vqSZToLZcLgjyEeAQKBgD5EvDo80j9VwMzvpxecB6qv+S4pG94vcWOQxYm6 +wMBATjjT6HP/9EoUPM9S/32F9er0QFfGRL8bT6Blix4I62Dl6KqmQy2gcXwH2tOS +DHRmUIe40H6oQDAyHwg6HC4B4WInI6N+qzgnvnku0VQD8FdbAgVQQmY1t1PxulN1 +aG8XAoGAPWAr4i8KkVAx4wLlMF8E/ecKcsX1J0+UuKket7Dvk7xJfwtkSLPeV8Bp +HuoHXMM3KYoZ93Hlto5rAT1VQhYuj7heU10v+9UtYTFHgaitptYmxovoCKKiZICl +48aPUI377e5jQ6RhhGYy8ltKsJ80K1T9DIkThJPSS+9NAI+jrmg= +-----END RSA PRIVATE KEY----- diff --git a/tools/marvell/doimage/secure/csk_priv_pem1.key b/tools/marvell/doimage/secure/csk_priv_pem1.key new file mode 100644 index 0000000..91d1aeb --- /dev/null +++ b/tools/marvell/doimage/secure/csk_priv_pem1.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAgwHXB0AaIhT15Z9lHpQ2YT1W8i4oMvvRiLGQCrba5l7BJ03E +ct0x3zagNKZEnpNndT4EAy98ihkhwVlUhxZCparJ2L3JsTs5RgV0wyQkQzwMLM8g +QI5EMmJCgFAVRHmVICOsisGGfNVUHjGdGwPOipyQCsX2MAm3E139VpB7NYj+Q4IR +4kvcb+59LZxKuRJTFKRDIqMGJu98P/ga70+YLXPCBPKSfnZnUppuaI86jF1E6xt8 +o7YtfEPCHDd2LXxKPZ670OapVqwo0t7ZSzEG63NkLp56FXc1OpfC69C8VPiZ8JqW +wxvS/vL8MMCxsBnjSuqnmOAbcNR2GFtUwJOGwwIDAQABAoIBAFcfoiDwQHDp/531 +ownzBzcj0+67Q4Ckd3SwoVp+wJTz7wB0d3DsKX6IlYJuELRk0yjlVUXJDsnIbOpo +vg4Yf7otGo9JqBh1imFGv6AHKRaNmIs0M/66nh/juNYcbAhd0w7MqrKcgRQDyy1J +UXHl1jXYaPLBNDg+PcJjf1dSPp4axzmW2Pk2rXnJCsPcZXL/0YmEvqhfOze0GdjR +hOkbbr6MPPVM66tA00xSwg9XEYJvHtwH6oB0rnANM8ieNK1mtcWkTU5di17CCrjS +ohIhXQrdVpxt549EJoUqEFSgo8OOMm2npDbFrjlukb5euakvMacwoT1te79blSKf +hrTvjgECgYEA0VqoFL0Vqe1qleikYDJ7S5xcv1oruEV31TeuBhDuf0c4PADCnBrV +/RnCEYuXs6wCk60chHg5s0jxg+nGbiY6jRTHkJLRU3ZhDtrtfidEZ78GRzFF3shl +Uzt7dHkKK1ZdiMH4sWzyRLom91TKWMrNKC1AD7v4/zjEXy6phall3ZcCgYEAoDJa +0dIKvVCS6dM2E2kMqi/45mJqsJzFvYL1s4mbma/BAC47bBju/YEse90x+iIi3Gg/ +NoXmNfGPrtgdl+/J/Y6Pohxf/e7gGN71tYVETzgc2Jv09wqmzmTjCmo3wyepyWf+ +pIAE39kdhwnqXVw5xwOG1N3xrQ9TomOO+1QiXbUCgYAF84TJqiJehUA9aLKbhXPZ +z2UXj3GkuFzSs9V/mKWe+qBPnFnr5BtnKX9JzmUOl3ovRoGEBoLlZNJwxIl+ghmx +/wA5TOMkcz4JFRIhPu6D4HtGNNFepuWyewNkaThvyPG5vIHcUVOFvqDy8PcblRBF +7xteFyLZ5nw2lHX/NbSOmwKBgFxLZqPIPcPArkPlGhyow1Ex/lbNkOZcDFkZIHHl +8C3lYm62NCodW2PWjkh2shqInEkcDn9dObsOh1eWz8X/swJQplQhwPROMfJiUnHY +a/iwPX5WrBXAn0X+Pgh8FdBsA5g0QDOKRkSplCd/APX08pzEXWQ60siAMhE3BuOq +H3qZAoGAVnzFidlXuyn+fbNaNVepK9hbuoxHHbzYYWSkpi+73EchN8kXktC+AdEf +owr9TPILbwWWJyisa3wW4xdbMifCgVLTedWZpZ09BENVqC+7g7ksX0pNMGYuFLOh +Td7mFAgmclxG5UiKexajOLjjdnAsJyrDaNKhHn8NQNN6L93N0sE= +-----END RSA PRIVATE KEY----- diff --git a/tools/marvell/doimage/secure/csk_priv_pem2.key b/tools/marvell/doimage/secure/csk_priv_pem2.key new file mode 100644 index 0000000..ea47ac5 --- /dev/null +++ b/tools/marvell/doimage/secure/csk_priv_pem2.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAjxTSTh57/5njUpE200+Qb3ySAn8lKeufgaa0K2Xc6Ri7lDZR +ZJ2BPuQZV4lYGqgWUf0IOzNf2WnE2lPfVnLMx08h7NhBqJ83yJVajpr+itnOmW+r +M7h76TFyuna1xz2kw1uhgI5Y4FRnJ4Cg4AexCSyViXSzEN/7LQwxa5z5WGDiNX5N +3/tgjGu+dzSMOiIQhXwIcK/XaiQNm3WHqqnAhPb5Q9IBuuqBfpZoFfH4XmbFWrC8 +neSMMMxX5Ti9pKhLd1EsiaP0aUNQlF8gNWuC/tNaf+OCtwVelVa3sGSRjRFe06VQ +sAE9oyXKri11yD5Dwp1xXivbpOrf7xjUe5gILwIDAQABAoIBABTr94CCxqDucKYP +I9QsSzNyJKuGyfliQdWkea3q3C2ddzhJ5QbwXQjEM8xwAdkMAQ+GD2EQtxBEfgtq +vjqW2MjAEnbefGNavL5w0GgP0+6bwLEA+ii67iuAFoWbfCMhKWmDiY8RwX8z+E13 +ao63sTRlN4x86v4pskG5CbTxpCg+8m7KklLns4SwRGf5gGQcgKRtNSR5nE4g2UNl +dghbDdNlvUncm4zxUcTh0kquhF5Tef5w+6L7W8Hv9Pky3b1c2OK1BMhJlxYrtt69 +/zhIJs89CLx5ACfam+DT/xs0uUiuRQq/e1CCQLCnUO02JqpeN/schtDCd0ZWhbtB +nT7fwTECgYEAx+COhys+7AZI0U+PeuTkI86GUsWHoBislXThxbxyGvMFjgyADZD+ +q/XEGAcxd4eTA1fr0Q9cLuuHZubjGQ7+OIXMZ6arXUsrmMrjRu3kHO+y6K6r4s8j +5bxN/iQ0bymUtJRfJSLI172plszusiPWhCL5+yhYlNoh4mNZJuJnzXkCgYEAt0Gz +07P19YPsxk5ow7ZnSNOMOkkEPP0SuHHWekMIK9KMjiRUSygOAk07zTL7MUoFn9Gy +Prfi0ybFArNhIa4Xio3Fbjfig7rGgaApK4Y3d9A/CGPv/Nj7C2OTepqlEzRLmU9e +Xw5yhbccCydXLyAYFAET2XHsmbewpvHyeYUSoOcCgYBRMJEUrOdhPmhDxZqVo/Zb +6R887gnaaUtpZlHzXUnIUqEWA1PcruIT/b/KttlMIWEBQayDfkbGtFuK3AyxeBqh +4Q+XpucC/W7XIMrTW/yGGIPG6nTdq6B8SFIyAojeArjp5T8Eua11nRAPNm1bJR2V +DRQYBlp9FGIhMJPdLKhXmQKBgGeywSyR0COfBHPu2K+u3uFB/D7bJI/ScS54FHLY +zZ3mpeylOCHTR6IbzDRAng31Ihue0KtW6P6tGJx/nv4tAltAADFvZDlAjqW5WLKt +X2PoLlL0IlBFBEIclc6yBalJVWIqnG9TwJBT3oWdPGOJWLaxKWdJZSZS4J6HmLsV +B0aPAoGAduLsOt8C5z48jPqmJxyPwsmT0Q424FccPMcvGOJ13yxq3xNsfAsbmg9l +L2i/ktE0wCMA+Pm7cuFgxwD7xTr67POZgt9022KsOSonjPsIn24UQeP46vAX/Qtx +Qf3sfvzf57vNy2Hybe38T8RsVOZla+v/QctfSfmb8Y95XL/SZzA= +-----END RSA PRIVATE KEY----- diff --git a/tools/marvell/doimage/secure/csk_priv_pem3.key b/tools/marvell/doimage/secure/csk_priv_pem3.key new file mode 100644 index 0000000..e40a864 --- /dev/null +++ b/tools/marvell/doimage/secure/csk_priv_pem3.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAlA/T/5IMTPTu+k5PeesB2oeP80Y6nq0ls8vXLul0TVEJoJ+O +InbPYNqYPu4dbQQg/u8qp8BeXm2ujtJbBTcdn0jKIiDTKYEnlsGfUt9GHnuuzvFh +rORSKuAolUqvo/zcSCo1uykaFvSuyTovSPlwllzSixD9XBbHfn3kXneiIUa45vsJ +AyjTn2qCJt0WgvX42NTxH6Q/OWLeOuKTyRHf25eabucIv77KYy0mlEPq5jjiV5AJ +gl5F1h5G8n07JCIWjkZ2QV4wr+Hv9uGNaSb0WGppBp4CbdQa0eUI75cKzz4WXqds +HZaYiX/a8YC+EUfvqDD02vKREIKFL/1zL53P/wIDAQABAoIBAGzBj5w7oBNrGpr7 +qL9KEyt8xg0Q+gAR+Q6vXRlVXBtquiKk8Jd6I+vlxUz8RNsN3FrGPNPJpse/0yeP +dlJHYNfedLNK3zCucPD4uln6LRw5B3d0sKV5dK2Px9+ZY5iWJQxRDPS0RTi1dCnV +NmRo7P1Vo0WJLkFVbiYIvRVy1MGRfF9ejN41G6U4MoBAQ9WqLp+JasUMTspZI49a +z8tOiJPT94MHBwbKnz8Mcq8sy02LR7U5h82+0T7JoRVix/OXiOoiQExNjZ9yGar0 +wBnl0SL1UW5UUaYzbyNH0mlMXLD+qowbDZM2pBWPfqXK+CMOsL6STIwnns7lY+ZJ +ILbaVmECgYEA2kQXE1PZ25A87a81wCEld402WJ2KegrZC719EWv+xeoS72Ji8uv7 +V0PxVGJQOcG1N+dzJ5tN59SQ/NvVTrjwqNUxQqsygmWq/TcfGb9ONZRmyzcehYLb +m4xTjqJKQ6Kwm5SoaCYmzEb/xaeLwLS9HmR9MdB1dxtDOLpjaK/8qPECgYEArait +QhgaknlxG8pcAimPsEUrLHYWSFRE/MUk4+YvZg/5+YJ8csvY0SO2h0tF/ARwUrdI +DaLEifHm4vqgN03K/0gqj7TKxcNlV16PvVx7Vz97xejdqdHZLDfAo4lcotsgvFQW +zIqoQGGPLf6WhFixZ8mEYj8xnmzLGPvHQmf1h+8CgYEA0LDl917nIN4qw4ARPqDy +t/pXCienrcUNfgIxwSSnNwj2DdjejzI+4VNfPbW6y16BLPCp1CbUOGOwNXTj4R9H +S8Z8ESirZK5c7Tt1CyM1XlmEZ61OC43w+CsWAXz+0OiPQFLFKr+/vPXtvEjUgO7P +HG4sniKZDccNYQIl5oTOaaECgYAPU4u3AZmWw9EPutRT/IcJ75DX47Qjvgw4os2W +r4IPZ+mP88w39XW1P4mkdyg+DcY8BqD9Uxg1dHwEHEp3lw4LabsX48Thn1UaWOYm +uDrKgHfUB7FIg5S/Kkx+ImliliRVerZoZvRiejnAvW9bTtiZaFeetCUU7lUeZ1o2 +qiYpUQKBgHQDfdDhguBGPKpkJ7pVwHkJA/lyRWaN1hwplw4TvX2oH14NsHg5Q5Fd +lHqHFs2Ry/6X3bKgF0E6q4cx0V1Xnnj9sGsemlrHdiSxplDYRQql7X5OeYPGF/Bg +ZTTG8rDwy+ey6EP9BZUb03hISx/LyMynOzjGl6uOcdAcy2d9Vno0 +-----END RSA PRIVATE KEY----- diff --git a/tools/marvell/doimage/secure/kak_priv_pem.key b/tools/marvell/doimage/secure/kak_priv_pem.key new file mode 100644 index 0000000..dfceaba --- /dev/null +++ b/tools/marvell/doimage/secure/kak_priv_pem.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsj2cHcrE2pdyCqNr+oVcQULiRx6RivkrhLl2DTqWXpP33BPm +MP0W0X0z98X7E3kZO+JIGRZ8q+6AWmUpL+53aOGItNeUgT7jQKViPJIo9ZcEnv/n +PJqdgDd4xFhnwYMgq8uVYN9IPfaKDwB3EoOqjNox2JholUVxvLw6W8DAC8La3zwb +0hiqtIlirQOQ/KaTHxC6dPYkrai+jSK5uAX7Vt8RKYg5qfDxSdZckmC2xVKYURhV +bZAlyKki4h6f8CwYCJMQDpHL6mVYCuJ1Ju/OJEXvthDKD0CD2hwILhksdey3qMOC +I5lHSO1b+sTvnVHGs65wI7A+ZYwnadMNvS9e2QIDAQABAoIBAH2uu9q2FEEe8SdX +PNiWGQtbojsL7wzTzj/0lq2VVlqyc+AXmAWLMP/fDTn1vKlqhsSXNseZ96c0sgUL +uBM4T7MA9WivauQH+C6pb6/OUFt8daG4SNGPJOg4NUweGmt1jyAUmeyJBWPL6GXT +qiK//Q78/JECRxyaryyqfWwdak3flzfwONBJ03tQ9EO+L7hf9gAP7OYnAsuNp+Bz +tj1xzNMumYYYiHvsEXx8UTe8HGrmYuO53ZY5fBLGB6Jj7hRlAHNfcdVDvvoBU5cI +Zwi+5YsBuSP2Hr9Gt2Odu+KitH3gFdS0HIiDh44AT+Trj29NMANFDfkDbVHUmE0q +YBL75NECgYEA2E+fJzdaYyyPIcvQgVM8g52hltR5IRgJICND3NOdB/Zb2teBGZh+ +1XJ6ZqQMDcOQZo0CMbX9UNRnf3NU55k48/EEITxCgUJTx/WdfJeTVlWGspt5+U/r +hDnQmkePdU1en63+u9eqsla9+VhLwU3fl/pIOpsBAnoEzs3hMQZ1G0cCgYEA0vHH +ilm3AztIoZlH3wgDAl2Gu5/YopqEofKA8G4Jp89rlkk919P/GNjEc6575wjgztDB +0Xab+H7Nqxjs3HqQX/DTTuAxzAggBg3j/ijpHnmjrCHLeMT5ciyH+EH5Bg///cLq ++Cwn7aOWuSK1hGdDYxUycHylAYZXXFJzmEIEhN8CgYEA1qTrwPZkctTckyS0GiCG +g/P/TLQ6HmTDaWiVBqPVxvjn3RjLuqJf+V5Hp2JRs7bDq39xFfMJExQyP34qWkbp +BOe8uV4agDlY+ar4Q5IFWj40EzfEqWhsxCC6pt0rtbK4mqsFg1BWyfDZQnwjcAXe +QejRk5YMQnDiJHSXaRaHTjECgYAv6ecvD624ODEJM63VhRZZ5TCDUY19caeKuXB8 +LCJZUY3Ydw5rBaY92I7Wz90o3yVhFJ3RnCVVTkgdAu5aLiS5BhSZJ+dntri/Z0xQ +IK7C01JP+OUkq2kVe/Pued28eMnms+13LWBsY+oKZ03foyz1Ro1Ma6N3MzKIr9m9 +zdEE9QKBgECfoh0xE2T/cbJrtH0mwMCUM6eMVGq+yQBKNvuuPg6kaQUsah1n1rp6 +OyvjwRAXdhshszEzNTX1WTT6/i+vZX277Ax50pPo9UhQ9kVteVt1frN6+u5sy07V +fg1f2+m0iFx4BD/irU0fzSyfGE+QkBnmXFBUNSYjp2PSqYIdufmW +-----END RSA PRIVATE KEY----- diff --git a/tools/marvell/doimage/secure/sec_img_7K.cfg b/tools/marvell/doimage/secure/sec_img_7K.cfg new file mode 100644 index 0000000..459f731 --- /dev/null +++ b/tools/marvell/doimage/secure/sec_img_7K.cfg @@ -0,0 +1,29 @@ +# Trusted boot image extension definitions + +kak_key_file = "tools/doimage/secure/kak_priv_pem.key"; + +# CSK keys array - 16 entries total. +# Only a key with csk_key_index will be used for signing the image +# use "*" string instead of file name for specifying an empty key +csk_key_file = ["tools/doimage/secure/csk_priv_pem0.key", + "tools/doimage/secure/csk_priv_pem1.key", + "tools/doimage/secure/csk_priv_pem2.key", + "tools/doimage/secure/csk_priv_pem3.key", + "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*"]; + +# index of CSK key in the array. Valid range is 0 to 15 +csk_key_index = 3; + +# AES-256 symmetric key for image encryption +aes_key_file = "tools/doimage/secure/aes_key.txt"; + +efuse_disable = false; +jtag = { enable = true; delay = 20; }; + +box_id = 0xdeadbeef; +flash_id = 0xbaddf00d; + +# SecureBootControl and EfuseBurnControl registers array +# Two register addresses for each connected CP +# A7K - one CP, two register values +control = [0xF2441920, 0xF2441940]; diff --git a/tools/marvell/doimage/secure/sec_img_8K.cfg b/tools/marvell/doimage/secure/sec_img_8K.cfg new file mode 100644 index 0000000..a849dff --- /dev/null +++ b/tools/marvell/doimage/secure/sec_img_8K.cfg @@ -0,0 +1,29 @@ +# Trusted boot image extension definitions + +kak_key_file = "tools/doimage/secure/kak_priv_pem.key"; + +# CSK keys array - 16 entries total. +# Only a key with csk_key_index will be used for signing the image +# use "*" string instead of file name for specifying an empty key +csk_key_file = ["tools/doimage/secure/csk_priv_pem0.key", + "tools/doimage/secure/csk_priv_pem1.key", + "tools/doimage/secure/csk_priv_pem2.key", + "tools/doimage/secure/csk_priv_pem3.key", + "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*"]; + +# index of CSK key in the array. Valid range is 0 to 15 +csk_key_index = 3; + +# AES-256 symmetric key for image encryption +aes_key_file = "tools/doimage/secure/aes_key.txt"; + +efuse_disable = false; +jtag = { enable = true; delay = 20; }; + +box_id = 0xdeadbeef; +flash_id = 0xbaddf00d; + +# SecureBootControl and EfuseBurnControl registers array +# Two register addresses for each connected CP +# A8K - two CP, four register values +control = [0xF2441920, 0xF2441940, 0xF4441920, 0xF4441940]; diff --git a/tools/memory/__init__.py b/tools/memory/__init__.py new file mode 100644 index 0000000..0b4c8d3 --- /dev/null +++ b/tools/memory/__init__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# diff --git a/tools/memory/memory/__init__.py b/tools/memory/memory/__init__.py new file mode 100644 index 0000000..0b4c8d3 --- /dev/null +++ b/tools/memory/memory/__init__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# diff --git a/tools/memory/memory/buildparser.py b/tools/memory/memory/buildparser.py new file mode 100755 index 0000000..dedff79 --- /dev/null +++ b/tools/memory/memory/buildparser.py @@ -0,0 +1,88 @@ +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import re +from pathlib import Path + +from memory.elfparser import TfaElfParser +from memory.mapparser import TfaMapParser + + +class TfaBuildParser: + """A class for performing analysis on the memory layout of a TF-A build.""" + + def __init__(self, path: Path, map_backend=False): + self._modules = dict() + self._path = path + self.map_backend = map_backend + self._parse_modules() + + def __getitem__(self, module: str): + """Returns an TfaElfParser instance indexed by module.""" + return self._modules[module] + + def _parse_modules(self): + """Parse the build files using the selected backend.""" + backend = TfaElfParser + files = list(self._path.glob("**/*.elf")) + io_perms = "rb" + + if self.map_backend or len(files) == 0: + backend = TfaMapParser + files = self._path.glob("**/*.map") + io_perms = "r" + + for file in files: + module_name = file.name.split("/")[-1].split(".")[0] + with open(file, io_perms) as f: + self._modules[module_name] = backend(f) + + if not len(self._modules): + raise FileNotFoundError( + f"failed to find files to analyse in path {self._path}!" + ) + + @property + def symbols(self) -> list: + return [ + (*sym, k) for k, v in self._modules.items() for sym in v.symbols + ] + + @staticmethod + def filter_symbols(symbols: list, regex: str = None) -> list: + """Returns a map of symbols to modules.""" + regex = r".*" if not regex else regex + return sorted( + filter(lambda s: re.match(regex, s[0]), symbols), + key=lambda s: (-s[1], s[0]), + reverse=True, + ) + + def get_mem_usage_dict(self) -> dict: + """Returns map of memory usage per memory type for each module.""" + mem_map = {} + for k, v in self._modules.items(): + mod_mem_map = v.get_memory_layout() + if len(mod_mem_map): + mem_map[k] = mod_mem_map + return mem_map + + def get_mem_tree_as_dict(self) -> dict: + """Returns _tree of modules, segments and segments and their total + memory usage.""" + return { + k: { + "name": k, + **v.get_mod_mem_usage_dict(), + **{"children": v.get_seg_map_as_dict()}, + } + for k, v in self._modules.items() + } + + @property + def module_names(self): + """Returns sorted list of module names.""" + return sorted(self._modules.keys()) diff --git a/tools/memory/memory/elfparser.py b/tools/memory/memory/elfparser.py new file mode 100644 index 0000000..2dd2513 --- /dev/null +++ b/tools/memory/memory/elfparser.py @@ -0,0 +1,161 @@ +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import re +from dataclasses import asdict, dataclass +from typing import BinaryIO + +from elftools.elf.elffile import ELFFile + + +@dataclass(frozen=True) +class TfaMemObject: + name: str + start: int + end: int + size: int + children: list + + +class TfaElfParser: + """A class representing an ELF file built for TF-A. + + Provides a basic interface for reading the symbol table and other + attributes of an ELF file. The constructor accepts a file-like object with + the contents an ELF file. + """ + + def __init__(self, elf_file: BinaryIO): + self._segments = {} + self._memory_layout = {} + + elf = ELFFile(elf_file) + + self._symbols = { + sym.name: sym.entry["st_value"] + for sym in elf.get_section_by_name(".symtab").iter_symbols() + } + + self.set_segment_section_map(elf.iter_segments(), elf.iter_sections()) + self._memory_layout = self.get_memory_layout_from_symbols() + self._start = elf["e_entry"] + self._size, self._free = self._get_mem_usage() + self._end = self._start + self._size + + @property + def symbols(self): + return self._symbols.items() + + @staticmethod + def tfa_mem_obj_factory(elf_obj, name=None, children=None, segment=False): + """Converts a pyelfparser Segment or Section to a TfaMemObject.""" + # Ensure each segment is provided a name since they aren't in the + # program header. + assert not ( + segment and name is None + ), "Attempting to make segment without a name" + + if children is None: + children = list() + + # Segment and sections header keys have different prefixes. + vaddr = "p_vaddr" if segment else "sh_addr" + size = "p_memsz" if segment else "sh_size" + + # TODO figure out how to handle free space for sections and segments + return TfaMemObject( + name if segment else elf_obj.name, + elf_obj[vaddr], + elf_obj[vaddr] + elf_obj[size], + elf_obj[size], + [] if not children else children, + ) + + def _get_mem_usage(self) -> (int, int): + """Get total size and free space for this component.""" + size = free = 0 + + # Use information encoded in the segment header if we can't get a + # memory configuration. + if not self._memory_layout: + return sum(s.size for s in self._segments.values()), 0 + + for v in self._memory_layout.values(): + size += v["length"] + free += v["start"] + v["length"] - v["end"] + + return size, free + + def set_segment_section_map(self, segments, sections): + """Set segment to section mappings.""" + segments = list( + filter(lambda seg: seg["p_type"] == "PT_LOAD", segments) + ) + + for sec in sections: + for n, seg in enumerate(segments): + if seg.section_in_segment(sec): + if n not in self._segments.keys(): + self._segments[n] = self.tfa_mem_obj_factory( + seg, name=f"{n:#02}", segment=True + ) + + self._segments[n].children.append( + self.tfa_mem_obj_factory(sec) + ) + + def get_memory_layout_from_symbols(self, expr=None) -> dict: + """Retrieve information about the memory configuration from the symbol + table. + """ + assert len(self._symbols), "Symbol table is empty!" + + expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)" if not expr else expr + region_symbols = filter(lambda s: re.match(expr, s), self._symbols) + memory_layout = {} + + for symbol in region_symbols: + region, _, attr = tuple(symbol.lower().strip("__").split("_")) + if region not in memory_layout: + memory_layout[region] = {} + + # Retrieve the value of the symbol using the symbol as the key. + memory_layout[region][attr] = self._symbols[symbol] + + return memory_layout + + def get_seg_map_as_dict(self): + """Get a dictionary of segments and their section mappings.""" + return [asdict(v) for k, v in self._segments.items()] + + def get_memory_layout(self): + """Get the total memory consumed by this module from the memory + configuration. + {"rom": {"start": 0x0, "end": 0xFF, "length": ... } + """ + mem_dict = {} + + for mem, attrs in self._memory_layout.items(): + limit = attrs["start"] + attrs["length"] + mem_dict[mem] = { + "start": attrs["start"], + "limit": limit, + "size": attrs["end"] - attrs["start"], + "free": limit - attrs["end"], + "total": attrs["length"], + } + return mem_dict + + def get_mod_mem_usage_dict(self): + """Get the total memory consumed by the module, this combines the + information in the memory configuration. + """ + return { + "start": self._start, + "end": self._end, + "size": self._size, + "free": self._free, + } diff --git a/tools/memory/memory/mapparser.py b/tools/memory/memory/mapparser.py new file mode 100644 index 0000000..b1a4b4c --- /dev/null +++ b/tools/memory/memory/mapparser.py @@ -0,0 +1,75 @@ +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from re import match, search +from typing import TextIO + + +class TfaMapParser: + """A class representing a map file built for TF-A. + + Provides a basic interface for reading the symbol table. The constructor + accepts a file-like object with the contents a Map file. Only GNU map files + are supported at this stage. + """ + + def __init__(self, map_file: TextIO): + self._symbols = self.read_symbols(map_file) + + @property + def symbols(self): + return self._symbols.items() + + @staticmethod + def read_symbols(file: TextIO, pattern: str = None) -> dict: + pattern = r"\b(0x\w*)\s*(\w*)\s=" if not pattern else pattern + symbols = {} + + for line in file.readlines(): + match = search(pattern, line) + + if match is not None: + value, name = match.groups() + symbols[name] = int(value, 16) + + return symbols + + def get_memory_layout(self) -> dict: + """Get the total memory consumed by this module from the memory + configuration. + {"rom": {"start": 0x0, "end": 0xFF, "length": ... } + """ + assert len(self._symbols), "Symbol table is empty!" + expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)" + memory_layout = {} + + region_symbols = filter(lambda s: match(expr, s), self._symbols) + + for symbol in region_symbols: + region, _, attr = tuple(symbol.lower().strip("__").split("_")) + if region not in memory_layout: + memory_layout[region] = {} + + memory_layout[region][attr] = self._symbols[symbol] + + if "start" and "length" and "end" in memory_layout[region]: + memory_layout[region]["limit"] = ( + memory_layout[region]["end"] + + memory_layout[region]["length"] + ) + memory_layout[region]["free"] = ( + memory_layout[region]["limit"] + - memory_layout[region]["end"] + ) + memory_layout[region]["total"] = memory_layout[region][ + "length" + ] + memory_layout[region]["size"] = ( + memory_layout[region]["end"] + - memory_layout[region]["start"] + ) + + return memory_layout diff --git a/tools/memory/memory/memmap.py b/tools/memory/memory/memmap.py new file mode 100755 index 0000000..99149b5 --- /dev/null +++ b/tools/memory/memory/memmap.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from pathlib import Path + +import click +from memory.buildparser import TfaBuildParser +from memory.printer import TfaPrettyPrinter + + +@click.command() +@click.option( + "-r", + "--root", + type=Path, + default=None, + help="Root containing build output.", +) +@click.option( + "-p", + "--platform", + show_default=True, + default="fvp", + help="The platform targeted for analysis.", +) +@click.option( + "-b", + "--build-type", + default="release", + help="The target build type.", + type=click.Choice(["debug", "release"], case_sensitive=False), +) +@click.option( + "-f", + "--footprint", + is_flag=True, + show_default=True, + help="Generate a high level view of memory usage by memory types.", +) +@click.option( + "-t", + "--tree", + is_flag=True, + help="Generate a hierarchical view of the modules, segments and sections.", +) +@click.option( + "--depth", + default=3, + help="Generate a virtual address map of important TF symbols.", +) +@click.option( + "-s", + "--symbols", + is_flag=True, + help="Generate a map of important TF symbols.", +) +@click.option("-w", "--width", type=int, envvar="COLUMNS") +@click.option( + "-d", + is_flag=True, + default=False, + help="Display numbers in decimal base.", +) +@click.option( + "--no-elf-images", + is_flag=True, + help="Analyse the build's map files instead of ELF images.", +) +def main( + root: Path, + platform: str, + build_type: str, + footprint: str, + tree: bool, + symbols: bool, + depth: int, + width: int, + d: bool, + no_elf_images: bool, +): + build_path = root if root else Path("build/", platform, build_type) + click.echo(f"build-path: {build_path.resolve()}") + + parser = TfaBuildParser(build_path, map_backend=no_elf_images) + printer = TfaPrettyPrinter(columns=width, as_decimal=d) + + if footprint or not (tree or symbols): + printer.print_footprint(parser.get_mem_usage_dict()) + + if tree: + printer.print_mem_tree( + parser.get_mem_tree_as_dict(), parser.module_names, depth=depth + ) + + if symbols: + expr = ( + r"(.*)(TEXT|BSS|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF" + r"|R.M)(.*)(START|UNALIGNED|END)__$" + ) + printer.print_symbol_table( + parser.filter_symbols(parser.symbols, expr), parser.module_names + ) + + +if __name__ == "__main__": + main() diff --git a/tools/memory/memory/printer.py b/tools/memory/memory/printer.py new file mode 100755 index 0000000..4b18560 --- /dev/null +++ b/tools/memory/memory/printer.py @@ -0,0 +1,163 @@ +# +# Copyright (c) 2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from anytree import RenderTree +from anytree.importer import DictImporter +from prettytable import PrettyTable + + +class TfaPrettyPrinter: + """A class for printing the memory layout of ELF files. + + This class provides interfaces for printing various memory layout views of + ELF files in a TF-A build. It can be used to understand how the memory is + structured and consumed. + """ + + def __init__(self, columns: int = None, as_decimal: bool = False): + self.term_size = columns if columns and columns > 120 else 120 + self._tree = None + self._footprint = None + self._symbol_map = None + self.as_decimal = as_decimal + + def format_args(self, *args, width=10, fmt=None): + if not fmt and type(args[0]) is int: + fmt = f">{width}x" if not self.as_decimal else f">{width}" + return [f"{arg:{fmt}}" if fmt else arg for arg in args] + + def format_row(self, leading, *args, width=10, fmt=None): + formatted_args = self.format_args(*args, width=width, fmt=fmt) + return leading + " ".join(formatted_args) + + @staticmethod + def map_elf_symbol( + leading: str, + section_name: str, + rel_pos: int, + columns: int, + width: int = None, + is_edge: bool = False, + ): + empty_col = "{:{}{}}" + + # Some symbols are longer than the column width, truncate them until + # we find a more elegant way to display them! + len_over = len(section_name) - width + if len_over > 0: + section_name = section_name[len_over:-len_over] + + sec_row = f"+{section_name:-^{width-1}}+" + sep, fill = ("+", "-") if is_edge else ("|", "") + + sec_row_l = empty_col.format(sep, fill + "<", width) * rel_pos + sec_row_r = empty_col.format(sep, fill + ">", width) * ( + columns - rel_pos - 1 + ) + + return leading + sec_row_l + sec_row + sec_row_r + + def print_footprint( + self, app_mem_usage: dict, sort_key: str = None, fields: list = None + ): + assert len(app_mem_usage), "Empty memory layout dictionary!" + if not fields: + fields = ["Component", "Start", "Limit", "Size", "Free", "Total"] + + sort_key = fields[0] if not sort_key else sort_key + + # Iterate through all the memory types, create a table for each + # type, rows represent a single module. + for mem in sorted(set(k for _, v in app_mem_usage.items() for k in v)): + table = PrettyTable( + sortby=sort_key, + title=f"Memory Usage (bytes) [{mem.upper()}]", + field_names=fields, + ) + + for mod, vals in app_mem_usage.items(): + if mem in vals.keys(): + val = vals[mem] + table.add_row( + [ + mod.upper(), + *self.format_args( + *[val[k.lower()] for k in fields[1:]] + ), + ] + ) + print(table, "\n") + + def print_symbol_table( + self, + symbols: list, + modules: list, + start: int = 12, + ): + assert len(symbols), "Empty symbol list!" + modules = sorted(modules) + col_width = int((self.term_size - start) / len(modules)) + address_fixed_width = 11 + + num_fmt = ( + f"0=#0{address_fixed_width}x" if not self.as_decimal else ">10" + ) + + _symbol_map = [ + " " * start + + "".join(self.format_args(*modules, fmt=f"^{col_width}")) + ] + last_addr = None + + for i, (name, addr, mod) in enumerate(symbols): + # Do not print out an address twice if two symbols overlap, + # for example, at the end of one region and start of another. + leading = ( + f"{addr:{num_fmt}}" + " " if addr != last_addr else " " * start + ) + + _symbol_map.append( + self.map_elf_symbol( + leading, + name, + modules.index(mod), + len(modules), + width=col_width, + is_edge=(not i or i == len(symbols) - 1), + ) + ) + + last_addr = addr + + self._symbol_map = ["Memory Layout:"] + self._symbol_map += list(reversed(_symbol_map)) + print("\n".join(self._symbol_map)) + + def print_mem_tree( + self, mem_map_dict, modules, depth=1, min_pad=12, node_right_pad=12 + ): + # Start column should have some padding between itself and its data + # values. + anchor = min_pad + node_right_pad * (depth - 1) + headers = ["start", "end", "size"] + + self._tree = [ + (f"{'name':<{anchor}}" + " ".join(f"{arg:>10}" for arg in headers)) + ] + + for mod in sorted(modules): + root = DictImporter().import_(mem_map_dict[mod]) + for pre, fill, node in RenderTree(root, maxlevel=depth): + leading = f"{pre}{node.name}".ljust(anchor) + self._tree.append( + self.format_row( + leading, + node.start, + node.end, + node.size, + ) + ) + print("\n".join(self._tree), "\n") diff --git a/tools/nxp/cert_create_helper/cert_create_tbbr.mk b/tools/nxp/cert_create_helper/cert_create_tbbr.mk new file mode 100644 index 0000000..e3b2e91 --- /dev/null +++ b/tools/nxp/cert_create_helper/cert_create_tbbr.mk @@ -0,0 +1,31 @@ +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Compile time defines used by NXP platforms + +PLAT_DEF_OID := yes + +ifeq (${PLAT_DEF_OID},yes) + +$(eval $(call add_define, PLAT_DEF_OID)) +$(eval $(call add_define, PDEF_KEYS)) +$(eval $(call add_define, PDEF_CERTS)) +$(eval $(call add_define, PDEF_EXTS)) + + +INC_DIR += -I../../plat/nxp/common/fip_handler/common/ + +PDEF_CERT_TOOL_PATH := ../nxp/cert_create_helper +PLAT_INCLUDE += -I${PDEF_CERT_TOOL_PATH}/include + +PLAT_OBJECTS += ${PDEF_CERT_TOOL_PATH}/src/pdef_tbb_cert.o \ + ${PDEF_CERT_TOOL_PATH}/src/pdef_tbb_ext.o \ + ${PDEF_CERT_TOOL_PATH}/src/pdef_tbb_key.o + +$(shell rm ${PLAT_OBJECTS}) + +OBJECTS += ${PLAT_OBJECTS} +endif diff --git a/tools/nxp/cert_create_helper/include/pdef_tbb_cert.h b/tools/nxp/cert_create_helper/include/pdef_tbb_cert.h new file mode 100644 index 0000000..f185619 --- /dev/null +++ b/tools/nxp/cert_create_helper/include/pdef_tbb_cert.h @@ -0,0 +1,21 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PDEF_TBB_CERT_H +#define PDEF_TBB_CERT_H + +#include <tbbr/tbb_cert.h> + +/* + * Enumerate the certificates that are used to establish the chain of trust + */ +enum { + DDR_FW_KEY_CERT = FWU_CERT + 1, + DDR_UDIMM_FW_CONTENT_CERT, + DDR_RDIMM_FW_CONTENT_CERT +}; + +#endif /* PDEF_TBB_CERT_H */ diff --git a/tools/nxp/cert_create_helper/include/pdef_tbb_ext.h b/tools/nxp/cert_create_helper/include/pdef_tbb_ext.h new file mode 100644 index 0000000..5fb349c --- /dev/null +++ b/tools/nxp/cert_create_helper/include/pdef_tbb_ext.h @@ -0,0 +1,25 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PDEF_TBB_EXT_H +#define PDEF_TBB_EXT_H + +#include <tbbr/tbb_ext.h> + +/* Plat Defined TBBR extensions */ +enum { + DDR_FW_CONTENT_CERT_PK_EXT = FWU_HASH_EXT + 1, + DDR_IMEM_UDIMM_1D_HASH_EXT, + DDR_IMEM_UDIMM_2D_HASH_EXT, + DDR_DMEM_UDIMM_1D_HASH_EXT, + DDR_DMEM_UDIMM_2D_HASH_EXT, + DDR_IMEM_RDIMM_1D_HASH_EXT, + DDR_IMEM_RDIMM_2D_HASH_EXT, + DDR_DMEM_RDIMM_1D_HASH_EXT, + DDR_DMEM_RDIMM_2D_HASH_EXT +}; + +#endif /* PDEF_TBB_EXT_H */ diff --git a/tools/nxp/cert_create_helper/include/pdef_tbb_key.h b/tools/nxp/cert_create_helper/include/pdef_tbb_key.h new file mode 100644 index 0000000..b26b651 --- /dev/null +++ b/tools/nxp/cert_create_helper/include/pdef_tbb_key.h @@ -0,0 +1,18 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PDEF_TBB_KEY_H +#define PDEF_TBB_KEY_H + +#include <tbbr/tbb_key.h> + +/* + * Enumerate the pltform defined keys that are used to establish the chain of trust + */ +enum { + DDR_FW_CONTENT_KEY = NON_TRUSTED_FW_CONTENT_CERT_KEY + 1, +}; +#endif /* PDEF_TBB_KEY_H */ diff --git a/tools/nxp/cert_create_helper/src/pdef_tbb_cert.c b/tools/nxp/cert_create_helper/src/pdef_tbb_cert.c new file mode 100644 index 0000000..40bd928 --- /dev/null +++ b/tools/nxp/cert_create_helper/src/pdef_tbb_cert.c @@ -0,0 +1,62 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <pdef_tbb_cert.h> +#include <pdef_tbb_ext.h> +#include <pdef_tbb_key.h> + +static cert_t pdef_tbb_certs[] = { + [DDR_FW_KEY_CERT - DDR_FW_KEY_CERT] = { + .id = DDR_FW_KEY_CERT, + .opt = "ddr-fw-key-cert", + .help_msg = "DDR Firmware Key Certificate (output file)", + .fn = NULL, + .cn = "DDR Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = DDR_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + DDR_FW_CONTENT_CERT_PK_EXT, + }, + .num_ext = 2 + }, + [DDR_UDIMM_FW_CONTENT_CERT - DDR_FW_KEY_CERT] = { + .id = DDR_UDIMM_FW_CONTENT_CERT, + .opt = "ddr-udimm-fw-cert", + .help_msg = "DDR UDIMM Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "DDR UDIMM Firmware Content Certificate", + .key = DDR_FW_CONTENT_KEY, + .issuer = DDR_UDIMM_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + DDR_IMEM_UDIMM_1D_HASH_EXT, + DDR_IMEM_UDIMM_2D_HASH_EXT, + DDR_DMEM_UDIMM_1D_HASH_EXT, + DDR_DMEM_UDIMM_2D_HASH_EXT, + }, + .num_ext = 5 + }, + [DDR_RDIMM_FW_CONTENT_CERT - DDR_FW_KEY_CERT] = { + .id = DDR_RDIMM_FW_CONTENT_CERT, + .opt = "ddr-rdimm-fw-cert", + .help_msg = "DDR RDIMM Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "DDR RDIMM Firmware Content Certificate", + .key = DDR_FW_CONTENT_KEY, + .issuer = DDR_RDIMM_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + DDR_IMEM_RDIMM_1D_HASH_EXT, + DDR_IMEM_RDIMM_2D_HASH_EXT, + DDR_DMEM_RDIMM_1D_HASH_EXT, + DDR_DMEM_RDIMM_2D_HASH_EXT, + }, + .num_ext = 5 + } +}; + +PLAT_REGISTER_COT(pdef_tbb_certs); diff --git a/tools/nxp/cert_create_helper/src/pdef_tbb_ext.c b/tools/nxp/cert_create_helper/src/pdef_tbb_ext.c new file mode 100644 index 0000000..f6da6dd --- /dev/null +++ b/tools/nxp/cert_create_helper/src/pdef_tbb_ext.c @@ -0,0 +1,108 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <string.h> +#include <openssl/err.h> +#include <openssl/x509v3.h> + +#if USE_TBBR_DEFS +#include <tbbr_oid.h> +#else +#include <platform_oid.h> +#endif + +#include "ext.h" +#include "tbbr/tbb_ext.h" +#include "tbbr/tbb_key.h" + +#include <pdef_tbb_ext.h> +#include <pdef_tbb_key.h> + +static ext_t pdef_tbb_ext[] = { + [DDR_FW_CONTENT_CERT_PK_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_FW_CONTENT_CERT_PK_OID, + .sn = "DDR FirmwareContentCertPK", + .ln = "DDR Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = DDR_FW_CONTENT_KEY + }, + [DDR_IMEM_UDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_UDIMM_1D_HASH_OID, + .opt = "ddr-immem-udimm-1d", + .help_msg = "DDR Firmware IMEM UDIMM 1D image file", + .sn = "DDR UDIMM IMEM 1D FirmwareHash", + .ln = "DDR UDIMM IMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_IMEM_UDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_UDIMM_2D_HASH_OID, + .opt = "ddr-immem-udimm-2d", + .help_msg = "DDR Firmware IMEM UDIMM 2D image file", + .sn = "DDR UDIMM IMEM 2D FirmwareHash", + .ln = "DDR UDIMM IMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_UDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_UDIMM_1D_HASH_OID, + .opt = "ddr-dmmem-udimm-1d", + .help_msg = "DDR Firmware DMEM UDIMM 1D image file", + .sn = "DDR UDIMM DMEM 1D FirmwareHash", + .ln = "DDR UDIMM DMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_UDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_UDIMM_2D_HASH_OID, + .opt = "ddr-dmmem-udimm-2d", + .help_msg = "DDR Firmware DMEM UDIMM 2D image file", + .sn = "DDR UDIMM DMEM 2D FirmwareHash", + .ln = "DDR UDIMM DMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_IMEM_RDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_RDIMM_1D_HASH_OID, + .opt = "ddr-immem-rdimm-1d", + .help_msg = "DDR Firmware IMEM RDIMM 1D image file", + .sn = "DDR RDIMM IMEM 1D FirmwareHash", + .ln = "DDR RDIMM IMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_IMEM_RDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_RDIMM_2D_HASH_OID, + .opt = "ddr-immem-rdimm-2d", + .help_msg = "DDR Firmware IMEM RDIMM 2D image file", + .sn = "DDR RDIMM IMEM 2D FirmwareHash", + .ln = "DDR RDIMM IMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_RDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_RDIMM_1D_HASH_OID, + .opt = "ddr-dmmem-rdimm-1d", + .help_msg = "DDR Firmware DMEM RDIMM 1D image file", + .sn = "DDR RDIMM DMEM 1D FirmwareHash", + .ln = "DDR RDIMM DMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_RDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_RDIMM_2D_HASH_OID, + .opt = "ddr-dmmem-rdimm-2d", + .help_msg = "DDR Firmware DMEM RDIMM 2D image file", + .sn = "DDR RDIMM DMEM 2D FirmwareHash", + .ln = "DDR RDIMM DMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + } +}; + +PLAT_REGISTER_EXTENSIONS(pdef_tbb_ext); diff --git a/tools/nxp/cert_create_helper/src/pdef_tbb_key.c b/tools/nxp/cert_create_helper/src/pdef_tbb_key.c new file mode 100644 index 0000000..cf2ebda --- /dev/null +++ b/tools/nxp/cert_create_helper/src/pdef_tbb_key.c @@ -0,0 +1,18 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <pdef_tbb_key.h> + +static key_t pdef_tbb_keys[] = { + [DDR_FW_CONTENT_KEY - DDR_FW_CONTENT_KEY] = { + .id = DDR_FW_CONTENT_KEY, + .opt = "ddr-fw-key", + .help_msg = "DDR Firmware Content Certificate key (input/output file)", + .desc = "DDR Firmware Content Certificate key" + } +}; + +PLAT_REGISTER_KEYS(pdef_tbb_keys); diff --git a/tools/nxp/create_pbl/Makefile b/tools/nxp/create_pbl/Makefile new file mode 100644 index 0000000..f971a74 --- /dev/null +++ b/tools/nxp/create_pbl/Makefile @@ -0,0 +1,61 @@ +# +# Copyright 2018-2020 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk + +PROJECT_1 := create_pbl${BIN_EXT} +OBJECTS_1 := create_pbl.o +PROJECT_2 := byte_swap${BIN_EXT} +OBJECTS_2 := byte_swap.o +V ?= 0 + +override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 +CFLAGS := -Wall -Werror -pedantic -std=c99 +ifeq (${DEBUG},1) + CFLAGS += -g -O0 -DDEBUG +else + CFLAGS += -O2 +endif +LDLIBS := + +ifeq (${V},0) + Q := @ +else + Q := +endif + +INCLUDE_PATHS := + +HOSTCC ?= gcc +CC = gcc + +.PHONY: all clean distclean + +all: create_pbl byte_swap + +${PROJECT_1}: ${OBJECTS_1} Makefile + @echo " LD $@" + ${Q}${HOSTCC} ${OBJECTS_1} -o $@ ${LDLIBS} + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} + +${PROJECT_2}: ${OBJECTS_2} Makefile + @echo " LD $@" + ${Q}${HOSTCC} ${OBJECTS_2} -o $@ ${LDLIBS} + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} + +%.o: %.c %.h Makefile + @echo " CC $<" + ${Q}${HOSTCC} -c ${CPPFLAGS} ${CFLAGS} ${INCLUDE_PATHS} $< -o $@ + +clean: + $(call SHELL_DELETE_ALL, ${PROJECT_1} ${OBJECTS_1}) + $(call SHELL_DELETE_ALL, ${PROJECT_2} ${OBJECTS_2}) diff --git a/tools/nxp/create_pbl/README b/tools/nxp/create_pbl/README new file mode 100644 index 0000000..3b6f854 --- /dev/null +++ b/tools/nxp/create_pbl/README @@ -0,0 +1,65 @@ +Description: +------------ +Tool 'create_pbl' is a standalone tool to create the PBL images. + where, + On the basis of Chassis, + RCW image is placed first followed by the, + PBI commands to copy the, + Input BL2 image stored on the, + Specified boot source (QSPI or SD or NOR) to the, + Specified destination address. + + +Usage in standalone way: +----------------------- + +./create_pbl [options] (mentioned below): + + -r <RCW file-name> - name of RCW binary file. + -i <BL2 Bin file-name> - file to be added to rcw file. + -c <SoC Number> - SoC numeric identifier, may be one of + 1012,1023,1026.1028, + 1043,1046,1088,2080, + 2088,2160 + -b <boot source id> - Boot source id string, may be one of + "qspi", "nor", "nand", "sd", "emmc" + -d <Address> - Destination address where BL2 + image is to be copied + -o <output filename> - Name of PBL image generated + as an output of the tool. + -e <Address> - [Optional] Entry Point Address + of the BL2.bin + -f <Address> - BL2 image offset + on Boot Source for block copy. + command for chassis >=3.) + (Must for Ch3, Ignored for Ch2) + -h Help. + -s Secure boot. + + -s secure boot + -c SoC Number (see description above) + -b Boot source. + -r RCW binary file. + -i Input file that is to be added to rcw file. + -o Name of output file + -f Source Offset (Block Copy) + -d Destination address to which file has to be copied + -h Help. + +Example: + ./create_pbl -r <RCW file> -i <bl2.bin> -c <chassis_no> -b <boot_source = sd/qspi/nor> -d <Destination_Addr> -o <pbl_image_name> + + + +Usage at compilation time: +-------------------------------- + + make <compilation command......> pbl RCW=<Path_to_RCW_File>/<rcw_file_name.bin> + +Example: QSPI Boot For LS1046ARDB- + + make PLAT=ls1046rdb all fip BOOT_MODE=qspi SPD=opteed BL32=tee.bin BL33=u-boot-ls1046.bin pbl RCW=/home/pankaj/flexbuild/packages/firmware/dash-rcw/ls1046ardb/RR_FFSSPPPN_1133_5506/rcw_1600_qspiboot.bin + +Example: QSPI Boot For LX2160ARDB- + + make PLAT=lx2160ardb all fip BOOT_MODE=flexspi_nor SPD=opteed BL32=tee_lx2.bin BL33=u-boot_lx2160.bin pbl RCW=plat/nxp/soc-lx2160/lx2160ardb/rcw_1900_600_1600_19_5_2.bin diff --git a/tools/nxp/create_pbl/byte_swap.c b/tools/nxp/create_pbl/byte_swap.c new file mode 100644 index 0000000..1d0bfce --- /dev/null +++ b/tools/nxp/create_pbl/byte_swap.c @@ -0,0 +1,113 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <getopt.h> +#include <unistd.h> + +#define NUM_MEM_BLOCK 1 +#define FOUR_BYTE_ALIGN 4 +#define EIGHT_BYTE_ALIGN 8 +#define SIZE_TWO_PBL_CMD 24 + +#define SUCCESS 0 +#define FAILURE -1 +#define BYTE_SWAP_32(word) ((((word) & 0xff000000) >> 24)| \ + (((word) & 0x00ff0000) >> 8) | \ + (((word) & 0x0000ff00) << 8) | \ + (((word) & 0x000000ff) << 24)) + + +/* + * Returns: + * SUCCESS, on successful byte swapping. + * FAILURE, on failure. + */ +int do_byteswap(FILE *fp) +{ + int bytes = 0; + uint32_t upper_byte; + uint32_t lower_byte; + uint32_t pad = 0U; + /* Carries number of Padding bytes to be appended to + * make file size 8 byte aligned. + */ + int append_bytes; + int ret = FAILURE; + + fseek(fp, 0L, SEEK_END); + bytes = ftell(fp); + + append_bytes = EIGHT_BYTE_ALIGN - (bytes % EIGHT_BYTE_ALIGN); + if (append_bytes != 0) { + if (fwrite(&pad, append_bytes, NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) { + printf("%s: Error in appending padding bytes.\n", + __func__); + goto byteswap_err; + } + bytes += append_bytes; + } + + rewind(fp); + while (bytes > 0) { + if ((fread(&upper_byte, sizeof(upper_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) && (feof(fp) == 0)) { + printf("%s: Error reading upper bytes.\n", __func__); + goto byteswap_err; + } + if ((fread(&lower_byte, sizeof(lower_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) && (feof(fp) == 0)) { + printf("%s: Error reading lower bytes.\n", __func__); + goto byteswap_err; + } + fseek(fp, -8L, SEEK_CUR); + upper_byte = BYTE_SWAP_32(upper_byte); + lower_byte = BYTE_SWAP_32(lower_byte); + if (fwrite(&lower_byte, sizeof(lower_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) { + printf("%s: Error writing lower bytes.\n", __func__); + goto byteswap_err; + } + if (fwrite(&upper_byte, sizeof(upper_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) { + printf("%s: Error writing upper bytes.\n", __func__); + goto byteswap_err; + } + bytes -= EIGHT_BYTE_ALIGN; + } + ret = SUCCESS; + +byteswap_err: + return ret; +} + +int main(int argc, char **argv) +{ + FILE *fp = NULL; + int ret = 0; + + if (argc > 2) { + printf("Usage format is byteswap <filename>"); + return -1; + } + + fp = fopen(argv[1], "rb+"); + if (fp == NULL) { + printf("%s: Error opening the input file: %s\n", + __func__, argv[1]); + return -1; + } + + ret = do_byteswap(fp); + fclose(fp); + return ret; +} diff --git a/tools/nxp/create_pbl/create_pbl.c b/tools/nxp/create_pbl/create_pbl.c new file mode 100644 index 0000000..c277e39 --- /dev/null +++ b/tools/nxp/create_pbl/create_pbl.c @@ -0,0 +1,1000 @@ +/* + * Copyright 2021-2022 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <getopt.h> +#include <unistd.h> + +#define NUM_MEM_BLOCK 1 +#define FOUR_BYTE_ALIGN 4 +#define EIGHT_BYTE_ALIGN 8 +#define SIZE_TWO_PBL_CMD 24 + +/* Define for add_boot_ptr_cmd() */ +#define BOOTPTR_ADDR 0x09570604 +#define CSF_ADDR_SB 0x09ee0200 +/* CCSR write command to address 0x1e00400 i.e BOOTLOCPTR */ +#define BOOTPTR_ADDR_CH3 0x31e00400 +/* Load CSF header command */ +#define CSF_ADDR_SB_CH3 0x80220000 + +#define MAND_ARG_MASK 0xFFF3 +#define ARG_INIT_MASK 0xFF00 +#define RCW_FILE_NAME_ARG_MASK 0x0080 +#define IN_FILE_NAME_ARG_MASK 0x0040 +#define CHASSIS_ARG_MASK 0x0020 +#define BOOT_SRC_ARG_MASK 0x0010 +#define ENTRY_POINT_ADDR_ARG_MASK 0x0008 +#define BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK 0x0004 +#define BL2_BIN_CPY_DEST_ADDR_ARG_MASK 0x0002 +#define OP_FILE_NAME_ARG_MASK 0x0001 + +/* Define for add_cpy_cmd() */ +#define OFFSET_MASK 0x00ffffff +#define WRITE_CMD_BASE 0x81000000 +#define MAX_PBI_DATA_LEN_BYTE 64 + +/* 140 Bytes = Preamble + LOAD RCW command + RCW (128 bytes) + Checksum */ +#define CHS3_CRC_PAYLOAD_START_OFFSET 140 + +#define PBI_CRC_POLYNOMIAL 0x04c11db7 + +typedef enum { + CHASSIS_UNKNOWN, + CHASSIS_2, + CHASSIS_3, + CHASSIS_3_2, + CHASSIS_MAX /* must be last item in list */ +} chassis_t; + +typedef enum { + UNKNOWN_BOOT = 0, + IFC_NOR_BOOT, + IFC_NAND_BOOT, + QSPI_BOOT, + SD_BOOT, + EMMC_BOOT, + FLXSPI_NOR_BOOT, + FLXSPI_NAND_BOOT, + FLXSPI_NAND4K_BOOT, + MAX_BOOT /* must be last item in list */ +} boot_src_t; + +/* Base Addresses where PBL image is copied depending on the boot source. + * Boot address map varies as per Chassis architecture. + */ +#define BASE_ADDR_UNDEFINED 0xFFFFFFFF +#define BASE_ADDR_QSPI 0x20000000 +#define BASE_ADDR_SD 0x00001000 +#define BASE_ADDR_IFC_NOR 0x30000000 +#define BASE_ADDR_EMMC 0x00001000 +#define BASE_ADDR_FLX_NOR 0x20000000 +#define BASE_ADDR_NAND 0x20000000 + +uint32_t base_addr_ch3[MAX_BOOT] = { + BASE_ADDR_UNDEFINED, + BASE_ADDR_IFC_NOR, + BASE_ADDR_UNDEFINED, /*IFC NAND */ + BASE_ADDR_QSPI, + BASE_ADDR_SD, + BASE_ADDR_EMMC, + BASE_ADDR_UNDEFINED, /*FLXSPI NOR */ + BASE_ADDR_UNDEFINED, /*FLXSPI NAND 2K */ + BASE_ADDR_UNDEFINED /*FLXSPI NAND 4K */ +}; + +uint32_t base_addr_ch32[MAX_BOOT] = { + BASE_ADDR_UNDEFINED, + BASE_ADDR_UNDEFINED, /* IFC NOR */ + BASE_ADDR_UNDEFINED, /* IFC NAND */ + BASE_ADDR_UNDEFINED, /* QSPI */ + BASE_ADDR_SD, + BASE_ADDR_EMMC, + BASE_ADDR_FLX_NOR, + BASE_ADDR_UNDEFINED, /*FLXSPI NAND 2K */ + BASE_ADDR_UNDEFINED /*FLXSPI NAND 4K */ +}; + +/* for Chassis 3 */ +uint32_t blk_cpy_hdr_map_ch3[] = { + + 0, /* Unknown Boot Source */ + 0x80000020, /* NOR_BOOT */ + 0x0, /* NAND_BOOT */ + 0x80000062, /* QSPI_BOOT */ + 0x80000040, /* SD_BOOT */ + 0x80000041, /* EMMC_BOOT */ + 0x0, /* FLEXSPI NOR_BOOT */ + 0x0, /* FLEX SPI NAND2K BOOT */ + 0x0, /* CHASIS3_2_NAND4K_BOOT */ +}; + +uint32_t blk_cpy_hdr_map_ch32[] = { + 0, /* Unknown Boot Source */ + 0x0, /* NOR_BOOT */ + 0x0, /* NAND_BOOT */ + 0x0, /* QSPI_BOOT */ + 0x80000008, /* SD_BOOT */ + 0x80000009, /* EMMC_BOOT */ + 0x8000000F, /* FLEXSPI NOR_BOOT */ + 0x8000000C, /* FLEX SPI NAND2K BOOT */ + 0x8000000D, /* CHASIS3_2_NAND4K_BOOT */ +}; + +char *boot_src_string[] = { + "UNKNOWN_BOOT", + "IFC_NOR_BOOT", + "IFC_NAND_BOOT", + "QSPI_BOOT", + "SD_BOOT", + "EMMC_BOOT", + "FLXSPI_NOR_BOOT", + "FLXSPI_NAND_BOOT", + "FLXSPI_NAND4K_BOOT", +}; + +enum stop_command { + STOP_COMMAND = 0, + CRC_STOP_COMMAND +}; + +/* Structure will get populated in the main function + * as part of parsing the command line arguments. + * All member parameters are mandatory except: + * -ep + * -src_addr + */ +struct pbl_image { + char *rcw_nm; /* Input RCW File */ + char *sec_imgnm; /* Input BL2 binary */ + char *imagefile; /* Generated output file */ + boot_src_t boot_src; /* Boot Source - QSPI, SD, NOR, NAND etc */ + uint32_t src_addr; /* Source Address */ + uint32_t addr; /* Load address */ + uint32_t ep; /* Entry point <opt> default is load address */ + chassis_t chassis; /* Chassis type */ +} pblimg; + +#define SUCCESS 0 +#define FAILURE -1 +#define CRC_STOP_CMD_ARM 0x08610040 +#define CRC_STOP_CMD_ARM_CH3 0x808f0000 +#define STOP_CMD_ARM_CH3 0x80ff0000 +#define BYTE_SWAP_32(word) ((((word) & 0xff000000) >> 24)| \ + (((word) & 0x00ff0000) >> 8) | \ + (((word) & 0x0000ff00) << 8) | \ + (((word) & 0x000000ff) << 24)) + +#define PBI_LEN_MASK 0xFFF00000 +#define PBI_LEN_SHIFT 20 +#define NUM_RCW_WORD 35 +#define PBI_LEN_ADD 6 + +#define MAX_CRC_ENTRIES 256 + +/* SoC numeric identifier */ +#define SOC_LS1012 1012 +#define SOC_LS1023 1023 +#define SOC_LS1026 1026 +#define SOC_LS1028 1028 +#define SOC_LS1043 1043 +#define SOC_LS1046 1046 +#define SOC_LS1088 1088 +#define SOC_LS2080 2080 +#define SOC_LS2088 2088 +#define SOC_LX2160 2160 + +static uint32_t pbl_size; +bool sb_flag; + +/*************************************************************************** + * Description : CRC32 Lookup Table + ***************************************************************************/ +static uint32_t crc32_lookup[] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + +static void print_usage(void) +{ + printf("\nCorrect Usage of Tool is:\n"); + printf("\n ./create_pbl [options] (mentioned below):\n\n"); + printf("\t-r <RCW file-name> - name of RCW binary file.\n"); + printf("\t-i <BL2 Bin file-name> - file to be added to rcw file.\n"); + printf("\t-c <Number> - Chassis Architecture (=2 or =3\n"); + printf("\t or =4 for 3.2).\n"); + printf("\t-b <qspi/nor/nand/sd> - Boot source.\n"); + printf("\t-d <Address> - Destination address where BL2\n"); + printf("\t image is to be copied\n"); + printf("\t-o <output filename> - Name of PBL image generated\n"); + printf("\t as an output of the tool.\n"); + printf("\t-f <Address> - BL2 image Src Offset\n"); + printf("\t on Boot Source for block copy.\n"); + printf("\t command for chassis >=3.)\n"); + printf("\t-e <Address> - [Optional] Entry Point Address\n"); + printf("\t of the BL2.bin\n"); + printf("\t-s Secure Boot.\n"); + printf("\t-h Help.\n"); + printf("\n\n"); + exit(0); + +} + +/*************************************************************************** + * Function : crypto_calculate_checksum() + * Arguments : data - Pointer to FILE + * num - Number of 32 bit words for checksum + * Return : Checksum Value + * Description : Calculate Checksum over the data + ***************************************************************************/ +uint32_t crypto_calculate_checksum(FILE *fp_rcw_pbi_op, uint32_t num) +{ + uint32_t i; + uint64_t sum = 0; + uint32_t word; + + fseek(fp_rcw_pbi_op, 0L, SEEK_SET); + for (i = 0; i < num ; i++) { + if ((fread(&word, sizeof(word), NUM_MEM_BLOCK, fp_rcw_pbi_op)) + < NUM_MEM_BLOCK) { + printf("%s: Error reading word.\n", __func__); + return FAILURE; + } + sum = sum + word; + sum = sum & 0xFFFFFFFF; + } + return (uint32_t)sum; +} + +/*************************************************************************** + * Function : add_pbi_stop_cmd + * Arguments : fp_rcw_pbi_op - output rcw_pbi file pointer + * Return : SUCCESS or FAILURE + * Description : This function insert pbi stop command. + ***************************************************************************/ +int add_pbi_stop_cmd(FILE *fp_rcw_pbi_op, enum stop_command flag) +{ + int ret = FAILURE; + int32_t pbi_stop_cmd; + uint32_t pbi_crc = 0xffffffff, i, j, c; + uint32_t crc_table[MAX_CRC_ENTRIES]; + uint8_t data; + + switch (pblimg.chassis) { + case CHASSIS_2: + pbi_stop_cmd = BYTE_SWAP_32(CRC_STOP_CMD_ARM); + break; + case CHASSIS_3: + case CHASSIS_3_2: + /* Based on flag add the corresponsding cmd + * -- stop cmd or stop with CRC cmd + */ + if (flag == CRC_STOP_COMMAND) { + pbi_stop_cmd = CRC_STOP_CMD_ARM_CH3; + } else { + pbi_stop_cmd = STOP_CMD_ARM_CH3; + } + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + default: + printf("Internal Error: Invalid Chassis val = %d.\n", + pblimg.chassis); + goto pbi_stop_err; + } + + if (fwrite(&pbi_stop_cmd, sizeof(pbi_stop_cmd), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI STOP CMD\n", __func__); + goto pbi_stop_err; + } + + if (flag == CRC_STOP_COMMAND) { + for (i = 0; i < MAX_CRC_ENTRIES; i++) { + c = i << 24; + for (j = 0; j < 8; j++) { + c = (c & 0x80000000) ? + PBI_CRC_POLYNOMIAL ^ (c << 1) : c << 1; + } + + crc_table[i] = c; + } + } + + switch (pblimg.chassis) { + case CHASSIS_2: + /* Chassis 2: CRC is calculated on RCW + PBL cmd.*/ + fseek(fp_rcw_pbi_op, 0L, SEEK_SET); + break; + case CHASSIS_3: + case CHASSIS_3_2: + /* Chassis 3: CRC is calculated on PBL cmd only. */ + fseek(fp_rcw_pbi_op, CHS3_CRC_PAYLOAD_START_OFFSET, SEEK_SET); + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + printf("%s: Unknown Chassis.\n", __func__); + goto pbi_stop_err; + } + + while ((fread(&data, sizeof(data), NUM_MEM_BLOCK, fp_rcw_pbi_op)) + == NUM_MEM_BLOCK) { + if (flag == CRC_STOP_COMMAND) { + if (pblimg.chassis == CHASSIS_2) { + pbi_crc = crc_table + [((pbi_crc >> 24) ^ (data)) & 0xff] ^ + (pbi_crc << 8); + } else { + pbi_crc = (pbi_crc >> 8) ^ + crc32_lookup[((pbi_crc) ^ + (data)) & 0xff]; + } + } + } + + switch (pblimg.chassis) { + case CHASSIS_2: + pbi_crc = BYTE_SWAP_32(pbi_crc); + break; + case CHASSIS_3: + case CHASSIS_3_2: + if (flag == CRC_STOP_COMMAND) { + pbi_crc = pbi_crc ^ 0xFFFFFFFF; + } else { + pbi_crc = 0x00000000; + } + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + printf("%s: Unknown Chassis.\n", __func__); + goto pbi_stop_err; + } + + if (fwrite(&pbi_crc, sizeof(pbi_crc), NUM_MEM_BLOCK, fp_rcw_pbi_op) + != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI PBI CRC\n", __func__); + goto pbi_stop_err; + } + ret = SUCCESS; + +pbi_stop_err: + return ret; +} + +/* + * Returns: + * File size in bytes, on Success. + * FAILURE, on failure. + */ +int get_filesize(const char *c) +{ + FILE *fp; + int ret = FAILURE; + + fp = fopen(c, "rb"); + if (fp == NULL) { + fprintf(stderr, "%s: Error in opening the file: %s\n", + __func__, c); + goto filesize_err; + } + + fseek(fp, 0L, SEEK_END); + ret = ftell(fp); + fclose(fp); + +filesize_err: + return ret; +} + +/*************************************************************************** + * Function : get_bootptr + * Arguments : fp_rcw_pbi_op - Pointer to output file + * Return : SUCCESS or FAILURE + * Description : Add bootptr pbi command to output file + ***************************************************************************/ +int add_boot_ptr_cmd(FILE *fp_rcw_pbi_op) +{ + uint32_t bootptr_addr; + int ret = FAILURE; + + switch (pblimg.chassis) { + case CHASSIS_2: + if (sb_flag == true) + bootptr_addr = BYTE_SWAP_32(CSF_ADDR_SB); + else + bootptr_addr = BYTE_SWAP_32(BOOTPTR_ADDR); + pblimg.ep = BYTE_SWAP_32(pblimg.ep); + break; + case CHASSIS_3: + case CHASSIS_3_2: + if (sb_flag == true) + bootptr_addr = CSF_ADDR_SB_CH3; + else + bootptr_addr = BOOTPTR_ADDR_CH3; + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + default: + printf("Internal Error: Invalid Chassis val = %d.\n", + pblimg.chassis); + goto bootptr_err; + } + + if (fwrite(&bootptr_addr, sizeof(bootptr_addr), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI Words:[%d].\n", + __func__, ret); + goto bootptr_err; + } + + if (pblimg.ep != 0) { + if (fwrite(&pblimg.ep, sizeof(pblimg.ep), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI Words\n", __func__); + goto bootptr_err; + } + } + + printf("\nBoot Location Pointer= 0x%x\n", + pblimg.chassis == CHASSIS_2 ? BYTE_SWAP_32(pblimg.ep) : + pblimg.ep); + ret = SUCCESS; + +bootptr_err: + return ret; +} + +/*************************************************************************** + * Function : add_blk_cpy_cmd + * Arguments : pbi_word - pointer to pbi commands + * args - Command line args flag. + * Return : SUCCESS or FAILURE + * Description : Add pbi commands for block copy cmd in pbi_words + ***************************************************************************/ +int add_blk_cpy_cmd(FILE *fp_rcw_pbi_op, uint16_t args) +{ + uint32_t blk_cpy_hdr; + uint32_t file_size, new_file_size; + uint32_t align = 4; + int ret = FAILURE; + int num_pad_bytes = 0; + + if ((args & BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK) == 0) { + printf("ERROR: Offset not specified for Block Copy Cmd.\n"); + printf("\tSee Usage and use -f option\n"); + goto blk_copy_err; + } + + switch (pblimg.chassis) { + case CHASSIS_3: + /* Block copy command */ + blk_cpy_hdr = blk_cpy_hdr_map_ch3[pblimg.boot_src]; + pblimg.src_addr += base_addr_ch3[pblimg.boot_src]; + break; + case CHASSIS_3_2: + /* Block copy command */ + blk_cpy_hdr = blk_cpy_hdr_map_ch32[pblimg.boot_src]; + pblimg.src_addr += base_addr_ch32[pblimg.boot_src]; + break; + default: + printf("%s: Error invalid chassis type for this command.\n", + __func__); + goto blk_copy_err; + } + + file_size = get_filesize(pblimg.sec_imgnm); + if (file_size > 0) { + new_file_size = (file_size + (file_size % align)); + + /* Add Block copy command */ + if (fwrite(&blk_cpy_hdr, sizeof(blk_cpy_hdr), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing blk_cpy_hdr to the file.\n", + __func__); + goto blk_copy_err; + } + + if ((args & BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK) == 0) + num_pad_bytes = pblimg.src_addr % 4; + + /* Add Src address word */ + if (fwrite(&pblimg.src_addr + num_pad_bytes, + sizeof(pblimg.src_addr), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing BLK SRC Addr to the file.\n", + __func__); + goto blk_copy_err; + } + + /* Add Dest address word */ + if (fwrite(&pblimg.addr, sizeof(pblimg.addr), + NUM_MEM_BLOCK, fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing DST Addr to the file.\n", + __func__); + goto blk_copy_err; + } + + /* Add size */ + if (fwrite(&new_file_size, sizeof(new_file_size), + NUM_MEM_BLOCK, fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing size to the file.\n", + __func__); + goto blk_copy_err; + } + } + + ret = SUCCESS; + +blk_copy_err: + return ret; +} + +/*************************************************************************** + * Function : add_cpy_cmd + * Arguments : pbi_word - pointer to pbi commands + * Return : SUCCESS or FAILURE + * Description : Append pbi commands for copying BL2 image to the + * load address stored in pbl_image.addr + ***************************************************************************/ +int add_cpy_cmd(FILE *fp_rcw_pbi_op) +{ + uint32_t ALTCBAR_ADDRESS = BYTE_SWAP_32(0x09570158); + uint32_t WAIT_CMD_WRITE_ADDRESS = BYTE_SWAP_32(0x096100c0); + uint32_t WAIT_CMD = BYTE_SWAP_32(0x000FFFFF); + int file_size; + uint32_t pbi_cmd, altcbar; + uint8_t pbi_data[MAX_PBI_DATA_LEN_BYTE]; + uint32_t dst_offset; + FILE *fp_img = NULL; + int ret = FAILURE; + + altcbar = pblimg.addr; + dst_offset = pblimg.addr; + fp_img = fopen(pblimg.sec_imgnm, "rb"); + if (fp_img == NULL) { + printf("%s: Error in opening the file: %s\n", __func__, + pblimg.sec_imgnm); + goto add_cpy_err; + } + file_size = get_filesize(pblimg.sec_imgnm); + altcbar = 0xfff00000 & altcbar; + altcbar = BYTE_SWAP_32(altcbar >> 16); + if (fwrite(&ALTCBAR_ADDRESS, sizeof(ALTCBAR_ADDRESS), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in writing address of ALTCFG CMD.\n", + __func__); + goto add_cpy_err; + } + if (fwrite(&altcbar, sizeof(altcbar), NUM_MEM_BLOCK, fp_rcw_pbi_op) + != NUM_MEM_BLOCK) { + printf("%s: Error in writing ALTCFG CMD.\n", __func__); + goto add_cpy_err; + } + if (fwrite(&WAIT_CMD_WRITE_ADDRESS, sizeof(WAIT_CMD_WRITE_ADDRESS), + NUM_MEM_BLOCK, fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in writing address of WAIT_CMD.\n", + __func__); + goto add_cpy_err; + } + if (fwrite(&WAIT_CMD, sizeof(WAIT_CMD), NUM_MEM_BLOCK, fp_rcw_pbi_op) + != NUM_MEM_BLOCK) { + printf("%s: Error in writing WAIT_CMD.\n", __func__); + goto add_cpy_err; + } + do { + memset(pbi_data, 0, MAX_PBI_DATA_LEN_BYTE); + + ret = fread(&pbi_data, MAX_PBI_DATA_LEN_BYTE, + NUM_MEM_BLOCK, fp_img); + if ((ret != NUM_MEM_BLOCK) && (!feof(fp_img))) { + printf("%s: Error writing ALTCFG Word: [%d].\n", + __func__, ret); + goto add_cpy_err; + } + + dst_offset &= OFFSET_MASK; + pbi_cmd = WRITE_CMD_BASE | dst_offset; + pbi_cmd = BYTE_SWAP_32(pbi_cmd); + if (fwrite(&pbi_cmd, sizeof(pbi_cmd), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing ALTCFG Word write cmd.\n", + __func__); + goto add_cpy_err; + } + if (fwrite(&pbi_data, MAX_PBI_DATA_LEN_BYTE, NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing ALTCFG_Word.\n", __func__); + goto add_cpy_err; + } + dst_offset += MAX_PBI_DATA_LEN_BYTE; + file_size -= MAX_PBI_DATA_LEN_BYTE; + } while (!feof(fp_img)); + + ret = SUCCESS; + +add_cpy_err: + if (fp_img != NULL) { + fclose(fp_img); + } + return ret; +} + +int main(int argc, char **argv) +{ + FILE *file = NULL; + char *ptr; + int opt; + int tmp; + uint16_t args = ARG_INIT_MASK; + FILE *fp_rcw_pbi_ip = NULL, *fp_rcw_pbi_op = NULL; + uint32_t word, word_1; + int ret = FAILURE; + bool bootptr_flag = false; + enum stop_command flag_stop_cmd = CRC_STOP_COMMAND; + + /* Initializing the global structure to zero. */ + memset(&pblimg, 0x0, sizeof(struct pbl_image)); + + while ((opt = getopt(argc, argv, + ":b:f:r:i:e:d:c:o:h:s")) != -1) { + switch (opt) { + case 'd': + pblimg.addr = strtoull(optarg, &ptr, 16); + if (*ptr != 0) { + fprintf(stderr, "CMD Error: invalid load or destination address %s\n", optarg); + goto exit_main; + } + args |= BL2_BIN_CPY_DEST_ADDR_ARG_MASK; + break; + case 'r': + pblimg.rcw_nm = optarg; + file = fopen(pblimg.rcw_nm, "r"); + if (file == NULL) { + printf("CMD Error: Opening the RCW File.\n"); + goto exit_main; + } else { + args |= RCW_FILE_NAME_ARG_MASK; + fclose(file); + } + break; + case 'e': + bootptr_flag = true; + pblimg.ep = strtoull(optarg, &ptr, 16); + if (*ptr != 0) { + fprintf(stderr, + "CMD Error: Invalid entry point %s\n", optarg); + goto exit_main; + } + break; + case 'h': + print_usage(); + break; + case 'i': + pblimg.sec_imgnm = optarg; + file = fopen(pblimg.sec_imgnm, "r"); + if (file == NULL) { + printf("CMD Error: Opening Input file.\n"); + goto exit_main; + } else { + args |= IN_FILE_NAME_ARG_MASK; + fclose(file); + } + break; + case 'c': + tmp = atoi(optarg); + switch (tmp) { + case SOC_LS1012: + case SOC_LS1023: + case SOC_LS1026: + case SOC_LS1043: + case SOC_LS1046: + pblimg.chassis = CHASSIS_2; + break; + case SOC_LS1088: + case SOC_LS2080: + case SOC_LS2088: + pblimg.chassis = CHASSIS_3; + break; + case SOC_LS1028: + case SOC_LX2160: + pblimg.chassis = CHASSIS_3_2; + break; + default: + printf("CMD Error: Invalid SoC Val = %d.\n", tmp); + goto exit_main; + } + + args |= CHASSIS_ARG_MASK; + break; + case 'o': + pblimg.imagefile = optarg; + args |= OP_FILE_NAME_ARG_MASK; + break; + case 's': + sb_flag = true; + break; + case 'b': + if (strcmp(optarg, "qspi") == 0) { + pblimg.boot_src = QSPI_BOOT; + } else if (strcmp(optarg, "nor") == 0) { + pblimg.boot_src = IFC_NOR_BOOT; + } else if (strcmp(optarg, "nand") == 0) { + pblimg.boot_src = IFC_NAND_BOOT; + } else if (strcmp(optarg, "sd") == 0) { + pblimg.boot_src = SD_BOOT; + } else if (strcmp(optarg, "emmc") == 0) { + pblimg.boot_src = EMMC_BOOT; + } else if (strcmp(optarg, "flexspi_nor") == 0) { + pblimg.boot_src = FLXSPI_NOR_BOOT; + } else if (strcmp(optarg, "flexspi_nand") == 0) { + pblimg.boot_src = FLXSPI_NAND_BOOT; + } else if (strcmp(optarg, "flexspi_nand2k") == 0) { + pblimg.boot_src = FLXSPI_NAND4K_BOOT; + } else { + printf("CMD Error: Invalid boot source.\n"); + goto exit_main; + } + args |= BOOT_SRC_ARG_MASK; + break; + case 'f': + pblimg.src_addr = strtoull(optarg, &ptr, 16); + if (*ptr != 0) { + fprintf(stderr, + "CMD Error: Invalid src offset %s\n", optarg); + goto exit_main; + } + args |= BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK; + break; + default: + /* issue a warning and skip the unknown arg */ + printf("Cmd Warning: Invalid Arg = %c.\n", opt); + } + } + + if ((args & MAND_ARG_MASK) != MAND_ARG_MASK + || pblimg.rcw_nm == NULL + || pblimg.imagefile == NULL) { + print_usage(); + } + + fp_rcw_pbi_ip = fopen(pblimg.rcw_nm, "rb"); + if (fp_rcw_pbi_ip == NULL) { + printf("%s: Error in opening the rcw file: %s\n", + __func__, pblimg.rcw_nm); + goto exit_main; + } + + fp_rcw_pbi_op = fopen(pblimg.imagefile, "wb+"); + if (fp_rcw_pbi_op == NULL) { + printf("%s: Error opening the input file: %s\n", + __func__, pblimg.imagefile); + goto exit_main; + } + + printf("\nInput Boot Source: %s\n", boot_src_string[pblimg.boot_src]); + printf("Input RCW File: %s\n", pblimg.rcw_nm); + printf("Input BL2 Binary File: %s\n", pblimg.sec_imgnm); + printf("Input load address for BL2 Binary File: 0x%x\n", pblimg.addr); + + printf("Chassis Type: %d\n", pblimg.chassis); + switch (pblimg.chassis) { + case CHASSIS_2: + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, fp_rcw_pbi_ip) + != NUM_MEM_BLOCK) { + printf("%s: Error in reading word from the rcw file.\n", + __func__); + goto exit_main; + } + while (BYTE_SWAP_32(word) != 0x08610040) { + if (BYTE_SWAP_32(word) == 0x09550000 + || BYTE_SWAP_32(word) == 0x000f400c) { + break; + } + if (fwrite(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: [CH2] Error in Writing PBI Words\n", + __func__); + goto exit_main; + } + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_ip) != NUM_MEM_BLOCK) { + printf("%s: [CH2] Error in Reading PBI Words\n", + __func__); + goto exit_main; + } + } + + if (bootptr_flag == true) { + /* Add command to set boot_loc ptr */ + ret = add_boot_ptr_cmd(fp_rcw_pbi_op); + if (ret != SUCCESS) { + goto exit_main; + } + } + + /* Write acs write commands to output file */ + ret = add_cpy_cmd(fp_rcw_pbi_op); + if (ret != SUCCESS) { + goto exit_main; + } + + /* Add stop command after adding pbi commands + * For Chasis 2.0 platforms it is always CRC & + * Stop command + */ + flag_stop_cmd = CRC_STOP_COMMAND; + ret = add_pbi_stop_cmd(fp_rcw_pbi_op, flag_stop_cmd); + if (ret != SUCCESS) { + goto exit_main; + } + + break; + + case CHASSIS_3: + case CHASSIS_3_2: + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, fp_rcw_pbi_ip) + != NUM_MEM_BLOCK) { + printf("%s: Error reading PBI Cmd.\n", __func__); + goto exit_main; + } + while (word != 0x808f0000 && word != 0x80ff0000) { + pbl_size++; + /* 11th words in RCW has PBL length. Update it + * with new length. 2 commands get added + * Block copy + CCSR Write/CSF header write + */ + if (pbl_size == 11) { + word_1 = (word & PBI_LEN_MASK) + + (PBI_LEN_ADD << 20); + word = word & ~PBI_LEN_MASK; + word = word | word_1; + } + /* Update the CRC command */ + /* Check load command.. + * add a check if command is Stop with CRC + * or stop without checksum + */ + if (pbl_size == 35) { + word = crypto_calculate_checksum(fp_rcw_pbi_op, + NUM_RCW_WORD - 1); + if (word == FAILURE) { + goto exit_main; + } + } + if (fwrite(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: [CH3] Error in Writing PBI Words\n", + __func__); + goto exit_main; + } + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_ip) != NUM_MEM_BLOCK) { + printf("%s: [CH3] Error in Reading PBI Words\n", + __func__); + goto exit_main; + } + + if (word == CRC_STOP_CMD_ARM_CH3) { + flag_stop_cmd = CRC_STOP_COMMAND; + } else if (word == STOP_CMD_ARM_CH3) { + flag_stop_cmd = STOP_COMMAND; + } + } + if (bootptr_flag == true) { + /* Add command to set boot_loc ptr */ + ret = add_boot_ptr_cmd(fp_rcw_pbi_op); + if (ret != SUCCESS) { + printf("%s: add_boot_ptr_cmd return failure.\n", + __func__); + goto exit_main; + } + } + + /* Write acs write commands to output file */ + ret = add_blk_cpy_cmd(fp_rcw_pbi_op, args); + if (ret != SUCCESS) { + printf("%s: Function add_blk_cpy_cmd return failure.\n", + __func__); + goto exit_main; + } + + /* Add stop command after adding pbi commands */ + ret = add_pbi_stop_cmd(fp_rcw_pbi_op, flag_stop_cmd); + if (ret != SUCCESS) { + goto exit_main; + } + + break; + + default: + printf("%s: Unknown chassis type.\n", + __func__); + } + + if (ret == SUCCESS) { + printf("Output file successfully created with name: %s\n\n", + pblimg.imagefile); + } + +exit_main: + if (fp_rcw_pbi_op != NULL) { + fclose(fp_rcw_pbi_op); + } + if (fp_rcw_pbi_ip != NULL) { + fclose(fp_rcw_pbi_ip); + } + + return ret; +} diff --git a/tools/nxp/create_pbl/create_pbl.mk b/tools/nxp/create_pbl/create_pbl.mk new file mode 100644 index 0000000..305c049 --- /dev/null +++ b/tools/nxp/create_pbl/create_pbl.mk @@ -0,0 +1,52 @@ +# +# Copyright 2018-2020 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# +# + +CREATE_PBL ?= ${CREATE_PBL_TOOL_PATH}/create_pbl${BIN_EXT} +BYTE_SWAP ?= ${CREATE_PBL_TOOL_PATH}/byte_swap${BIN_EXT} + +HOST_GCC := gcc + +#SWAP is required for Chassis 2 platforms - LS102, ls1043 and ls1046 for QSPI +ifeq (${SOC},ls1046a) +SOC_NUM := 1046a +SWAP = 1 +CH = 2 +else ifeq (${SOC},ls1043a) +SOC_NUM := 1043a +SWAP = 1 +CH = 2 +else ifeq (${SOC},ls1012a) +SOC_NUM := 1012a +SWAP = 1 +CH = 2 +else ifeq (${SOC},ls1088a) +SOC_NUM := 1088a +CH = 3 +else ifeq (${SOC},ls2088a) +SOC_NUM := 2088a +CH = 3 +else ifeq (${SOC},lx2160a) +SOC_NUM := 2160a +CH = 3 +else ifeq (${SOC},ls1028a) +SOC_NUM := 1028a +CH = 3 +else +$(error "Check SOC Not defined in create_pbl.mk.") +endif + +ifeq (${CH},2) + +include ${CREATE_PBL_TOOL_PATH}/pbl_ch2.mk + +endif #CH2 + +ifeq (${CH},3) + +include ${CREATE_PBL_TOOL_PATH}/pbl_ch3.mk + +endif #CH3 diff --git a/tools/nxp/create_pbl/pbl_ch2.mk b/tools/nxp/create_pbl/pbl_ch2.mk new file mode 100644 index 0000000..e6f1d8b --- /dev/null +++ b/tools/nxp/create_pbl/pbl_ch2.mk @@ -0,0 +1,60 @@ +# +# Copyright 2020 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# +# + +CREATE_PBL ?= ${CREATE_PBL_TOOL_PATH}/create_pbl${BIN_EXT} +BYTE_SWAP ?= ${CREATE_PBL_TOOL_PATH}/byte_swap${BIN_EXT} + +HOST_GCC := gcc + +.PHONY: pbl +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(SECURE_BOOT),yes) +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(RCW),"") + ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + # Generate header for bl2.bin + $(Q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} + # Compile create_pbl tool + ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ + # Add bl2.bin to RCW + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE}\ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl ;\ + # Add header to RCW + ${CREATE_PBL} -r ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -i ${BUILD_PLAT}/hdr_bl2 -b ${BOOT_MODE} -c ${SOC_NUM} \ + -d ${BL2_HDR_LOC} -e ${BL2_HDR_LOC} -o ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl -s;\ + rm ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl +# Swapping of RCW is required for QSPi Chassis 2 devices +ifeq (${BOOT_MODE}, qspi) +ifeq ($(SWAP),1) + ${Q}echo "Byteswapping RCW for QSPI" + ${BYTE_SWAP} ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl; +endif # SWAP +endif # BOOT_MODE + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +else # NON SECURE_BOOT +ifeq ($(RCW),"") + ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + # -a option appends the image for Chassis 3 devices in case of non secure boot + ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE} \ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl ; +# Swapping of RCW is required for QSPi Chassis 2 devices +ifeq (${BOOT_MODE}, qspi) +ifeq ($(SWAP),1) + ${Q}echo "Byteswapping RCW for QSPI" + ${BYTE_SWAP} ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl; +endif # SWAP +endif # BOOT_MODE + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +endif # SECURE_BOOT + + + diff --git a/tools/nxp/create_pbl/pbl_ch3.mk b/tools/nxp/create_pbl/pbl_ch3.mk new file mode 100644 index 0000000..9283474 --- /dev/null +++ b/tools/nxp/create_pbl/pbl_ch3.mk @@ -0,0 +1,71 @@ +# +# Copyright 2018-2022 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# +# +SHELL=/bin/bash + +CREATE_PBL ?= ${CREATE_PBL_TOOL_PATH}/create_pbl${BIN_EXT} +BYTE_SWAP ?= ${CREATE_PBL_TOOL_PATH}/byte_swap${BIN_EXT} + +HOST_GCC := gcc + +BL2_SRC_OFFSET ?= 0x9000 +BL2_HDR_SRC_OFFSET ?= 0x5000 +bl2_hdr_loc=$(shell echo $$(( $(BL2_HDR_SRC_OFFSET) / 1024 ))) +bl2_loc=$(shell echo $$(( $(BL2_SRC_OFFSET) / 1024 ))) + +.PHONY: pbl +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(SECURE_BOOT),yes) +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(RCW),"") + ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + # Generate header for bl2.bin + $(Q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} + + # Compile create_pbl tool + ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ + + # Add Block Copy command for bl2.bin to RCW + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE}\ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -f ${BL2_SRC_OFFSET};\ + + # Add Block Copy command and Load CSF header command to RCW + ${CREATE_PBL} -r ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -i ${BUILD_PLAT}/hdr_bl2 -b ${BOOT_MODE} -c ${SOC_NUM} \ + -d ${BL2_HDR_LOC} -e ${BL2_HDR_LOC} -s -f ${BL2_HDR_SRC_OFFSET} \ + -o ${BUILD_PLAT}/rcw_sec.pbl + + # Sign and add "Load Security Header command to PBI commands + $(Q)$(CST_DIR)/create_hdr_pbi --out ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl --in ${BUILD_PLAT}/rcw_sec.pbl ${PBI_INPUT_FILE} + + # Append the bl2_hdr to the RCW image + @echo "${bl2_hdr_loc}" + dd if=${BUILD_PLAT}/hdr_bl2 of=${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl bs=1K seek=${bl2_hdr_loc} + + # Append the bl2.bin to the RCW image + @echo "${bl2_loc}" + dd if=${BUILD_PLAT}/bl2.bin of=${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl bs=1K seek=${bl2_loc} + + rm ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +else #SECURE_BOOT +ifeq ($(RCW),"") + ${Q}echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; + + # Add Block Copy command and populate boot loc ptrfor bl2.bin to RCW + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE} \ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -f ${BL2_SRC_OFFSET}; + + # Append the bl2.bin to the RCW image + @echo "bl2_loc is ${bl2_loc} KB" + dd if=${BUILD_PLAT}/bl2.bin of=${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl bs=1K seek=${bl2_loc} + + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +endif # SECURE_BOOT diff --git a/tools/renesas/rcar_layout_create/makefile b/tools/renesas/rcar_layout_create/makefile new file mode 100644 index 0000000..d585754 --- /dev/null +++ b/tools/renesas/rcar_layout_create/makefile @@ -0,0 +1,121 @@ +# +# Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +################################################### +# makefile +################################################### + +#output file name +FILE_NAME_SA0 = bootparam_sa0 +FILE_NAME_SA6 = cert_header_sa6 + +OUTPUT_FILE_SA0 = $(FILE_NAME_SA0).elf +OUTPUT_FILE_SA6 = $(FILE_NAME_SA6).elf + +#object file name +OBJ_FILE_SA0 = sa0.o +OBJ_FILE_SA6 = sa6.o + +#linker script name +MEMORY_DEF_SA0 = sa0.ld.S +MEMORY_DEF_SA6 = sa6.ld.S + +################################################### +# Convenience function for adding build definitions +# $(eval $(call add_define,FOO)) will have: +# -DFOO if $(FOO) is empty; -DFOO=$(FOO) otherwise +define add_define +DEFINES += -D$(1)$(if $(value $(1)),=$(value $(1)),) +endef + +# Process RCAR_SA0_SIZE flag +ifndef RCAR_SA0_SIZE +RCAR_SA0_SIZE := 1 +else +ifeq (${RCAR_SA0_SIZE},0) +RCAR_SA0_SIZE := 0 +else +RCAR_SA0_SIZE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA0_SIZE)) + +# Process RCAR_SA6_TYPE flag +ifndef RCAR_SA6_TYPE +RCAR_SA6_TYPE := 0 +else +ifeq (${RCAR_SA6_TYPE},0) +RCAR_SA6_TYPE := 0 +else +RCAR_SA6_TYPE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA6_TYPE)) + +# Handle different VMA adjustment on D3 +ifeq (${RCAR_LSI},${RCAR_D3}) +RCAR_VMA_ADJUST_ADDR := 0xE6320000 +else +RCAR_VMA_ADJUST_ADDR := 0xE6312000 +endif +$(eval $(call add_define,RCAR_VMA_ADJUST_ADDR)) + + +################################################### + +#c compiler +CC = $(CROSS_COMPILE)gcc +CFLAGS += ${DEFINES} +CFLAGS += -I../../include/lib/stdlib + +#Linker +LD = $(CROSS_COMPILE)ld + +#objcopy +objcopy = $(CROSS_COMPILE)objcopy + +#clean +CL = rm -f + +################################################### +.SUFFIXES : .s .c .o + +################################################### +# command + +.PHONY: all +all: $(OUTPUT_FILE_SA0) $(OUTPUT_FILE_SA6) +################################################### +# Linker +################################################### +$(OUTPUT_FILE_SA0) : $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) + $(LD) $(OBJ_FILE_SA0) \ + -T $(MEMORY_DEF_SA0) \ + -o $(OUTPUT_FILE_SA0) \ + -Map $(FILE_NAME_SA0).map \ + + $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec + $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin + +$(OUTPUT_FILE_SA6) : $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) + $(LD) $(OBJ_FILE_SA6) \ + -T $(MEMORY_DEF_SA6) \ + -o $(OUTPUT_FILE_SA6) \ + -Map $(FILE_NAME_SA6).map \ + + $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec + $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin + +################################################### +# Compile +################################################### + +%.o:../%.c + $(CC) -c -I $< -o $@ + +.PHONY: clean +clean: + $(CL) *.bin *.map *.srec *.elf *.o diff --git a/tools/renesas/rcar_layout_create/sa0.c b/tools/renesas/rcar_layout_create/sa0.c new file mode 100644 index 0000000..79354ec --- /dev/null +++ b/tools/renesas/rcar_layout_create/sa0.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define RCAR_SA0_SIZE_SMALL (0) /* for E3/D3 */ +#define RCAR_SA0_SIZE_NORMAL (1) /* for H3/M3/M3N */ + +#define BL2_ADDRESS (0xE6304000) /* BL2 start address */ + +#if (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) +#define BL2_SIZE (80*1024/4) /* BL2 size is 80KB(0x00005000) */ +#else /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ +#define BL2_SIZE (170*1024/4) /* BL2 size is 170KB(0x0000AA00) */ +#endif /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ + +/* SA0 */ +/* 0x00000000 */ +const unsigned int __attribute__ ((section (".sa0_bootrom"))) bootrom_paramA = 0x00000100; +/* 0x00000080 (Map Type 3 for eMMC Boot)*/ +/* 0x000001D4 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_addr3"))) bl2dst_addr3 = BL2_ADDRESS; +/* 0x000002E4 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_size3"))) bl2dst_size3 = BL2_SIZE; +/* 0x00000C00 (Map Type 1 for HyperFlash/QSPI Flash Boot)*/ +/* 0x00000D54 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_addr1"))) bl2dst_addr1 = BL2_ADDRESS; +/* 0x00000E64 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_size1"))) bl2dst_size1 = BL2_SIZE; diff --git a/tools/renesas/rcar_layout_create/sa0.ld.S b/tools/renesas/rcar_layout_create/sa0.ld.S new file mode 100644 index 0000000..98fee23 --- /dev/null +++ b/tools/renesas/rcar_layout_create/sa0.ld.S @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa0_bootrom)) + /* Map Type 3 for eMMC Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x000001D4; /* H'00000080 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr3)) + /* A-side IPL content cert "Size" */ + . = 0x000002E4; /* H'00000080 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size3)) + /* Map Type 1 for HyperFlash/QSPI Flash Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x00000D54; /* H'00000C00 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr1)) + /* A-side IPL content cert "Size" */ + . = 0x00000E64; /* H'00000C00 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size1)) + } + +} diff --git a/tools/renesas/rcar_layout_create/sa6.c b/tools/renesas/rcar_layout_create/sa6.c new file mode 100644 index 0000000..8fafdad --- /dev/null +++ b/tools/renesas/rcar_layout_create/sa6.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#define RCAR_SA6_TYPE_HYPERFLASH (0) +#define RCAR_SA6_TYPE_EMMC (1) + +#if (RCAR_SA6_TYPE == RCAR_SA6_TYPE_HYPERFLASH) + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on flash for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x001C0000U) +/* Reserved */ +#define RCAR_BL31_PARTITION (0x00000000U) +/* Source address on flash for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Reserved */ +#define RCAR_BL32_PARTITION (0x00000000U) +/* Source address on flash for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00640000U) +/* Reserved */ +#define RCAR_BL33_PARTITION (0x00000000U) +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL332_PARTITION (0x00000000U) +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL333_PARTITION (0x00000000U) +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL334_PARTITION (0x00000000U) +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL335_PARTITION (0x00000000U) +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL336_PARTITION (0x00000000U) +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL337_PARTITION (0x00000000U) +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL338_PARTITION (0x00000000U) + +#else /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_HYPERFLASH */ + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on eMMC for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x00040000U) +/* Source partition on eMMC for BL31 */ +#define RCAR_BL31_PARTITION (0x00000001U) +/* Source address on eMMC for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Source partition on eMMC for BL32 */ +#define RCAR_BL32_PARTITION (0x00000001U) +/* Source address on eMMC for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00000000U) +/* Source partition on eMMC for BL33 */ +#define RCAR_BL33_PARTITION (0x00000002U) +/* Reserved */ +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +#define RCAR_BL332_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +#define RCAR_BL333_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +#define RCAR_BL334_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +#define RCAR_BL335_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +#define RCAR_BL336_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +#define RCAR_BL337_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +#define RCAR_BL338_PARTITION (0x00000000U) + +#endif /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_HYPERFLASH */ + +/* Destination address for BL31 */ +#define RCAR_BL31DST_ADDRESS (0x44000000U) +#define RCAR_BL31DST_ADDRESSH (0x00000000U) +/* Destination size for BL31 */ +#define RCAR_BL31DST_SIZE (0x00004000U) +/* Destination address for BL32 */ +#define RCAR_BL32DST_ADDRESS (0x44100000U) +#define RCAR_BL32DST_ADDRESSH (0x00000000U) +/* Destination size for BL32 */ +#define RCAR_BL32DST_SIZE (0x00080000U) +/* Destination address for BL33 */ +#define RCAR_BL33DST_ADDRESS (0x50000000U) +#define RCAR_BL33DST_ADDRESSH (0x00000000U) +/* Destination size for BL33 */ +#define RCAR_BL33DST_SIZE (0x00040000U) +/* Reserved */ +#define RCAR_BL332DST_ADDRESS (0x00000000U) +#define RCAR_BL332DST_ADDRESSH (0x00000000U) +#define RCAR_BL332DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL333DST_ADDRESS (0x00000000U) +#define RCAR_BL333DST_ADDRESSH (0x00000000U) +#define RCAR_BL333DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL334DST_ADDRESS (0x00000000U) +#define RCAR_BL334DST_ADDRESSH (0x00000000U) +#define RCAR_BL334DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL335DST_ADDRESS (0x00000000U) +#define RCAR_BL335DST_ADDRESSH (0x00000000U) +#define RCAR_BL335DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL336DST_ADDRESS (0x00000000U) +#define RCAR_BL336DST_ADDRESSH (0x00000000U) +#define RCAR_BL336DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL337DST_ADDRESS (0x00000000U) +#define RCAR_BL337DST_ADDRESSH (0x00000000U) +#define RCAR_BL337DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL338DST_ADDRESS (0x00000000U) +#define RCAR_BL338DST_ADDRESSH (0x00000000U) +#define RCAR_BL338DST_SIZE (0x00000000U) + +/* SA6 */ +const uint64_t __attribute__ ((section (".sa6_image_num"))) image_num = RCAR_IMAGE_NUM; +const uint64_t __attribute__ ((section (".sa6_bl31src_addr"))) bl31src_addr = RCAR_BL31SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl31partition"))) bl31partition = RCAR_BL31_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl32src_addr"))) bl32src_addr = RCAR_BL32SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl32partition"))) bl32partition = RCAR_BL32_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl33src_addr"))) bl33src_addr = RCAR_BL33SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl33partition"))) bl33partition = RCAR_BL33_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl332src_addr"))) bl332src_addr = RCAR_BL332SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl332partition")))bl332partition = RCAR_BL332_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl333src_addr"))) bl333src_addr = RCAR_BL333SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl333partition")))bl333partition = RCAR_BL333_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl334src_addr"))) bl334src_addr = RCAR_BL334SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl334partition")))bl334partition = RCAR_BL334_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl335src_addr"))) bl335src_addr = RCAR_BL335SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl335partition")))bl335partition = RCAR_BL335_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl336src_addr"))) bl336src_addr = RCAR_BL336SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl336partition")))bl336partition = RCAR_BL336_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl337src_addr"))) bl337src_addr = RCAR_BL337SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl337partition")))bl337partition = RCAR_BL337_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl338src_addr"))) bl338src_addr = RCAR_BL338SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl338partition")))bl338partition = RCAR_BL338_PARTITION; +const uint32_t __attribute__ ((section (".sa6_bl31dst_addr"))) bl31dst_addr = RCAR_BL31DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl31dst_addrh"))) bl31dst_addrh = RCAR_BL31DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl31dst_size"))) bl31dst_size = RCAR_BL31DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl32dst_addr"))) bl32dst_addr = RCAR_BL32DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl32dst_addrh"))) bl32dst_addrh = RCAR_BL32DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl32dst_size"))) bl32dst_size = RCAR_BL32DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl33dst_addr"))) bl33dst_addr = RCAR_BL33DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl33dst_addrh"))) bl33dst_addrh = RCAR_BL33DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl33dst_size"))) bl33dst_size = RCAR_BL33DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl332dst_addr"))) bl332dst_addr = RCAR_BL332DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl332dst_addrh")))bl332dst_addrh = RCAR_BL332DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl332dst_size"))) bl332dst_size = RCAR_BL332DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl333dst_addr"))) bl333dst_addr = RCAR_BL333DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl333dst_addrh")))bl333dst_addrh = RCAR_BL333DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl333dst_size"))) bl333dst_size = RCAR_BL333DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl334dst_addr"))) bl334dst_addr = RCAR_BL334DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl334dst_addrh")))bl334dst_addrh = RCAR_BL334DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl334dst_size"))) bl334dst_size = RCAR_BL334DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl335dst_addr"))) bl335dst_addr = RCAR_BL335DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl335dst_addrh")))bl335dst_addrh = RCAR_BL335DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl335dst_size"))) bl335dst_size = RCAR_BL335DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl336dst_addr"))) bl336dst_addr = RCAR_BL336DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl336dst_addrh")))bl336dst_addrh = RCAR_BL336DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl336dst_size"))) bl336dst_size = RCAR_BL336DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl337dst_addr"))) bl337dst_addr = RCAR_BL337DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl337dst_addrh")))bl337dst_addrh = RCAR_BL337DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl337dst_size"))) bl337dst_size = RCAR_BL337DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl338dst_addr"))) bl338dst_addr = RCAR_BL338DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl338dst_addrh")))bl338dst_addrh = RCAR_BL338DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl338dst_size"))) bl338dst_size = RCAR_BL338DST_SIZE; diff --git a/tools/renesas/rcar_layout_create/sa6.ld.S b/tools/renesas/rcar_layout_create/sa6.ld.S new file mode 100644 index 0000000..9ca0c1d --- /dev/null +++ b/tools/renesas/rcar_layout_create/sa6.ld.S @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa6_image_num)) + . = 0x00000008; + KEEP(*(.sa6_bl31src_addr)) + . = 0x00000010; + KEEP(*(.sa6_bl31partition)) + . = 0x00000018; + KEEP(*(.sa6_bl32src_addr)) + . = 0x00000020; + KEEP(*(.sa6_bl32partition)) + . = 0x00000028; + KEEP(*(.sa6_bl33src_addr)) + . = 0x00000030; + KEEP(*(.sa6_bl33partition)) + . = 0x00000038; + KEEP(*(.sa6_bl332src_addr)) + . = 0x00000040; + KEEP(*(.sa6_bl332partition)) + . = 0x00000048; + KEEP(*(.sa6_bl333src_addr)) + . = 0x00000050; + KEEP(*(.sa6_bl333partition)) + . = 0x00000058; + KEEP(*(.sa6_bl334src_addr)) + . = 0x00000060; + KEEP(*(.sa6_bl334partition)) + . = 0x00000068; + KEEP(*(.sa6_bl335src_addr)) + . = 0x00000070; + KEEP(*(.sa6_bl335partition)) + . = 0x00000078; + KEEP(*(.sa6_bl336src_addr)) + . = 0x00000080; + KEEP(*(.sa6_bl336partition)) + . = 0x00000088; + KEEP(*(.sa6_bl337src_addr)) + . = 0x00000090; + KEEP(*(.sa6_bl337partition)) + . = 0x00000098; + KEEP(*(.sa6_bl338src_addr)) + . = 0x000000A0; + KEEP(*(.sa6_bl338partition)) + . = 0x00000554; + KEEP(*(.sa6_bl31dst_addr)) + . = 0x00000558; + KEEP(*(.sa6_bl31dst_addrh)) + . = 0x00000664; + KEEP(*(.sa6_bl31dst_size)) + . = 0x00000D54; + KEEP(*(.sa6_bl32dst_addr)) + . = 0x00000D58; + KEEP(*(.sa6_bl32dst_addrh)) + . = 0x00000E64; + KEEP(*(.sa6_bl32dst_size)) + . = 0x00001554; + KEEP(*(.sa6_bl33dst_addr)) + . = 0x00001558; + KEEP(*(.sa6_bl33dst_addrh)) + . = 0x00001664; + KEEP(*(.sa6_bl33dst_size)) + . = 0x00001D54; + KEEP(*(.sa6_bl332dst_addr)) + . = 0x00001D58; + KEEP(*(.sa6_bl332dst_addrh)) + . = 0x00001E64; + KEEP(*(.sa6_bl332dst_size)) + . = 0x00002554; + KEEP(*(.sa6_bl333dst_addr)) + . = 0x00002558; + KEEP(*(.sa6_bl333dst_addrh)) + . = 0x00002664; + KEEP(*(.sa6_bl333dst_size)) + . = 0x00002D54; + KEEP(*(.sa6_bl334dst_addr)) + . = 0x00002D58; + KEEP(*(.sa6_bl334dst_addrh)) + . = 0x00002E64; + KEEP(*(.sa6_bl334dst_size)) + . = 0x00003554; + KEEP(*(.sa6_bl335dst_addr)) + . = 0x00003558; + KEEP(*(.sa6_bl335dst_addrh)) + . = 0x00003664; + KEEP(*(.sa6_bl335dst_size)) + . = 0x00003D54; + KEEP(*(.sa6_bl336dst_addr)) + . = 0x00003D58; + KEEP(*(.sa6_bl336dst_addrh)) + . = 0x00003E64; + KEEP(*(.sa6_bl336dst_size)) + . = 0x00004554; + KEEP(*(.sa6_bl337dst_addr)) + . = 0x00004558; + KEEP(*(.sa6_bl337dst_addrh)) + . = 0x00004664; + KEEP(*(.sa6_bl337dst_size)) + . = 0x00004D54; + KEEP(*(.sa6_bl338dst_addr)) + . = 0x00004D58; + KEEP(*(.sa6_bl338dst_addrh)) + . = 0x00004E64; + KEEP(*(.sa6_bl338dst_size)) + } + +} diff --git a/tools/renesas/rzg_layout_create/makefile b/tools/renesas/rzg_layout_create/makefile new file mode 100644 index 0000000..2d438b9 --- /dev/null +++ b/tools/renesas/rzg_layout_create/makefile @@ -0,0 +1,118 @@ +# +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +################################################### +# makefile +################################################### + +#output file name +FILE_NAME_SA0 = bootparam_sa0 +FILE_NAME_SA6 = cert_header_sa6 + +OUTPUT_FILE_SA0 = $(FILE_NAME_SA0).elf +OUTPUT_FILE_SA6 = $(FILE_NAME_SA6).elf + +#object file name +OBJ_FILE_SA0 = sa0.o +OBJ_FILE_SA6 = sa6.o + +#linker script name +MEMORY_DEF_SA0 = sa0.ld.S +MEMORY_DEF_SA6 = sa6.ld.S + +################################################### +# Convenience function for adding build definitions +# $(eval $(call add_define,FOO)) will have: +# -DFOO if $(FOO) is empty; -DFOO=$(FOO) otherwise +define add_define +DEFINES += -D$(1)$(if $(value $(1)),=$(value $(1)),) +endef + +# Process RCAR_SA0_SIZE flag +ifndef RCAR_SA0_SIZE +RCAR_SA0_SIZE := 1 +else +ifeq (${RCAR_SA0_SIZE},0) +RCAR_SA0_SIZE := 0 +else +RCAR_SA0_SIZE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA0_SIZE)) + +# Process RCAR_SA6_TYPE flag +ifndef RCAR_SA6_TYPE +RCAR_SA6_TYPE := 0 +else +ifeq (${RCAR_SA6_TYPE},0) +RCAR_SA6_TYPE := 0 +else +RCAR_SA6_TYPE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA6_TYPE)) + +RCAR_VMA_ADJUST_ADDR := 0xE6320000 +$(eval $(call add_define,RCAR_VMA_ADJUST_ADDR)) + + +################################################### + +#c compiler +CC = $(CROSS_COMPILE)gcc +CFLAGS += ${DEFINES} +CFLAGS += -nostdinc \ + -I../../../include/lib/libc \ + -I../../../include/lib/libc/aarch64 + +#Linker +LD = $(CROSS_COMPILE)ld + +#objcopy +objcopy = $(CROSS_COMPILE)objcopy + +#clean +CL = rm -f + +################################################### +.SUFFIXES : .s .c .o + +################################################### +# command + +.PHONY: all +all: $(OUTPUT_FILE_SA0) $(OUTPUT_FILE_SA6) +################################################### +# Linker +################################################### +$(OUTPUT_FILE_SA0) : $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) + $(LD) $(OBJ_FILE_SA0) \ + -T $(MEMORY_DEF_SA0) \ + -o $(OUTPUT_FILE_SA0) \ + -Map $(FILE_NAME_SA0).map \ + + $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec + $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin + +$(OUTPUT_FILE_SA6) : $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) + $(LD) $(OBJ_FILE_SA6) \ + -T $(MEMORY_DEF_SA6) \ + -o $(OUTPUT_FILE_SA6) \ + -Map $(FILE_NAME_SA6).map \ + + $(objcopy) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec + $(objcopy) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin + +################################################### +# Compile +################################################### + +%.o:../%.c + $(CC) -c -I $< -o $@ + +.PHONY: clean +clean: + $(CL) *.bin *.map *.srec *.elf *.o diff --git a/tools/renesas/rzg_layout_create/sa0.c b/tools/renesas/rzg_layout_create/sa0.c new file mode 100644 index 0000000..763d3a5 --- /dev/null +++ b/tools/renesas/rzg_layout_create/sa0.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define RCAR_SA0_SIZE_SMALL (0) /* for RZ/G2E */ +#define RCAR_SA0_SIZE_NORMAL (1) /* for RZ/G2[HMN] */ + +#define BL2_ADDRESS (0xE6304000) /* BL2 start address */ + +#if (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) +#define BL2_SIZE (80*1024/4) /* BL2 size is 80KB(0x00005000) */ +#else /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ +#define BL2_SIZE (170*1024/4) /* BL2 size is 170KB(0x0000AA00) */ +#endif /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ + +/* SA0 */ +/* 0x00000000 */ +const unsigned int __attribute__ ((section(".sa0_bootrom"))) bootrom_paramA = 0x00000100; +/* 0x00000080 (Map Type 3 for eMMC Boot)*/ +/* 0x000001D4 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_addr3"))) bl2dst_addr3 = BL2_ADDRESS; +/* 0x000002E4 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_size3"))) bl2dst_size3 = BL2_SIZE; +/* 0x00000C00 (Map Type 1 for HyperFlash/QSPI Flash Boot)*/ +/* 0x00000D54 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_addr1"))) bl2dst_addr1 = BL2_ADDRESS; +/* 0x00000E64 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_size1"))) bl2dst_size1 = BL2_SIZE; diff --git a/tools/renesas/rzg_layout_create/sa0.ld.S b/tools/renesas/rzg_layout_create/sa0.ld.S new file mode 100644 index 0000000..23e2b23 --- /dev/null +++ b/tools/renesas/rzg_layout_create/sa0.ld.S @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa0_bootrom)) + /* Map Type 3 for eMMC Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x000001D4; /* H'00000080 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr3)) + /* A-side IPL content cert "Size" */ + . = 0x000002E4; /* H'00000080 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size3)) + /* Map Type 1 for HyperFlash/QSPI Flash Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x00000D54; /* H'00000C00 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr1)) + /* A-side IPL content cert "Size" */ + . = 0x00000E64; /* H'00000C00 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size1)) + } + +} diff --git a/tools/renesas/rzg_layout_create/sa6.c b/tools/renesas/rzg_layout_create/sa6.c new file mode 100644 index 0000000..76e3dc5 --- /dev/null +++ b/tools/renesas/rzg_layout_create/sa6.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> + +#define RCAR_SA6_TYPE_QSPIFLASH (0) +#define RCAR_SA6_TYPE_EMMC (1) + +#if (RCAR_SA6_TYPE == RCAR_SA6_TYPE_QSPIFLASH) + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on flash for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x001C0000U) +/* Reserved */ +#define RCAR_BL31_PARTITION (0x00000000U) +/* Source address on flash for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Reserved */ +#define RCAR_BL32_PARTITION (0x00000000U) +/* Source address on flash for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00300000U) +/* Reserved */ +#define RCAR_BL33_PARTITION (0x00000000U) +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL332_PARTITION (0x00000000U) +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL333_PARTITION (0x00000000U) +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL334_PARTITION (0x00000000U) +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL335_PARTITION (0x00000000U) +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL336_PARTITION (0x00000000U) +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL337_PARTITION (0x00000000U) +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL338_PARTITION (0x00000000U) + +#else /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_EMMC */ + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on eMMC for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x00040000U) +/* Source partition on eMMC for BL31 */ +#define RCAR_BL31_PARTITION (0x00000001U) +/* Source address on eMMC for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Source partition on eMMC for BL32 */ +#define RCAR_BL32_PARTITION (0x00000001U) +/* Source address on eMMC for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00000000U) +/* Source partition on eMMC for BL33 */ +#define RCAR_BL33_PARTITION (0x00000002U) +/* Reserved */ +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +#define RCAR_BL332_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +#define RCAR_BL333_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +#define RCAR_BL334_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +#define RCAR_BL335_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +#define RCAR_BL336_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +#define RCAR_BL337_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +#define RCAR_BL338_PARTITION (0x00000000U) + +#endif /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_QSPIFLASH */ + +/* Destination address for BL31 */ +#define RCAR_BL31DST_ADDRESS (0x44000000U) +#define RCAR_BL31DST_ADDRESSH (0x00000000U) +/* Destination size for BL31 */ +#define RCAR_BL31DST_SIZE (0x00004000U) +/* Destination address for BL32 */ +#define RCAR_BL32DST_ADDRESS (0x44100000U) +#define RCAR_BL32DST_ADDRESSH (0x00000000U) +/* Destination size for BL32 */ +#define RCAR_BL32DST_SIZE (0x00040000U) +/* Destination address for BL33 */ +#define RCAR_BL33DST_ADDRESS (0x50000000U) +#define RCAR_BL33DST_ADDRESSH (0x00000000U) +/* Destination size for BL33 */ +#define RCAR_BL33DST_SIZE (0x00040000U) +/* Reserved */ +#define RCAR_BL332DST_ADDRESS (0x00000000U) +#define RCAR_BL332DST_ADDRESSH (0x00000000U) +#define RCAR_BL332DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL333DST_ADDRESS (0x00000000U) +#define RCAR_BL333DST_ADDRESSH (0x00000000U) +#define RCAR_BL333DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL334DST_ADDRESS (0x00000000U) +#define RCAR_BL334DST_ADDRESSH (0x00000000U) +#define RCAR_BL334DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL335DST_ADDRESS (0x00000000U) +#define RCAR_BL335DST_ADDRESSH (0x00000000U) +#define RCAR_BL335DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL336DST_ADDRESS (0x00000000U) +#define RCAR_BL336DST_ADDRESSH (0x00000000U) +#define RCAR_BL336DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL337DST_ADDRESS (0x00000000U) +#define RCAR_BL337DST_ADDRESSH (0x00000000U) +#define RCAR_BL337DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL338DST_ADDRESS (0x00000000U) +#define RCAR_BL338DST_ADDRESSH (0x00000000U) +#define RCAR_BL338DST_SIZE (0x00000000U) + +/* SA6 */ +const uint64_t __attribute__ ((section(".sa6_image_num"))) + image_num = RCAR_IMAGE_NUM; +const uint64_t __attribute__ ((section(".sa6_bl31src_addr"))) + bl31src_addr = RCAR_BL31SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl31partition"))) + bl31partition = RCAR_BL31_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl32src_addr"))) + bl32src_addr = RCAR_BL32SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl32partition"))) + bl32partition = RCAR_BL32_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl33src_addr"))) + bl33src_addr = RCAR_BL33SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl33partition"))) + bl33partition = RCAR_BL33_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl332src_addr"))) + bl332src_addr = RCAR_BL332SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl332partition"))) + bl332partition = RCAR_BL332_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl333src_addr"))) + bl333src_addr = RCAR_BL333SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl333partition"))) + bl333partition = RCAR_BL333_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl334src_addr"))) + bl334src_addr = RCAR_BL334SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl334partition"))) + bl334partition = RCAR_BL334_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl335src_addr"))) + bl335src_addr = RCAR_BL335SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl335partition"))) + bl335partition = RCAR_BL335_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl336src_addr"))) + bl336src_addr = RCAR_BL336SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl336partition"))) + bl336partition = RCAR_BL336_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl337src_addr"))) + bl337src_addr = RCAR_BL337SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl337partition"))) + bl337partition = RCAR_BL337_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl338src_addr"))) + bl338src_addr = RCAR_BL338SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl338partition"))) + bl338partition = RCAR_BL338_PARTITION; +const uint32_t __attribute__ ((section(".sa6_bl31dst_addr"))) + bl31dst_addr = RCAR_BL31DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl31dst_addrh"))) + bl31dst_addrh = RCAR_BL31DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl31dst_size"))) + bl31dst_size = RCAR_BL31DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl32dst_addr"))) + bl32dst_addr = RCAR_BL32DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl32dst_addrh"))) + bl32dst_addrh = RCAR_BL32DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl32dst_size"))) + bl32dst_size = RCAR_BL32DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl33dst_addr"))) + bl33dst_addr = RCAR_BL33DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl33dst_addrh"))) + bl33dst_addrh = RCAR_BL33DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl33dst_size"))) + bl33dst_size = RCAR_BL33DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl332dst_addr"))) + bl332dst_addr = RCAR_BL332DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl332dst_addrh"))) + bl332dst_addrh = RCAR_BL332DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl332dst_size"))) + bl332dst_size = RCAR_BL332DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl333dst_addr"))) + bl333dst_addr = RCAR_BL333DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl333dst_addrh"))) + bl333dst_addrh = RCAR_BL333DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl333dst_size"))) + bl333dst_size = RCAR_BL333DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl334dst_addr"))) + bl334dst_addr = RCAR_BL334DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl334dst_addrh"))) + bl334dst_addrh = RCAR_BL334DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl334dst_size"))) + bl334dst_size = RCAR_BL334DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl335dst_addr"))) + bl335dst_addr = RCAR_BL335DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl335dst_addrh"))) + bl335dst_addrh = RCAR_BL335DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl335dst_size"))) + bl335dst_size = RCAR_BL335DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl336dst_addr"))) + bl336dst_addr = RCAR_BL336DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl336dst_addrh"))) + bl336dst_addrh = RCAR_BL336DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl336dst_size"))) + bl336dst_size = RCAR_BL336DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl337dst_addr"))) + bl337dst_addr = RCAR_BL337DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl337dst_addrh"))) + bl337dst_addrh = RCAR_BL337DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl337dst_size"))) + bl337dst_size = RCAR_BL337DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl338dst_addr"))) + bl338dst_addr = RCAR_BL338DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl338dst_addrh"))) + bl338dst_addrh = RCAR_BL338DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl338dst_size"))) + bl338dst_size = RCAR_BL338DST_SIZE; diff --git a/tools/renesas/rzg_layout_create/sa6.ld.S b/tools/renesas/rzg_layout_create/sa6.ld.S new file mode 100644 index 0000000..efe40b0 --- /dev/null +++ b/tools/renesas/rzg_layout_create/sa6.ld.S @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa6_image_num)) + . = 0x00000008; + KEEP(*(.sa6_bl31src_addr)) + . = 0x00000010; + KEEP(*(.sa6_bl31partition)) + . = 0x00000018; + KEEP(*(.sa6_bl32src_addr)) + . = 0x00000020; + KEEP(*(.sa6_bl32partition)) + . = 0x00000028; + KEEP(*(.sa6_bl33src_addr)) + . = 0x00000030; + KEEP(*(.sa6_bl33partition)) + . = 0x00000038; + KEEP(*(.sa6_bl332src_addr)) + . = 0x00000040; + KEEP(*(.sa6_bl332partition)) + . = 0x00000048; + KEEP(*(.sa6_bl333src_addr)) + . = 0x00000050; + KEEP(*(.sa6_bl333partition)) + . = 0x00000058; + KEEP(*(.sa6_bl334src_addr)) + . = 0x00000060; + KEEP(*(.sa6_bl334partition)) + . = 0x00000068; + KEEP(*(.sa6_bl335src_addr)) + . = 0x00000070; + KEEP(*(.sa6_bl335partition)) + . = 0x00000078; + KEEP(*(.sa6_bl336src_addr)) + . = 0x00000080; + KEEP(*(.sa6_bl336partition)) + . = 0x00000088; + KEEP(*(.sa6_bl337src_addr)) + . = 0x00000090; + KEEP(*(.sa6_bl337partition)) + . = 0x00000098; + KEEP(*(.sa6_bl338src_addr)) + . = 0x000000A0; + KEEP(*(.sa6_bl338partition)) + . = 0x00000554; + KEEP(*(.sa6_bl31dst_addr)) + . = 0x00000558; + KEEP(*(.sa6_bl31dst_addrh)) + . = 0x00000664; + KEEP(*(.sa6_bl31dst_size)) + . = 0x00000D54; + KEEP(*(.sa6_bl32dst_addr)) + . = 0x00000D58; + KEEP(*(.sa6_bl32dst_addrh)) + . = 0x00000E64; + KEEP(*(.sa6_bl32dst_size)) + . = 0x00001554; + KEEP(*(.sa6_bl33dst_addr)) + . = 0x00001558; + KEEP(*(.sa6_bl33dst_addrh)) + . = 0x00001664; + KEEP(*(.sa6_bl33dst_size)) + . = 0x00001D54; + KEEP(*(.sa6_bl332dst_addr)) + . = 0x00001D58; + KEEP(*(.sa6_bl332dst_addrh)) + . = 0x00001E64; + KEEP(*(.sa6_bl332dst_size)) + . = 0x00002554; + KEEP(*(.sa6_bl333dst_addr)) + . = 0x00002558; + KEEP(*(.sa6_bl333dst_addrh)) + . = 0x00002664; + KEEP(*(.sa6_bl333dst_size)) + . = 0x00002D54; + KEEP(*(.sa6_bl334dst_addr)) + . = 0x00002D58; + KEEP(*(.sa6_bl334dst_addrh)) + . = 0x00002E64; + KEEP(*(.sa6_bl334dst_size)) + . = 0x00003554; + KEEP(*(.sa6_bl335dst_addr)) + . = 0x00003558; + KEEP(*(.sa6_bl335dst_addrh)) + . = 0x00003664; + KEEP(*(.sa6_bl335dst_size)) + . = 0x00003D54; + KEEP(*(.sa6_bl336dst_addr)) + . = 0x00003D58; + KEEP(*(.sa6_bl336dst_addrh)) + . = 0x00003E64; + KEEP(*(.sa6_bl336dst_size)) + . = 0x00004554; + KEEP(*(.sa6_bl337dst_addr)) + . = 0x00004558; + KEEP(*(.sa6_bl337dst_addrh)) + . = 0x00004664; + KEEP(*(.sa6_bl337dst_size)) + . = 0x00004D54; + KEEP(*(.sa6_bl338dst_addr)) + . = 0x00004D58; + KEEP(*(.sa6_bl338dst_addrh)) + . = 0x00004E64; + KEEP(*(.sa6_bl338dst_size)) + } + +} diff --git a/tools/sptool/Makefile b/tools/sptool/Makefile new file mode 100644 index 0000000..1fa85fb --- /dev/null +++ b/tools/sptool/Makefile @@ -0,0 +1,50 @@ +# +# Copyright (c) 2018-2020, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk + +SPTOOL ?= sptool${BIN_EXT} +PROJECT := $(notdir ${SPTOOL}) +OBJECTS := sptool.o +V ?= 0 + +override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 +HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG +else + HOSTCCFLAGS += -O2 +endif + +ifeq (${V},0) + Q := @ +else + Q := +endif + +INCLUDE_PATHS := -I../../include/tools_share + +HOSTCC ?= gcc + +.PHONY: all clean distclean + +all: ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + @echo " HOSTLD $@" + ${Q}${HOSTCC} ${OBJECTS} -o $@ ${LDLIBS} + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} + +%.o: %.c Makefile + @echo " HOSTCC $<" + ${Q}${HOSTCC} -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ + +clean: + $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS}) diff --git a/tools/sptool/sp_mk_generator.py b/tools/sptool/sp_mk_generator.py new file mode 100644 index 0000000..c69e0a7 --- /dev/null +++ b/tools/sptool/sp_mk_generator.py @@ -0,0 +1,278 @@ +#!/usr/bin/python3 +# Copyright (c) 2020-2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +""" +This script is invoked by Make system and generates secure partition makefile. +It expects platform provided secure partition layout file which contains list +of Secure Partition Images and Partition manifests(PM). +Layout file can exist outside of TF-A tree and the paths of Image and PM files +must be relative to it. + +This script parses the layout file and generates a make file which updates +FDT_SOURCES, FIP_ARGS, CRT_ARGS and SPTOOL_ARGS which are used in later build +steps. +If the SP entry in the layout file has a "uuid" field the scripts gets the UUID +from there, otherwise it parses the associated partition manifest and extracts +the UUID from there. + +param1: Generated mk file "sp_gen.mk" +param2: "SP_LAYOUT_FILE", json file containing platform provided information +param3: plat out directory +param4: CoT parameter +param5: Generated dts file "sp_list_fragment.dts" + +Generated "sp_gen.mk" file contains triplet of following information for each +Secure Partition entry + FDT_SOURCES += sp1.dts + SPTOOL_ARGS += -i sp1.bin:sp1.dtb -o sp1.pkg + FIP_ARGS += --blob uuid=XXXXX-XXX...,file=sp1.pkg + CRT_ARGS += --sp-pkg1 sp1.pkg + +A typical SP_LAYOUT_FILE file will look like +{ + "SP1" : { + "image": "sp1.bin", + "pm": "test/sp1.dts" + }, + + "SP2" : { + "image": "sp2.bin", + "pm": "test/sp2.dts", + "uuid": "1b1820fe-48f7-4175-8999-d51da00b7c9f" + } + + ... +} + +""" +import json +import os +import re +import sys +import uuid +from spactions import SpSetupActions + +MAX_SP = 8 +UUID_LEN = 4 + +# Some helper functions to access args propagated to the action functions in +# SpSetupActions framework. +def check_sp_mk_gen(args :dict): + if "sp_gen_mk" not in args.keys(): + raise Exception(f"Path to file sp_gen.mk needs to be in 'args'.") + +def check_out_dir(args :dict): + if "out_dir" not in args.keys() or not os.path.isdir(args["out_dir"]): + raise Exception("Define output folder with \'out_dir\' key.") + +def check_sp_layout_dir(args :dict): + if "sp_layout_dir" not in args.keys() or not os.path.isdir(args["sp_layout_dir"]): + raise Exception("Define output folder with \'sp_layout_dir\' key.") + +def write_to_sp_mk_gen(content, args :dict): + check_sp_mk_gen(args) + with open(args["sp_gen_mk"], "a") as f: + f.write(f"{content}\n") + +def get_sp_manifest_full_path(sp_node, args :dict): + check_sp_layout_dir(args) + return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["pm"])) + +def get_sp_img_full_path(sp_node, args :dict): + check_sp_layout_dir(args) + return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["image"])) + +def get_sp_pkg(sp, args :dict): + check_out_dir(args) + return os.path.join(args["out_dir"], f"{sp}.pkg") + +def is_line_in_sp_gen(line, args :dict): + with open(args["sp_gen_mk"], "r") as f: + sppkg_rule = [l for l in f if line in l] + return len(sppkg_rule) != 0 + +def get_file_from_layout(node): + ''' Helper to fetch a file path from sp_layout.json. ''' + if type(node) is dict and "file" in node.keys(): + return node["file"] + return node + +def get_offset_from_layout(node): + ''' Helper to fetch an offset from sp_layout.json. ''' + if type(node) is dict and "offset" in node.keys(): + return int(node["offset"], 0) + return None + +def get_image_offset(node): + ''' Helper to fetch image offset from sp_layout.json ''' + return get_offset_from_layout(node["image"]) + +def get_pm_offset(node): + ''' Helper to fetch pm offset from sp_layout.json ''' + return get_offset_from_layout(node["pm"]) + +def get_uuid(sp_layout, sp, args :dict): + ''' Helper to fetch uuid from pm file listed in sp_layout.json''' + if "uuid" in sp_layout[sp]: + # Extract the UUID from the JSON file if the SP entry has a 'uuid' field + uuid_std = uuid.UUID(sp_layout[sp]['uuid']) + else: + with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f: + uuid_lines = [l for l in pm_f if 'uuid' in l] + assert(len(uuid_lines) == 1) + # The uuid field in SP manifest is the little endian representation + # mapped to arguments as described in SMCCC section 5.3. + # Convert each unsigned integer value to a big endian representation + # required by fiptool. + uuid_parsed = re.findall("0x([0-9a-f]+)", uuid_lines[0]) + y = list(map(bytearray.fromhex, uuid_parsed)) + z = [int.from_bytes(i, byteorder='little', signed=False) for i in y] + uuid_std = uuid.UUID(f'{z[0]:08x}{z[1]:08x}{z[2]:08x}{z[3]:08x}') + return uuid_std + +def get_load_address(sp_layout, sp, args :dict): + ''' Helper to fetch load-address from pm file listed in sp_layout.json''' + with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f: + load_address_lines = [l for l in pm_f if 'load-address' in l] + assert(len(load_address_lines) == 1) + load_address_parsed = re.search("(0x[0-9a-f]+)", load_address_lines[0]) + return load_address_parsed.group(0) + + +@SpSetupActions.sp_action(global_action=True) +def check_max_sps(sp_layout, _, args :dict): + ''' Check validate the maximum number of SPs is respected. ''' + if len(sp_layout.keys()) > MAX_SP: + raise Exception(f"Too many SPs in SP layout file. Max: {MAX_SP}") + return args + +@SpSetupActions.sp_action +def gen_fdt_sources(sp_layout, sp, args :dict): + ''' Generate FDT_SOURCES values for a given SP. ''' + manifest_path = get_sp_manifest_full_path(sp_layout[sp], args) + write_to_sp_mk_gen(f"FDT_SOURCES += {manifest_path}", args) + return args + +@SpSetupActions.sp_action +def gen_sptool_args(sp_layout, sp, args :dict): + ''' Generate Sp Pkgs rules. ''' + sp_pkg = get_sp_pkg(sp, args) + sp_dtb_name = os.path.basename(get_file_from_layout(sp_layout[sp]["pm"]))[:-1] + "b" + sp_dtb = os.path.join(args["out_dir"], f"fdts/{sp_dtb_name}") + sp_img = get_sp_img_full_path(sp_layout[sp], args) + + # Do not generate rule if already there. + if is_line_in_sp_gen(f'{sp_pkg}:', args): + return args + write_to_sp_mk_gen(f"SP_PKGS += {sp_pkg}\n", args) + + sptool_args = f" -i {sp_img}:{sp_dtb}" + pm_offset = get_pm_offset(sp_layout[sp]) + sptool_args += f" --pm-offset {pm_offset}" if pm_offset is not None else "" + image_offset = get_image_offset(sp_layout[sp]) + sptool_args += f" --img-offset {image_offset}" if image_offset is not None else "" + sptool_args += f" -o {sp_pkg}" + sppkg_rule = f''' +{sp_pkg}: {sp_dtb} {sp_img} +\t$(Q)echo Generating {sp_pkg} +\t$(Q)$(PYTHON) $(SPTOOL) {sptool_args} +''' + write_to_sp_mk_gen(sppkg_rule, args) + return args + +@SpSetupActions.sp_action(global_action=True, exec_order=1) +def check_dualroot(sp_layout, _, args :dict): + ''' Validate the amount of SPs from SiP and Platform owners. ''' + if not args.get("dualroot"): + return args + args["split"] = int(MAX_SP / 2) + owners = [sp_layout[sp].get("owner") for sp in sp_layout] + args["plat_max_count"] = owners.count("Plat") + # If it is owned by the platform owner, it is assigned to the SiP. + args["sip_max_count"] = len(sp_layout.keys()) - args["plat_max_count"] + if args["sip_max_count"] > args["split"] or args["sip_max_count"] > args["split"]: + print(f"WARN: SiP Secure Partitions should not be more than {args['split']}") + # Counters for gen_crt_args. + args["sip_count"] = 1 + args["plat_count"] = 1 + return args + +@SpSetupActions.sp_action +def gen_crt_args(sp_layout, sp, args :dict): + ''' Append CRT_ARGS. ''' + # If "dualroot" is configured, 'sp_pkg_idx' depends on whether the SP is owned + # by the "SiP" or the "Plat". + if args.get("dualroot"): + # If the owner is not specified as "Plat", default to "SiP". + if sp_layout[sp].get("owner") == "Plat": + if args["plat_count"] > args["plat_max_count"]: + raise ValueError("plat_count can't surpass plat_max_count in args.") + sp_pkg_idx = args["plat_count"] + args["split"] + args["plat_count"] += 1 + else: + if args["sip_count"] > args["sip_max_count"]: + raise ValueError("sip_count can't surpass sip_max_count in args.") + sp_pkg_idx = args["sip_count"] + args["sip_count"] += 1 + else: + sp_pkg_idx = [k for k in sp_layout.keys()].index(sp) + 1 + write_to_sp_mk_gen(f"CRT_ARGS += --sp-pkg{sp_pkg_idx} {get_sp_pkg(sp, args)}\n", args) + return args + +@SpSetupActions.sp_action +def gen_fiptool_args(sp_layout, sp, args :dict): + ''' Generate arguments for the FIP Tool. ''' + uuid_std = get_uuid(sp_layout, sp, args) + write_to_sp_mk_gen(f"FIP_ARGS += --blob uuid={str(uuid_std)},file={get_sp_pkg(sp, args)}\n", args) + return args + +@SpSetupActions.sp_action +def gen_fconf_fragment(sp_layout, sp, args: dict): + ''' Generate the fconf fragment file''' + with open(args["fconf_fragment"], "a") as f: + uuid = get_uuid(sp_layout, sp, args) + owner = "Plat" if sp_layout[sp].get("owner") == "Plat" else "SiP" + + if "physical-load-address" in sp_layout[sp].keys(): + load_address = sp_layout[sp]["physical-load-address"] + else: + load_address = get_load_address(sp_layout, sp, args) + + f.write( +f'''\ +{sp} {{ + uuid = "{uuid}"; + load-address = <{load_address}>; + owner = "{owner}"; +}}; + +''') + return args + +def init_sp_actions(sys): + # Initialize arguments for the SP actions framework + args = {} + args["sp_gen_mk"] = os.path.abspath(sys.argv[1]) + sp_layout_file = os.path.abspath(sys.argv[2]) + args["sp_layout_dir"] = os.path.dirname(sp_layout_file) + args["out_dir"] = os.path.abspath(sys.argv[3]) + args["dualroot"] = sys.argv[4] == "dualroot" + args["fconf_fragment"] = os.path.abspath(sys.argv[5]) + + + with open(sp_layout_file) as json_file: + sp_layout = json.load(json_file) + #Clear content of file "sp_gen.mk". + with open(args["sp_gen_mk"], "w"): + None + #Clear content of file "fconf_fragment". + with open(args["fconf_fragment"], "w"): + None + + return args, sp_layout + +if __name__ == "__main__": + args, sp_layout = init_sp_actions(sys) + SpSetupActions.run_actions(sp_layout, args) diff --git a/tools/sptool/spactions.py b/tools/sptool/spactions.py new file mode 100644 index 0000000..ff28ebb --- /dev/null +++ b/tools/sptool/spactions.py @@ -0,0 +1,155 @@ +#!/usr/bin/python3 +# Copyright (c) 2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +''' +This is a python module for defining and executing SP setup actions, targeting +a system deploying an SPM implementation. +Each action consists of a function, that processes the SP layout json file and +other provided arguments. +At the core of this is the SpSetupActions which provides a means to register +the functions into a table of actions, and execute them all when invoking +SpSetupActions.run_actions. +Registering the function is done by using the decorator '@SpSetupActions.sp_action' +at function definition. + +Functions can be called: +- once only, or per SP defined in the SP layout file; +- following an order, from lowest to highest of their execution order. +More information in the doc comments below. +''' +import bisect + +DEFAULT_ACTION_ORDER = 100 + +class _ConfiguredAction: + """ + Wraps action function with its configuration. + """ + def __init__(self, action, exec_order=DEFAULT_ACTION_ORDER, global_action=True, log_calls = False): + self.exec_order = exec_order + self.__name__ = action.__name__ + def logged_action(action): + def inner_logged_action(sp_layout, sp, args :dict): + print(f"Calling {action.__name__} -> {sp}") + return action(sp_layout, sp, args) + return inner_logged_action + self.action = logged_action(action) if log_calls is True else action + self.global_action = global_action + + def __lt__(self, other): + """ + To allow for ordered inserts in a list of actions. + """ + return self.exec_order < other.exec_order + + def __call__(self, sp_layout, sp, args :dict): + """ + Calls action function. + """ + return self.action(sp_layout, sp, args) + + def __repr__(self) -> str: + """ + Pretty format to show debug information about the action. + """ + return f"func: {self.__name__}; global:{self.global_action}; exec_order: {self.exec_order}" + +class SpSetupActions: + actions = [] + + def sp_action(in_action = None, global_action = False, log_calls=False, exec_order=DEFAULT_ACTION_ORDER): + """ + Function decorator that registers and configures action. + + :param in_action - function to register + :param global_action - make the function global, i.e. make it be + only called once. + :param log_calls - at every call to action, a useful log will be printed. + :param exec_order - action's calling order. + """ + def append_action(action): + action = _ConfiguredAction(action, exec_order, global_action, log_calls) + bisect.insort(SpSetupActions.actions, action) + return action + if in_action is not None: + return append_action(in_action) + return append_action + + def run_actions(sp_layout: dict, args: dict, verbose=False): + """ + Executes all actions in accordance to their registering configuration: + - If set as "global" it will be called once. + - Actions are called respecting the order established by their "exec_order" field. + + :param sp_layout - dictionary containing the SP layout information. + :param args - arguments to be propagated through the call of actions. + :param verbose - prints actions information in order of execution. + """ + args["called"] = [] # for debug purposes + def append_called(action, sp, args :dict): + args["called"].append(f"{action.__name__} -> {sp}") + return args + + for action in SpSetupActions.actions: + if verbose: + print(f"Calling {action}") + if action.global_action: + scope = "global" + args = action(sp_layout, scope, args) + args = append_called(action, scope, args) + else: + # Functions that are not global called for each SP defined in + # the SP layout. + for sp in sp_layout.keys(): + args = action(sp_layout, sp, args) + args = append_called(action, sp, args) + +if __name__ == "__main__": + # Executing this module will have the following test code/playground executed + sp_layout = { + "partition1" : { + "boot-info": True, + "image": { + "file": "partition.bin", + "offset":"0x2000" + }, + "pm": { + "file": "cactus.dts", + "offset":"0x1000" + }, + "owner": "SiP" + }, + "partition2" : { + "image": "partition.bin", + "pm": "cactus-secondary.dts", + "owner": "Plat" + }, + "partition3" : { + "image": "partition.bin", + "pm": "cactus-tertiary.dts", + "owner": "Plat" + }, + "partition4" : { + "image": "ivy.bin", + "pm": "ivy.dts", + "owner": "Plat" + } + } + + #Example of how to use this module + @SpSetupActions.sp_action(global_action=True) + def my_action1(sp_layout, _, args :dict): + print(f"inside function my_action1{sp_layout}\n\n args:{args})") + return args # Always return args in action function. + @SpSetupActions.sp_action(exec_order=1) + def my_action2(sp_layout, sp_name, args :dict): + print(f"inside function my_action2; SP: {sp_name} {sp_layout} args:{args}") + return args + + # Example arguments to be propagated through the functions. + # 'args' can be extended in the action functions. + args = dict() + args["arg1"] = 0xEEE + args["arg2"] = 0xFF + SpSetupActions.run_actions(sp_layout, args) diff --git a/tools/sptool/sptool.py b/tools/sptool/sptool.py new file mode 100755 index 0000000..ae7df92 --- /dev/null +++ b/tools/sptool/sptool.py @@ -0,0 +1,145 @@ +#!/usr/bin/python3 +# Copyright (c) 2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# +# Copyright 2022 The Hafnium Authors. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://opensource.org/licenses/BSD-3-Clause. + +""" +Script which generates a Secure Partition package. +https://trustedfirmware-a.readthedocs.io/en/latest/components/secure-partition-manager.html#secure-partition-packages +""" + +import argparse +from collections import namedtuple +import sys +from shutil import copyfileobj +import os + +HF_PAGE_SIZE = 0x1000 # bytes +HEADER_ELEMENT_BYTES = 4 # bytes +MANIFEST_IMAGE_SPLITTER=':' +PM_OFFSET_DEFAULT = "0x1000" +IMG_OFFSET_DEFAULT = "0x4000" + +def split_dtb_bin(i : str): + return i.split(MANIFEST_IMAGE_SPLITTER) + +def align_to_page(n): + return HF_PAGE_SIZE * \ + (round(n / HF_PAGE_SIZE) + \ + (1 if n % HF_PAGE_SIZE else 0)) + +def to_bytes(value): + return int(value).to_bytes(HEADER_ELEMENT_BYTES, 'little') + +class SpPkg: + def __init__(self, pm_path : str, img_path : str, pm_offset: int, + img_offset: int): + if not os.path.isfile(pm_path) or not os.path.isfile(img_path): + raise Exception(f"Parameters should be path. \ + manifest: {pm_path}; img: {img_path}") + self.pm_path = pm_path + self.img_path = img_path + self._SpPkgHeader = namedtuple("SpPkgHeader", + ("magic", "version", + "pm_offset", "pm_size", + "img_offset", "img_size")) + + if pm_offset >= img_offset: + raise ValueError("pm_offset must be smaller than img_offset") + + is_hfpage_aligned = lambda val : val % HF_PAGE_SIZE == 0 + if not is_hfpage_aligned(pm_offset) or not is_hfpage_aligned(img_offset): + raise ValueError(f"Offsets provided need to be page aligned: pm-{pm_offset}, img-{img_offset}") + + if img_offset - pm_offset < self.pm_size: + raise ValueError(f"pm_offset and img_offset do not fit the specified file:{pm_path})") + + self.pm_offset = pm_offset + self.img_offset = img_offset + + def __str__(self): + return \ + f'''--SP package Info-- + header:{self.header} + pm: {self.pm_path} + img: {self.img_path} + ''' + + @property + def magic(self): + return "SPKG".encode() + + @property + def version(self): + return 0x2 + + @property + def pm_size(self): + return os.path.getsize(self.pm_path) + + @property + def img_size(self): + return os.path.getsize(self.img_path) + + @property + def header(self): + return self._SpPkgHeader( + self.magic, + self.version, + self.pm_offset, + self.pm_size, + self.img_offset, + self.img_size) + + @property + def header_size(self): + return len(self._SpPkgHeader._fields) + + def generate(self, f_out : str): + with open(f_out, "wb+") as output: + for h in self.header: + to_write = h if type(h) is bytes else to_bytes(h) + output.write(to_write) + output.seek(self.pm_offset) + with open(self.pm_path, "rb") as pm: + copyfileobj(pm, output) + output.seek(self.img_offset) + with open(self.img_path, "rb") as img: + copyfileobj(img, output) + +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument("-i", required=True, + help="path to partition's image and manifest separated by a colon.") + parser.add_argument("--pm-offset", required=False, default=PM_OFFSET_DEFAULT, + help="set partitition manifest offset.") + parser.add_argument("--img-offset", required=False, default=IMG_OFFSET_DEFAULT, + help="set partition image offset.") + parser.add_argument("-o", required=True, help="set output file path.") + parser.add_argument("-v", required=False, action="store_true", + help="print package information.") + args = parser.parse_args() + + if not os.path.exists(os.path.dirname(args.o)): + raise Exception("Provide a valid output file path!\n") + + image_path, manifest_path = split_dtb_bin(args.i) + pm_offset = int(args.pm_offset, 0) + img_offset = int(args.img_offset, 0) + pkg = SpPkg(manifest_path, image_path, pm_offset, img_offset) + pkg.generate(args.o) + + if args.v is True: + print(pkg) + + return 0 + +if __name__ == "__main__": + sys.exit(Main()) diff --git a/tools/stm32image/Makefile b/tools/stm32image/Makefile new file mode 100644 index 0000000..9c9b7b5 --- /dev/null +++ b/tools/stm32image/Makefile @@ -0,0 +1,49 @@ +# +# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk + +PROJECT := stm32image${BIN_EXT} +OBJECTS := stm32image.o +V := 0 + +HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 -D_GNU_SOURCE + +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG +else + HOSTCCFLAGS += -O2 +endif + +ifeq (${V},0) + Q := @ +else + Q := +endif + +HOSTCC := gcc + +.PHONY: all clean distclean + +all: ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + @echo " HOSTLD $@" + ${Q}${HOSTCC} ${OBJECTS} -o $@ + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} + +%.o: %.c Makefile + @echo " HOSTCC $<" + ${Q}${HOSTCC} -c ${HOSTCCFLAGS} $< -o $@ + +clean: + $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS}) + +distclean: clean diff --git a/tools/stm32image/stm32image.c b/tools/stm32image/stm32image.c new file mode 100644 index 0000000..bd4720c --- /dev/null +++ b/tools/stm32image/stm32image.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <asm/byteorder.h> +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +/* Magic = 'S' 'T' 'M' 0x32 */ +#define HEADER_MAGIC __be32_to_cpu(0x53544D32) +#define VER_MAJOR 2 +#define VER_MINOR 1 +#define VER_VARIANT 0 +#define HEADER_VERSION_V1 0x1 +#define HEADER_VERSION_V2 0x2 +#define PADDING_HEADER_MAGIC __be32_to_cpu(0x5354FFFF) +#define PADDING_HEADER_FLAG (1 << 31) +#define PADDING_HEADER_LENGTH 0x180 + +struct stm32_header_v1 { + uint32_t magic_number; + uint8_t image_signature[64]; + uint32_t image_checksum; + uint8_t header_version[4]; + uint32_t image_length; + uint32_t image_entry_point; + uint32_t reserved1; + uint32_t load_address; + uint32_t reserved2; + uint32_t version_number; + uint32_t option_flags; + uint32_t ecdsa_algorithm; + uint8_t ecdsa_public_key[64]; + uint8_t padding[83]; + uint8_t binary_type; +}; + +struct stm32_header_v2 { + uint32_t magic_number; + uint8_t image_signature[64]; + uint32_t image_checksum; + uint8_t header_version[4]; + uint32_t image_length; + uint32_t image_entry_point; + uint32_t reserved1; + uint32_t load_address; + uint32_t reserved2; + uint32_t version_number; + uint32_t extension_flags; + uint32_t extension_headers_length; + uint32_t binary_type; + uint8_t padding[16]; + uint32_t extension_header_type; + uint32_t extension_header_length; + uint8_t extension_padding[376]; +}; + +static void stm32image_default_header(void *ptr) +{ + struct stm32_header_v1 *header = (struct stm32_header_v1 *)ptr; + + if (!header) { + return; + } + + header->magic_number = HEADER_MAGIC; + header->version_number = __cpu_to_le32(0); +} + +static uint32_t stm32image_checksum(void *start, uint32_t len, + uint32_t header_size) +{ + uint32_t csum = 0; + uint8_t *p; + + if (len < header_size) { + return 0; + } + + p = (unsigned char *)start + header_size; + len -= header_size; + + while (len > 0) { + csum += *p; + p++; + len--; + } + + return csum; +} + +static void stm32image_print_header(const void *ptr) +{ + struct stm32_header_v1 *stm32hdr = (struct stm32_header_v1 *)ptr; + struct stm32_header_v2 *stm32hdr_v2 = (struct stm32_header_v2 *)ptr; + + printf("Image Type : ST Microelectronics STM32 V%d.%d\n", + stm32hdr->header_version[VER_MAJOR], + stm32hdr->header_version[VER_MINOR]); + printf("Image Size : %lu bytes\n", + (unsigned long)__le32_to_cpu(stm32hdr->image_length)); + printf("Image Load : 0x%08x\n", + __le32_to_cpu(stm32hdr->load_address)); + printf("Entry Point : 0x%08x\n", + __le32_to_cpu(stm32hdr->image_entry_point)); + printf("Checksum : 0x%08x\n", + __le32_to_cpu(stm32hdr->image_checksum)); + + switch (stm32hdr->header_version[VER_MAJOR]) { + case HEADER_VERSION_V1: + printf("Option : 0x%08x\n", + __le32_to_cpu(stm32hdr->option_flags)); + break; + + case HEADER_VERSION_V2: + printf("Extension : 0x%08x\n", + __le32_to_cpu(stm32hdr_v2->extension_flags)); + break; + + default: + printf("Incorrect header version\n"); + } + + printf("Version : 0x%08x\n", + __le32_to_cpu(stm32hdr->version_number)); +} + +static int stm32image_set_header(void *ptr, struct stat *sbuf, int ifd, + uint32_t loadaddr, uint32_t ep, uint32_t ver, + uint32_t major, uint32_t minor, + uint32_t binary_type, uint32_t header_size) +{ + struct stm32_header_v1 *stm32hdr = (struct stm32_header_v1 *)ptr; + struct stm32_header_v2 *stm32hdr_v2 = (struct stm32_header_v2 *)ptr; + uint32_t ext_size = 0U; + uint32_t ext_flags = 0U; + + stm32image_default_header(ptr); + + stm32hdr->header_version[VER_MAJOR] = major; + stm32hdr->header_version[VER_MINOR] = minor; + stm32hdr->load_address = __cpu_to_le32(loadaddr); + stm32hdr->image_entry_point = __cpu_to_le32(ep); + stm32hdr->image_length = __cpu_to_le32((uint32_t)sbuf->st_size - + header_size); + stm32hdr->image_checksum = + __cpu_to_le32(stm32image_checksum(ptr, sbuf->st_size, + header_size)); + + switch (stm32hdr->header_version[VER_MAJOR]) { + case HEADER_VERSION_V1: + /* Default option for header v1 : bit0 => no signature */ + stm32hdr->option_flags = __cpu_to_le32(0x00000001); + stm32hdr->ecdsa_algorithm = __cpu_to_le32(1); + stm32hdr->binary_type = (uint8_t)binary_type; + break; + + case HEADER_VERSION_V2: + stm32hdr_v2->binary_type = binary_type; + ext_size += PADDING_HEADER_LENGTH; + ext_flags |= PADDING_HEADER_FLAG; + stm32hdr_v2->extension_flags = + __cpu_to_le32(ext_flags); + stm32hdr_v2->extension_headers_length = + __cpu_to_le32(ext_size); + stm32hdr_v2->extension_header_type = PADDING_HEADER_MAGIC; + stm32hdr_v2->extension_header_length = + __cpu_to_le32(PADDING_HEADER_LENGTH); + break; + + default: + return -1; + } + + stm32hdr->version_number = __cpu_to_le32(ver); + + return 0; +} + +static int stm32image_create_header_file(char *srcname, char *destname, + uint32_t loadaddr, uint32_t entry, + uint32_t version, uint32_t major, + uint32_t minor, uint32_t binary_type) +{ + int src_fd, dest_fd, header_size; + struct stat sbuf; + unsigned char *ptr; + void *stm32image_header; + + dest_fd = open(destname, O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0666); + if (dest_fd == -1) { + fprintf(stderr, "Can't open %s: %s\n", destname, + strerror(errno)); + return -1; + } + + src_fd = open(srcname, O_RDONLY); + if (src_fd == -1) { + fprintf(stderr, "Can't open %s: %s\n", srcname, + strerror(errno)); + return -1; + } + + if (fstat(src_fd, &sbuf) < 0) { + return -1; + } + + ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, src_fd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s\n", srcname); + return -1; + } + + switch (major) { + case HEADER_VERSION_V1: + stm32image_header = malloc(sizeof(struct stm32_header_v1)); + header_size = sizeof(struct stm32_header_v1); + break; + + case HEADER_VERSION_V2: + stm32image_header = malloc(sizeof(struct stm32_header_v2)); + header_size = sizeof(struct stm32_header_v2); + break; + + default: + return -1; + } + + memset(stm32image_header, 0, header_size); + if (write(dest_fd, stm32image_header, header_size) != + header_size) { + fprintf(stderr, "Write error %s: %s\n", destname, + strerror(errno)); + free(stm32image_header); + return -1; + } + + free(stm32image_header); + + if (write(dest_fd, ptr, sbuf.st_size) != sbuf.st_size) { + fprintf(stderr, "Write error on %s: %s\n", destname, + strerror(errno)); + return -1; + } + + munmap((void *)ptr, sbuf.st_size); + close(src_fd); + + if (fstat(dest_fd, &sbuf) < 0) { + return -1; + } + + ptr = mmap(0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, + dest_fd, 0); + + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't write %s\n", destname); + return -1; + } + + if (stm32image_set_header(ptr, &sbuf, dest_fd, loadaddr, + entry, version, major, minor, + binary_type, header_size) != 0) { + return -1; + } + + stm32image_print_header(ptr); + + munmap((void *)ptr, sbuf.st_size); + close(dest_fd); + return 0; +} + +int main(int argc, char *argv[]) +{ + int opt; + int loadaddr = -1; + int entry = -1; + int err = 0; + int version = 0; + int binary_type = -1; + int major = HEADER_VERSION_V2; + int minor = 0; + char *dest = NULL; + char *src = NULL; + + while ((opt = getopt(argc, argv, ":b:s:d:l:e:v:m:n:")) != -1) { + switch (opt) { + case 'b': + binary_type = strtol(optarg, NULL, 0); + break; + case 's': + src = optarg; + break; + case 'd': + dest = optarg; + break; + case 'l': + loadaddr = strtol(optarg, NULL, 0); + break; + case 'e': + entry = strtol(optarg, NULL, 0); + break; + case 'v': + version = strtol(optarg, NULL, 0); + break; + case 'm': + major = strtol(optarg, NULL, 0); + break; + case 'n': + minor = strtol(optarg, NULL, 0); + break; + default: + fprintf(stderr, + "Usage : %s [-s srcfile] [-d destfile] [-l loadaddr] [-e entry_point] [-m major] [-n minor] [-b binary_type]\n", + argv[0]); + return -1; + } + } + + if (!src) { + fprintf(stderr, "Missing -s option\n"); + return -1; + } + + if (!dest) { + fprintf(stderr, "Missing -d option\n"); + return -1; + } + + if (loadaddr == -1) { + fprintf(stderr, "Missing -l option\n"); + return -1; + } + + if (entry == -1) { + fprintf(stderr, "Missing -e option\n"); + return -1; + } + + if (binary_type == -1) { + fprintf(stderr, "Missing -b option\n"); + return -1; + } + + err = stm32image_create_header_file(src, dest, loadaddr, + entry, version, major, minor, + binary_type); + + return err; +} |