diff options
| author | Jaymes Wilks <mjwilks@us.ibm.com> | 2016-10-17 12:15:40 -0500 |
|---|---|---|
| committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2016-11-14 17:17:33 -0500 |
| commit | 16263a641c48773091dd60b55e28ad77ca5a8574 (patch) | |
| tree | 97120f76deb4132a1a1b7ceba8701318c5663a68 /src | |
| parent | a904e156364a8f0fd5f6bc2b7094f79cf77da1b2 (diff) | |
| download | blackbird-hostboot-16263a641c48773091dd60b55e28ad77ca5a8574.tar.gz blackbird-hostboot-16263a641c48773091dd60b55e28ad77ca5a8574.zip | |
Secure PNOR Resource Provider port from p8
Adds a Secure PNOR Resource Provider (SPNORRP) layer on top of the
original PNORRP to handle verification of secured PNOR sections.
Change-Id: Iff25abf599f3c850197c6e6d23ff03e5edf945bb
RTC:163078
Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/31588
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Stephen M. Cprek <smcprek@us.ibm.com>
Reviewed-by: Michael Baiocchi <mbaiocch@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
26 files changed, 2528 insertions, 167 deletions
diff --git a/src/include/usr/pnor/pnor_const.H b/src/include/usr/pnor/pnor_const.H index 9b65876fd..ddc4d19d5 100644 --- a/src/include/usr/pnor/pnor_const.H +++ b/src/include/usr/pnor/pnor_const.H @@ -27,6 +27,7 @@ #include <stdint.h> #include <builtins.h> +#include <config.h> namespace PNOR { @@ -67,7 +68,6 @@ enum SectionId RINGOVD, /**< Ring override data */ NUM_SECTIONS, /**< Number of defined sections */ - FIRST_SECTION = TOC, /**< First section (for looping) */ /**< Used for error cases, initialization */ INVALID_SECTION = NUM_SECTIONS, @@ -79,6 +79,7 @@ enum SectionId // Size and layout of this structure must be maintained for debug framework. struct SectionInfo_t { + SectionInfo_t(): id(INVALID_SECTION) {} SectionId id; /**< Identifier for this section */ const char* name; /**< Name of the section */ uint64_t vaddr; /**< Virtual address of the start of the section */ @@ -88,6 +89,11 @@ struct SectionInfo_t bool sha512Version; /**< Version Checking */ bool sha512perEC; /**< Version Checking perEC */ bool readOnly; /**< Section is read only */ +#ifdef CONFIG_SECUREBOOT + size_t secureProtectedPayloadSize; /**< Cache the secure payload size so + that the secure container only + needs to be parsed once */ +#endif }; /** diff --git a/src/include/usr/pnor/pnor_reasoncodes.H b/src/include/usr/pnor/pnor_reasoncodes.H index 4235339bd..b7fd96bee 100644 --- a/src/include/usr/pnor/pnor_reasoncodes.H +++ b/src/include/usr/pnor/pnor_reasoncodes.H @@ -96,6 +96,18 @@ namespace PNOR // pnor_common.C MOD_PNORCOMMON_PARSETOC = 0xC0, /**< PNOR::parseTOC */ + + // spnorrp.C + // Note: 0xD0 is available, so should be the next one used for spnorrp. + // Remove this comment after doing so. + MOD_SPNORRP_DIDSTARTUPFAIL = 0xD1, /**< didSecureStartupFail(rc) */ + MOD_SPNORRP_ALLOCATE_BLOCK = 0xD2, /**< SPnorRP::initDaemon */ + MOD_SPNORRP_WAITFORMESSAGE = 0xD3, /**< SPnorRP::waitForMessage */ + MOD_SPNORRP_VERIFYSECTIONS = 0xD4, /**< SPnorRP::verifySections */ + MOD_SPNORRP_SET_PERMISSION = 0xD5, /**< SPnorRP::initDaemon */ + MOD_PNORRP_LOADSECURESECTION = 0xD6, /**< PnorRP::loadSecureSection */ + MOD_SPNORRP_BASE_EXT_VER_CHK = 0xD7, /**< SPnorRP::baseExtVersCheck */ + MOD_SPNORRP_KEY_TRAN_CHK = 0xD8, /**< SPnorRP::keyTransitionCheck */ }; enum PNORReasonCode @@ -157,6 +169,10 @@ namespace PNOR RC_TOC_HDR_CHECKSUM_ERR = PNOR_COMP_ID | 0x2C, RC_PNOR_PARSE_ENTRIES_ERR = PNOR_COMP_ID | 0x2D, RC_PNOR_SET_VADDR_FAILED = PNOR_COMP_ID | 0x2E, + RC_BASE_EXT_MISMATCH = PNOR_COMP_ID | 0x2F, + RC_KEY_TRAN_FLAG_UNSET = PNOR_COMP_ID | 0x30, + RC_BAD_SECURE_MAGIC_NUM = PNOR_COMP_ID | 0x31, + //@fixme-RTC:131607-Temporary value to allow HWSV compile //termination_rc diff --git a/src/include/usr/pnor/pnorif.H b/src/include/usr/pnor/pnorif.H index f5e4fc385..311c6c4b4 100644 --- a/src/include/usr/pnor/pnorif.H +++ b/src/include/usr/pnor/pnorif.H @@ -81,6 +81,7 @@ errlHndl_t getSideInfo (SideId i_side, SideInfo_t& o_info); errlHndl_t getSectionInfo( SectionId i_section, SectionInfo_t& o_info ); +#ifdef CONFIG_SECUREBOOT /** * @brief Loads requested PNOR section to secure virtual address space * @@ -112,6 +113,48 @@ errlHndl_t loadSecureSection(SectionId i_section); errlHndl_t unloadSecureSection(SectionId i_section); /** + * @brief Memcmp a vaddr to the known secureboot magic number + * + * @param[in] i_vaddr: vaddr of secureboot header to check for magic number + * Note: must point to a buffer of size >= 4 bytes + * + * @return bool - True if the magic number and starting bytes of the vaddr + * match. False otherwise. + */ +bool cmpSecurebootMagicNumber(const uint8_t* i_vaddr); + +/** + * @brief Returns true if a PNOR section has the secureboot container + * header magic number at the beginning. This is mainly used to + * ignore unwanted PNOR sections like secureboot key transition. + * It indicates the section has valid content to be securely + * loaded, otherwise the section content will not be loaded. + * If a section does not have the header but needs to be loaded, + * it will fail ROM verify later on anyhow. + * Note: Does not work with HBB section and will assert if attempted + * + * @param[in] i_section: PNOR section to check first bytes of. + * @param[out] o_valid: true if section has the correct magic number at + * the beginning + * + * @return errlHndl_t - NULL if success, errlHndl_t otherwise. + * */ +errlHndl_t hasSecurebootMagicNumber(SectionId i_section, bool &o_valid); +#endif // CONFIG_SECUREBOOT + + +/** + * @brief Determines whether the given section is inhibited by secure boot + * for containing attribute overrides. + * + * @param[in] i_section PNOR section to test. + * + * @return bool True if inhibited section, false otherwise. + */ +bool isInhibitedSection(const uint32_t i_section); + + +/** * @brief Write the data back from hostboot memory to PNOR of a given section * of PNOR * diff --git a/src/include/usr/secureboot/containerheader.H b/src/include/usr/secureboot/containerheader.H new file mode 100644 index 000000000..c5188e629 --- /dev/null +++ b/src/include/usr/secureboot/containerheader.H @@ -0,0 +1,242 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/usr/secureboot/containerheader.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SECUREBOOT_CONTAINER_HEADER_H +#define __SECUREBOOT_CONTAINER_HEADER_H + +#include <errl/errlentry.H> +#include <secureboot/service.H> +#include <secureboot/rom.H> + +// Forward Declaration +class SecureROMTest; + +namespace SECUREBOOT +{ + +/** @class ContainerHeader + * @brief Class for parsing secureboot container headers. + */ +class ContainerHeader +{ + public: + + /** + * @brief ContainerHeader + * + * This constructor parses the input container header and sets values + * accordingly so they can be retrieved later. + * + * @param[in] i_header Secure container header to parse. + * NULL input will assert + */ + ContainerHeader(const void* i_header): + iv_isValid(false),iv_hdrBytesRead(0) + { + assert(i_header != NULL); + iv_pHdrStart = reinterpret_cast<const uint8_t*>(i_header); + memset(&iv_headerInfo, 0x00, sizeof(iv_headerInfo)); + memset(iv_hwKeyHash, 0, sizeof(SHA512_t)); + parse_header(i_header); + }; + + /** + * @brief Destructor + */ + ~ContainerHeader(){}; + + /** + * @brief Retrieves total container size (includes header, payload text, + * and payload data sizes) + * @return size_t - Total container size in bytes + */ + size_t totalContainerSize() const; + + /** + * @brief Retrieves pointer to first hw key + * @return ecc_key_t* - ptr to first hw key + */ + const ecc_key_t* hw_keys() const; + + /** + * @brief Total size of all hw keys concatenated + */ + static const size_t totalHwKeysSize = HW_KEY_COUNT*sizeof(ecc_key_t); + + /** + * @brief Retrieves payload text size + * @return size_t - size of payload text size + */ + size_t payloadTextSize() const; + + /** + * @brief Retrieves payload text hash + * @return SHA512_t* - ptr to hash of payload text + */ + const SHA512_t* payloadTextHash() const; + + /** + * @brief Retrieves total size of all sw keys concatenated + * @return size_t - size of concatenated sw keys + */ + size_t totalSwKeysSize() const; + + /** + * @brief Retrieves sw public key hash + * @return SHA512_t* - ptr to hash of sw public keys + */ + const SHA512_t* swKeyHash() const; + + /** + * @brief Retrieves pointer to first sw key + * @return ecc_key_t* - ptr to first sw key + */ + const ecc_key_t* sw_keys() const; + + /** + * @brief Retrieves pointer to first sw signature + * @return ecc_key_t* - ptr to first sw signature + */ + const ecc_key_t* sw_sigs() const; + + /** + * @brief Retrieves pointer to sb flag struct holding all hw and sw + * flags set when parsing the header. + * @return sb_flags_t - hw and sw flag struct + */ + const sb_flags_t* sb_flags() const; + + /** + * @brief Retrieves hw public key hash + * @return SHA512_t* - ptr to hash of hw public keys + */ + const SHA512_t* hwKeyHash() const; + + /** + * @brief Returns if the parsed header is a valid secureboot one. This + * is a temporary, non-secure way of pragmatically determining + * if secureboot signing was supported. Eventually it will always + * happen + * @return bool - whether or not the container is a valid secureboot + */ + bool isValid() const; + + private: + /** + * @brief Default Constructor in private to prevent being instantiated + * by non friend/children derivatives. + */ + ContainerHeader(){}; + + /** + * @brief Complete container header structure based on ROM structures + */ + struct SecureHeaderInfo + { + ROM_container_raw hw_hdr; + ROM_prefix_header_raw hw_prefix_hdr; + ROM_prefix_data_raw hw_prefix_data; + ROM_sw_header_raw sw_hdr; + ROM_sw_sig_raw sw_sig; + }; + + // Entire cached container header content + SecureHeaderInfo iv_headerInfo; + + // Indicates if container header is a valid, in a very loose sense, + // secureboot header. + bool iv_isValid; + + // Pointer to the start of the container header + const uint8_t* iv_pHdrStart; + + // Counter for bytes read while parsing the container header + size_t iv_hdrBytesRead; + + // Total size of all software keys concatenated + size_t iv_totalSwKeysSize; + + // Struct to hold all hw and sw flags set + sb_flags_t iv_sbFlags; + + // HW keys' hash for current container. + SHA512_t iv_hwKeyHash; + + /** + * @brief Determines what flags are set based on the hw and sw flag bit + * fields in the container header. + * Also sets iv_sbFlags private member + */ + void parseFlags(); + + /** + * @brief Generate and store hw key hash. Concatenate all hw public keys + * and then take sha512 hash. + * Also sets iv_hwKeyHash private member + */ + void genHwKeyHash(); + + /** + * @brief Weak check to determine if secureboot header looks right. + * Also sets iv_isValid private member + */ + void validate(); + + /** + * @brief Print out useful sections of the container header + */ + void print() const; + + /** + * @brief parse_header Blob + * + * Parses a secure container header defined by ROM structures and set + * internal header structure. + * + * @param[in] i_containerHdr Secure container header to parse + * NULL input will assert + */ + void parse_header(const void* i_header); + + /** + * @brief Checks bounds of parsing before mempy and increments pointer + * + * Ensures that we don't memcpy more bytes than the max size of a + * secure container header. Asserts on out of bounds memcpy. + * + * @param[in] i_dest Pointer to the memory location to copy to + * NULL input will assert + * @param[in] io_hdr Pointer to current location of container header + * NULL input will assert + * @param[in] i_size Number of bytes to copy + */ + void safeMemCpyAndInc(void* i_dest, const uint8_t* &io_hdr, + const size_t i_size); + + friend class ::SecureROMTest; +}; + +}; //end of SECUREBOOT namespace + +#endif diff --git a/src/include/usr/secureboot/header.H b/src/include/usr/secureboot/header.H new file mode 100644 index 000000000..f7a5121c6 --- /dev/null +++ b/src/include/usr/secureboot/header.H @@ -0,0 +1,144 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/usr/secureboot/header.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2013,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SECUREBOOT_HEADER_H +#define __SECUREBOOT_HEADER_H + +#include <stdint.h> +#include <pnor/pnorif.H> +#include <util/singleton.H> + +/** @file header.H + * + * @brief Class for manipulating the base image (HBB) secureboot header. + */ +namespace SECUREBOOT +{ + /** @class Header + * @brief Class for storing the base image (HBB) header for later use. + */ + class Header + { + public: + /** + * @brief Build a base image (HBB) header object + */ + Header() + : iv_data(NULL) + { + } + + /** + * @brief Destroy a base image (HBB) header object, including any + * cached header page + */ + ~Header() + { + free(iv_data); + iv_data=NULL; + } + + // TODO securebootp9 This is from p9 code. See the corresponding + // comment in header.C for more info. + /** @brief Extract header from original HRMOR - 1 page address. */ + void loadBaseHeader(); + + /** + * @brief Extracts base image (HBB) header (ECC removed) from + * HBB secure load address (HRMOR - 4k) to support extending + * HBB measurements to TPM in secure mode. + * + * @warning Asserts if header is already cached (code bug) + */ + void loadSecurely(); + + /** + * @brief Caches non-secure PNOR copy of the base image (HBB) + * header (ECC removed) to support extending HBB measurements + * to TPM in non-secure mode. + * + * @param[in] i_pHeader Pointer to non-secure 4k HBB header + * extracted from PNOR. + * + * @warning Asserts if input pointer is NULL (code bug) + * @warning Asserts if header already cached (code bug) + * @warning Memory violation if buffer data is less than 4k in size + * (code bug) + * @warning Ignores buffer data beyond 4k in size + */ + void setNonSecurely( + const void* i_pHeader); + + /** + * @brief Return pointer to base image (HBB) header. + * + * @par Detailed Description: + * When SBE first loads Hostboot, if system is in secure mode, + * it copies the HBB code to the HRMOR address (aka the secure + * load address) and puts the HBB header 4k in front of it. In + * non-secure mode, SBE only loads the HBB code to the HRMOR and + * discards the header, leaving no trace of it in memory. When + * HBB gets control, if in secure mode, it copies its own header + * from HRMOR-4k and caches it in this object. Otherwise, if + * not in secure mode, it pulls the header from PNOR and writes + * it into this object. This API then returns the addresses of + * the cached header. + * + * @param[out] o_pHeader Pointer to HBB header + * + * @warning Asserts if HBB header not loaded (code bug) + */ + void getHeader( + const void*& o_pHeader) const; + + private: + + /** + * @brief Returns base (HBB) image secure load address (the address + * where SBE -always- loads hostboot regardless of security + * state) + * + * @param[out] o_pCode Base (HBB) image secure load address + */ + void _calcSecureLoadAddr( + const void*& o_pCode) const; + + // Pointer to copy of the base image's (HBB's) secureboot header + void* iv_data; + + // Don't allow copies / assignments + Header(const Header& that); + Header& operator=(const Header&); + }; + + /** + * @brief Returns the base image (HBB) header singleton + * + * @return Header Reference to base image (HBB) header object + */ + Header& baseHeader(); + +}; + +#endif diff --git a/src/include/usr/secureboot/rom.H b/src/include/usr/secureboot/rom.H new file mode 100644 index 000000000..45f9c5dd4 --- /dev/null +++ b/src/include/usr/secureboot/rom.H @@ -0,0 +1,195 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/usr/secureboot/rom.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SECUREBOOT_ROM_H +#define __SECUREBOOT_ROM_H + +// Consts used for container header validation +const uint32_t MAGIC_NUMBER = 0x17082011; +const uint16_t ROM_VERSION = 1; +const uint8_t ROM_HASH_ALG = 1; +const uint8_t ROM_SIG_ALG = 1; +const uint8_t HW_KEY_COUNT = 3; +const uint8_t SW_KEY_COUNT_MIN = 1; +const uint8_t SW_KEY_COUNT_MAX = 3; +const size_t MAX_SECURE_HEADER_SIZE = 4096; + +// Security Flags + +// HW Security Flags +enum HW_SB_FLAGS +{ + HB_FW_FLAG = 0x80000000, + OPAL_FLAG = 0x40000000, + PHYP_FLAG = 0x20000000, + KEY_TRANSITION_FLAG = 0x00000001 +}; + +// SW Security Flags +enum SW_SB_FLAGS +{ + // placeholder +}; + +// Structure to store all hw and sw flag values in a container header +struct sb_flags_t +{ + sb_flags_t() : hw_hb_fw(false), hw_opal(false), hw_phyp(false), + hw_key_transition(false) {} + bool hw_hb_fw; + bool hw_opal; + bool hw_phyp; + bool hw_key_transition; +}; + +/******************************************************************/ +/* Start of Chip Logic Secure ROM include section */ +/******************************************************************/ +// These defines come from the following directory: +// /afs/awd/projects/eclipz/c22/libs/tp/logic/p8m/head/trusted_boot_rom/src + +/* From hw_utils.h: */ +#define ECID_SIZE 16 +#define PIBMEM 0x00080000 +#define PIBMEM_HW_KEY_HASH (PIBMEM +0x0008) + +/* From ecverify.h */ +#define EC_COORDBYTES 66 /* P-521 */ +typedef uint8_t ecc_key_t[2*EC_COORDBYTES]; +typedef uint8_t ecc_signature_t[2*EC_COORDBYTES]; + +/* From sha512.h: */ +#define SHA512_DIGEST_LENGTH 64 +typedef uint8_t __attribute__((aligned(8))) sha2_hash_t[ \ + SHA512_DIGEST_LENGTH / sizeof(uint8_t) ]; + +typedef uint8_t sha2_byte; /* Exactly 1 byte */ + +/* From ROM.h */ +typedef enum { ROM_DONE, ROM_FAILED, PHYP_PARTIAL } ROM_response; + +/* From ROM.h */ +typedef struct { + uint16_t version; // (1: see versions above) + uint8_t hash_alg; // (1: SHA-512) + uint8_t sig_alg; // (1: SHA-512/ECDSA-521) +}__attribute__((packed)) ROM_version_raw; + +typedef struct { + uint32_t magic_number; // (17082011) + uint16_t version; // (1: see versions above) + uint64_t container_size; // filled by caller + uint64_t target_hrmor; // filled by caller + uint64_t stack_pointer; // filled by caller + //bottom of stack -> 128k added by rom code to get real stack pointer + ecc_key_t hw_pkey_a; + ecc_key_t hw_pkey_b; + ecc_key_t hw_pkey_c; + uint64_t prefix; // prefix header place holder + // followed by sw header (if not special prefix) + // followed by optional unprotected payload data +}__attribute__((packed)) ROM_container_raw; + +typedef struct { + ROM_version_raw ver_alg; + uint64_t code_start_offset; + uint64_t reserved; + uint32_t flags; + uint8_t sw_key_count; + uint64_t payload_size; + sha2_hash_t payload_hash; + uint8_t ecid_count; + uint8_t ecid[ECID_SIZE]; // optional ecid place holder ecid_count * ecid_size(128 bits) + // followed by prefix data (sig,keys) key raw +}__attribute__((packed)) ROM_prefix_header_raw; + +#define PREFIX_HEADER_SIZE(_p) (sizeof(ROM_prefix_header_raw)+((_p->ecid_count-1)*ECID_SIZE)) + +typedef struct { + ecc_signature_t hw_sig_a; + ecc_signature_t hw_sig_b; + ecc_signature_t hw_sig_c; + ecc_key_t sw_pkey_p; + ecc_key_t sw_pkey_q; + ecc_key_t sw_pkey_r; +}__attribute__((packed)) ROM_prefix_data_raw; + +typedef struct { + ROM_version_raw ver_alg; + uint64_t code_start_offset; + uint64_t reserved; + uint32_t flags; + uint8_t reserved_0; + uint64_t payload_size; + sha2_hash_t payload_hash; + uint8_t ecid_count; + uint8_t ecid[ECID_SIZE]; // optional ecid place holder ecid_count * ecid_size(128 bits) + // followed by sw sig raw +}__attribute__((packed)) ROM_sw_header_raw; + +#define SW_HEADER_SIZE(_p) (sizeof(ROM_sw_header_raw)+((_p->ecid_count-1)*ECID_SIZE)) + +typedef struct { + ecc_signature_t sw_sig_p; + ecc_signature_t sw_sig_q; + ecc_signature_t sw_sig_r; + // followed by zero's padding to 4K + // followed by protected sw payload_data + // followed by unprotected sw payload_text +}__attribute__((packed)) ROM_sw_sig_raw; + +typedef struct { + sha2_hash_t hw_key_hash; + uint8_t my_ecid[ECID_SIZE]; + uint64_t entry_point; + uint64_t log; +}__attribute__((packed)) ROM_hw_params; + +// Need this for the following definition +#ifdef __cplusplus +extern "C" +{ +#endif + +// Interfaces for Assembly Functions to call into Secure ROM +// - 1st parameter is address of function offset into Secure ROM, +// followed by additional parameters as necssary + +ROM_response call_rom_verify(void*, ROM_container_raw*, ROM_hw_params*); +void call_rom_SHA512(void*, const sha2_byte *, size_t, sha2_hash_t*); + +#ifdef __cplusplus +} +#endif + +/* Offsets needed to call functions in jump table at start of */ +/* SecureROM code - see .../trusted_boot_rom/bootrom.dis */ +#define SHA512_HASH_FUNCTION_OFFSET 0x20 +#define ROM_VERIFY_FUNCTION_OFFSET 0x30 + +/******************************************************************/ +/* End of Chip Logic ROM include section */ +/******************************************************************/ + +#endif diff --git a/src/include/usr/secureboot/service.H b/src/include/usr/secureboot/service.H index 1cc518007..577b27284 100644 --- a/src/include/usr/secureboot/service.H +++ b/src/include/usr/secureboot/service.H @@ -33,8 +33,22 @@ typedef uint8_t SHA512_t[64]; typedef uint8_t __attribute__((aligned(8))) sha2_hash_t[ \ SHA512_DIGEST_LENGTH / sizeof(uint8_t) ]; +// TODO securebootp9 added for spnorrp.C - service.H needs many more updates +// in order to match the p8 version +const size_t HASH_PAGE_TABLE_ENTRY_SIZE = 32; +typedef uint8_t PAGE_TABLE_ENTRY_t[HASH_PAGE_TABLE_ENTRY_SIZE]; + + namespace SECUREBOOT { + // TODO securebootp9 - the two constants below were taken from master-p8 + // branch (version 2257b1) of service.H underneath secureboot. + // The p9 version of service.H needs many more updates in order to match + // the p8 version. + const uint64_t PROC_SECURITY_SWITCH_REGISTER = 0x00010005ull; + const uint64_t + PROC_SECURITY_SWITCH_TRUSTED_BOOT_MASK = 0x8000000000000000ull; + /** @brief Perform initialization of Secureboot for the Base image. * * - Copy secure header from original location. @@ -60,11 +74,13 @@ namespace SECUREBOOT * @brief Verify Signed Container * * @param[in] i_container Void pointer to effective address of container - * @param[in] i_size Size of container in bytes + * @param[in] i_hwKeyHash Custom hw keys' hash to test against + * [default = nullptr, use current hw hash key] * * @return errlHndl_t NULL on success */ - errlHndl_t verifyContainer(void * i_container, size_t i_size); + errlHndl_t verifyContainer(void * i_container, + const sha2_hash_t* i_hwKeyHash = nullptr); /** * @brief Hash Signed Blob @@ -75,7 +91,7 @@ namespace SECUREBOOT * * @return errlHndl_t NULL on success */ - errlHndl_t hashBlob(void * i_blob, size_t i_size, SHA512_t o_buf); + errlHndl_t hashBlob(const void * i_blob, size_t i_size, SHA512_t o_buf); /** * @brief Retrieve the internal hardware hash key from secure ROM @@ -84,6 +100,15 @@ namespace SECUREBOOT * hash to. */ void getHwHashKeys(sha2_hash_t o_hash); + + /** + * @brief Common secureboot handler for secureboot failures. + * Properly handles callouts etc. + * + * @return i_err - Error log to cascade through failure path. + */ + void handleSecurebootFailure(errlHndl_t &i_err); + } #endif diff --git a/src/include/usr/secureboot/trustedbootif.H b/src/include/usr/secureboot/trustedbootif.H index ae6d183af..eaef78a74 100644 --- a/src/include/usr/secureboot/trustedbootif.H +++ b/src/include/usr/secureboot/trustedbootif.H @@ -37,6 +37,8 @@ #include <i2c/tpmddif.H> #include <errl/errlentry.H> #include <list> +#include <pnor/pnorif.H> +#include <secureboot/containerheader.H> namespace TRUSTEDBOOT { @@ -160,6 +162,32 @@ namespace TRUSTEDBOOT */ bool enabled(); + /** + * @brief Wrapper around pcrExtend for measuring PNOR sections + * @param[in] i_conHdr Reference to ContainerHeader of a section + * @param[in] i_vaddr Pointer to a virtual address for the protected + * portion of the PNOR section. + * [Not used if SECUREBOOT::enabled()] + * @param[in] i_sec Section ID of PNOR section + * @return errlHndl_t NULL if successful, otherwise a pointer to the + * error log. + */ + errlHndl_t extendPnorSectionHash(const SECUREBOOT::ContainerHeader& i_conHdr, + const void* i_vaddr, + const PNOR::SectionId i_sec); + + /** + * + * @brief Extends the Hostboot base image to the TPM + * + * @warning No-op if trusted boot compiled out + * + * @return errHndl_t Error log pointer + * @retval NULL Successfully extended Hostboot base image to the TPM + * @retval !NULL Failed to extend Hostboot base image to TPM + * */ + errlHndl_t extendBaseImage(); + } // end TRUSTEDBOOT namespace diff --git a/src/include/usr/vmmconst.h b/src/include/usr/vmmconst.h index 676e54f5a..1c91ae326 100644 --- a/src/include/usr/vmmconst.h +++ b/src/include/usr/vmmconst.h @@ -87,6 +87,17 @@ /** PNOR Resource Provider is at 2GB */ #define VMM_VADDR_PNOR_RP (2 * GIGABYTE) +/** Temp PNOR Resource Provider space is at 5GB */ +#define VMM_VADDR_SPNOR_TEMP (5 * GIGABYTE) + +/** The delta between PNOR RP and temp space and + * the delta between temp space and Secure PNOR RP space is 3GB + */ +#define VMM_VADDR_SPNOR_DELTA (VMM_VADDR_SPNOR_TEMP - VMM_VADDR_PNOR_RP) + +/** Secure PNOR Resource Provider is at 8GB */ +#define VMM_VADDR_SPNOR_RP (VMM_VADDR_SPNOR_TEMP + VMM_VADDR_SPNOR_DELTA) + /** SBE Update process is at 3GB, uses 512KB */ #define VMM_VADDR_SBE_UPDATE (3 * GIGABYTE) #define VMM_SBE_UPDATE_SIZE (512 * KILOBYTE) @@ -104,9 +115,10 @@ /** Virtual memory block priorities */ enum BlockPriority { - PNOR_PRIORITY = 0, //No dependencies - VFS_PRIORITY = (PNOR_PRIORITY + 1), //Dependent on PNOR - ATTR_PRIORITY = (PNOR_PRIORITY + 1), //Dependent on PNOR + PNOR_PRIORITY = 0, //No dependencies + SPNOR_PRIORITY = (PNOR_PRIORITY + 1), //Dependent on PNOR + VFS_PRIORITY = (SPNOR_PRIORITY + 1), //Dependent on PNOR and SPNOR + ATTR_PRIORITY = (SPNOR_PRIORITY + 1), //Dependent on PNOR and SPNOR }; /** diff --git a/src/usr/pnor/makefile b/src/usr/pnor/makefile index c1ebe38f0..b2c261a4b 100644 --- a/src/usr/pnor/makefile +++ b/src/usr/pnor/makefile @@ -29,6 +29,7 @@ ROOTPATH = ../../.. MODULE = pnor OBJS += pnor_utils.o OBJS += pnorrp.o +OBJS += $(if $(CONFIG_SECUREBOOT),spnorrp.o) OBJS += pnordd.o OBJS += pnor_common.o OBJS += pnorvalid.o diff --git a/src/usr/pnor/pnor_common.C b/src/usr/pnor/pnor_common.C index c1ceab291..351386256 100644 --- a/src/usr/pnor/pnor_common.C +++ b/src/usr/pnor/pnor_common.C @@ -367,3 +367,16 @@ errlHndl_t PNOR::extendHash(uint64_t i_addr, size_t i_size, const char* i_name) return l_errhdl; } +bool PNOR::isInhibitedSection(const uint32_t i_section) +{ +// TODO securebootp9 for now we won't be inhibiting overrides +#ifdef CONFIG_SECUREBOOT +// return (i_section == ATTR_PERM || +// i_section == ATTR_TMP) && +// SECUREBOOT::enabled(); + return false; +#else + return false; +#endif +} + diff --git a/src/usr/pnor/pnor_utils.C b/src/usr/pnor/pnor_utils.C index e522ea942..ee3355347 100644 --- a/src/usr/pnor/pnor_utils.C +++ b/src/usr/pnor/pnor_utils.C @@ -337,8 +337,8 @@ void PNOR::parseEntries (ffs_hdr* i_ffs_hdr, ((io_TOC[secId].size * 8 ) / 9); } - // TODO RTC:96009 handle version header w/secureboot - if (io_TOC[secId].version == FFS_VERS_SHA512) + if (io_TOC[secId].version == FFS_VERS_SHA512 + && !PNOR::isSecureSection(secId)) { //increment flash addr for sha header if (io_TOC[secId].integrity == FFS_INTEG_ECC_PROTECT) @@ -349,11 +349,31 @@ void PNOR::parseEntries (ffs_hdr* i_ffs_hdr, { io_TOC[secId].flashAddr += PAGESIZE ; } + + // now that we've skipped the header + // adjust the size to reflect that io_TOC[secId].size -= PAGESIZE; } } // For TOC Entries } - +bool PNOR::isSecureSection(const uint32_t i_section) +{ +// TODO securebootp9 uncomment these sections as they become ready for +// inclusion in p9. Remove this comment after the last one. +#ifdef CONFIG_SECUREBOOT +// return i_section == HB_EXT_CODE || +// i_section == HB_DATA || +// i_section == SBE_IPL || +// i_section == CENTAUR_SBE || +// i_section == PAYLOAD || +// i_section == SBKT || +// i_section == OCC || +// i_section == HB_RUNTIME; + return false; +#else + return false; +#endif +} diff --git a/src/usr/pnor/pnor_utils.H b/src/usr/pnor/pnor_utils.H index ed0f7176b..75d85d77b 100644 --- a/src/usr/pnor/pnor_utils.H +++ b/src/usr/pnor/pnor_utils.H @@ -220,6 +220,15 @@ void parseEntries (ffs_hdr* i_ffs_hdr, SectionData_t* io_TOC, ffs_entry*& o_err_entry); +/** + * @brief Determines whether the given section is secured by secure boot + * + * @param[in] i_section PNOR section to test. + * + * @return bool True if secure section, false otherwise. + */ +bool isSecureSection(const uint32_t i_section); + } // End namespace PNOR diff --git a/src/usr/pnor/pnorrp.C b/src/usr/pnor/pnorrp.C index 0c1feea77..ab7c66f23 100644 --- a/src/usr/pnor/pnorrp.C +++ b/src/usr/pnor/pnorrp.C @@ -24,6 +24,7 @@ /* */ /* IBM_PROLOG_END_TAG */ #include "pnorrp.H" +#include "spnorrp.H" #include <pnor/pnor_reasoncodes.H> #include <initservice/taskargs.H> #include <sys/msg.h> @@ -44,10 +45,19 @@ #include <endian.h> #include <util/align.H> #include <config.h> +#include <pnor/pnorif.H> #include "pnor_common.H" #include <hwas/common/hwasCallout.H> #include <console/consoleif.H> +#ifdef CONFIG_SECUREBOOT +#include <secureboot/service.H> +#include <secureboot/containerheader.H> +//#include <secureboot/settings.H> TODO securebootp9 include settings.H +#include <secureboot/header.H> +#include <secureboot/trustedbootif.H> +#endif + extern trace_desc_t* g_trac_pnor; // Easy macro replace for unit testing @@ -90,29 +100,6 @@ errlHndl_t PNOR::getSectionInfo( PNOR::SectionId i_section, } /** - * @brief Loads requested PNOR section to secure virtual address space - */ -errlHndl_t PNOR::loadSecureSection(const SectionId i_section) -{ - //@TODO RTC 156118 - // Replace with call to secure provider to load the section - errlHndl_t pError=NULL; - return pError; -} - -/** - * @brief Flushes any applicable pending writes and unloads requested PNOR - * section from secure virtual address space - */ -errlHndl_t PNOR::unloadSecureSection(const SectionId i_section) -{ - //@TODO RTC 156118 - // Replace with call to secure provider to load the section - errlHndl_t pError=NULL; - return pError; -} - -/** * @brief Clear pnor section */ errlHndl_t PNOR::clearSection(PNOR::SectionId i_section) @@ -175,16 +162,21 @@ void PnorRP::init( errlHndl_t &io_rtaskRetErrl ) { TRACUCOMP(g_trac_pnor, "PnorRP::init> " ); uint64_t rc = 0; + uint64_t rcs = 0; // spnorrp return code errlHndl_t l_errl = NULL; - if( Singleton<PnorRP>::instance().didStartupFail(rc) ) + if( Singleton<PnorRP>::instance().didStartupFail(rc) +#ifdef CONFIG_SECUREBOOT + || Singleton<SPnorRP>::instance().didStartupFail(rcs) +#endif + ) { /*@ * @errortype ERRL_SEV_CRITICAL_SYS_TERM * @moduleid PNOR::MOD_PNORRP_DIDSTARTUPFAIL * @reasoncode PNOR::RC_BAD_STARTUP_RC - * @userdata1 return code - * @userdata2 0 + * @userdata1 return code pnorrp + * @userdata2 return code spnorrp * * @devdesc PNOR startup task returned an error. * @custdesc A problem occurred while accessing the boot flash. @@ -194,11 +186,22 @@ void PnorRP::init( errlHndl_t &io_rtaskRetErrl ) PNOR::MOD_PNORRP_DIDSTARTUPFAIL, PNOR::RC_BAD_STARTUP_RC, rc, - 0, + rcs, true /*Add HB SW Callout*/ ); l_errl->collectTrace(PNOR_COMP_NAME); } + else + { + // Extend base image (HBB) when Hostboot first starts. Since HBB is + // never re-loaded, inhibit extending this image in runtime code. + #ifndef __HOSTBOOT_RUNTIME + #ifdef CONFIG_SECUREBOOT + // Extend the base image to the TPM, regardless of how it was obtained + l_errl = TRUSTEDBOOT::extendBaseImage(); + #endif + #endif + } io_rtaskRetErrl=l_errl; } @@ -326,6 +329,53 @@ void PnorRP::initDaemon() break; } + #ifndef __HOSTBOOT_RUNTIME + #ifdef CONFIG_SECUREBOOT + if(!SECUREBOOT::enabled()) + { + + // If in secure mode, we already have securely obtained the header + // because we copied it before the blind purge. In non-secure mode, + // cache the header from PNOR (susceptible to attacks). This is ok + // because there are already no security guarantees in non-secure + // mode. We need to get the HBB address separately because the + // OC ignores the header + PNOR::SideInfo_t pnorInfo = {PNOR::WORKING}; + l_errhdl = PnorRP::getSideInfo(PNOR::WORKING, pnorInfo); + if(l_errhdl != NULL) + { + break; + } + + const SectionData_t* pHbb = &iv_TOC[PNOR::HB_BASE_CODE]; + bool ecc = (pHbb->integrity == FFS_INTEG_ECC_PROTECT) ? true :false; + + // We have to read two pages because the secure header is a page by + // itself, but it is prefixed by the SBE header + uint8_t pHeader[PAGESIZE] = {0}; + uint64_t fatalError = 0; + l_errhdl = readFromDevice( + pnorInfo.hbbAddress, + pHbb->chip, + ecc, + pHeader, + fatalError); + + // If fatalError != 0 there is an uncorrectable ECC error (UE). + // In that case, continue on with inaccurate data, as + // readFromDevice API will initiate a shutdown + if(l_errhdl != NULL) + { + break; + } + + // Skip the SBE header on the HBB image to get the real header + (void)SECUREBOOT::baseHeader().setNonSecurely( + pHeader); + } + #endif + #endif + // start task to wait on the queue task_create( wait_for_message, NULL ); } while(0); @@ -418,25 +468,49 @@ errlHndl_t PnorRP::getSectionInfo( PNOR::SectionId i_section, break; } + // inhibit any attempt to getSectionInfo on any attribute override + // sections if secureboot is enabled + bool l_inhibited = isInhibitedSection(id); + // Zero-length means the section is invalid - if( 0 == iv_TOC[id].size ) + if( 0 == iv_TOC[id].size + // attribute overrides inhibited by secure boot + || l_inhibited + ) { TRACFCOMP( g_trac_pnor, "PnorRP::getSectionInfo> Invalid Section Requested : i_section=%d", i_section ); - TRACFCOMP(g_trac_pnor, "o_info={ id=%d, size=%d }", iv_TOC[i_section].id, iv_TOC[i_section].size ); + #ifdef CONFIG_SECUREBOOT + if (l_inhibited) + { + TRACFCOMP( g_trac_pnor, "PnorRP::getSectionInfo> " + "attribute override inhibited by secureboot"); + } + #endif + uint64_t size = iv_TOC[i_section].size; + TRACFCOMP(g_trac_pnor, "o_info={ id=%d, size=%d }", + iv_TOC[i_section].id, size ); /*@ * @errortype - * @moduleid PNOR::MOD_PNORRP_GETSECTIONINFO - * @reasoncode PNOR::RC_INVALID_SECTION - * @userdata1 Requested Section - * @userdata2 TOC used - * @devdesc PnorRP::getSectionInfo> Invalid Address for read/write - * @custdesc A problem occurred while accessing the boot flash. - */ + * @moduleid PNOR::MOD_PNORRP_GETSECTIONINFO + * @reasoncode PNOR::RC_INVALID_SECTION + * @userdata1 Size of section + * @userdata2[0:7] TOC used + * @userdata2[8:15] Inhibited flag + * @userdata2[16:23] Requested Section + * @devdesc PnorRP::getSectionInfo> Invalid Address for + * read/write or prohibited by secureboot + * @custdesc A problem occurred while accessing the boot + * flash. + */ l_errhdl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, PNOR::MOD_PNORRP_GETSECTIONINFO, PNOR::RC_INVALID_SECTION, - TO_UINT64(i_section), - iv_TOC_used, + size, + TO_UINT64(FOUR_UINT8_TO_UINT32( + iv_TOC_used, + l_inhibited, + i_section, + 0)), true /*Add HB SW Callout*/); l_errhdl->collectTrace(PNOR_COMP_NAME); @@ -444,27 +518,108 @@ errlHndl_t PnorRP::getSectionInfo( PNOR::SectionId i_section, id = PNOR::INVALID_SECTION; break; } - } while(0); - if (PNOR::INVALID_SECTION != id) - { - TRACDCOMP( g_trac_pnor, "PnorRP::getSectionInfo: i_section=%d, id=%d", i_section, iv_TOC[i_section].id ); - - // copy my data into the external format - o_info.id = iv_TOC[id].id; - o_info.name = cv_EYECATCHER[id]; - o_info.vaddr = iv_TOC[id].virtAddr; - o_info.flashAddr = iv_TOC[id].flashAddr; - o_info.size = iv_TOC[id].size; - o_info.eccProtected = ((iv_TOC[id].integrity & FFS_INTEG_ECC_PROTECT) - != 0) ? true : false; - o_info.sha512Version = ((iv_TOC[id].version & FFS_VERS_SHA512) - != 0) ? true : false; - o_info.sha512perEC = ((iv_TOC[id].version & FFS_VERS_SHA512_PER_EC) - != 0) ? true : false; - o_info.readOnly = ((iv_TOC[id].misc & FFS_MISC_READ_ONLY) - != 0) ? true : false; - } + + if (PNOR::INVALID_SECTION != id) + { + TRACDCOMP( g_trac_pnor, "PnorRP::getSectionInfo: i_section=%d, id=%d", i_section, iv_TOC[i_section].id ); + + // copy my data into the external format + o_info.id = iv_TOC[id].id; + o_info.name = cv_EYECATCHER[id]; + +#ifdef CONFIG_SECUREBOOT + o_info.secureProtectedPayloadSize = 0; // for non secure sections + // the protected payload size + // defaults to zero + // handle secure sections in SPnorRP's address space + if (PNOR::isSecureSection(o_info.id)) + { + uint8_t* l_vaddr = reinterpret_cast<uint8_t*>(iv_TOC[id].virtAddr); + // By adding VMM_VADDR_SPNOR_DELTA twice we can translate a pnor + // address into a secure pnor address, since pnor, temp, and spnor + // spaces are equidistant. + // See comments in SPnorRP::verifySections() method in spnorrp.C + // and the definition of VMM_VADDR_SPNOR_DELTA in vmmconst.h + // for specifics. + o_info.vaddr = reinterpret_cast<uint64_t>(l_vaddr) + + VMM_VADDR_SPNOR_DELTA + + VMM_VADDR_SPNOR_DELTA; + + // Get size of the secured payload for the secure section + // Note: the payloadSize we get back is untrusted because + // we are parsing the header in pnor (non secure space). + size_t payloadTextSize = 0; + // Do an existence check on the container to see if it's non-empty + // and has valid beginning bytes. For optional Secure PNOR sections. + + if (PNOR::cmpSecurebootMagicNumber(l_vaddr)) + { + SECUREBOOT::ContainerHeader l_conHdr(l_vaddr); + payloadTextSize = l_conHdr.payloadTextSize(); + assert(payloadTextSize > 0,"Non-zero payload text size expected."); + } + else + { + uint32_t l_badMagicHeader = 0; + memcpy(&l_badMagicHeader, l_vaddr, sizeof(MAGIC_NUMBER)); + TRACFCOMP( g_trac_pnor, ERR_MRK"PnorRP::getSectionInfo: magic number not valid to parse container for section = %s magic number = 0x%X", + o_info.name, l_badMagicHeader); + /*@ + * @errortype + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid PNOR::MOD_PNORRP_GETSECTIONINFO + * @reasoncode PNOR::RC_BAD_SECURE_MAGIC_NUM + * @userdata1 Requested Section + * @userdata2 Bad magic number + * @devdesc PNOR section does not have the known secureboot magic number + * @custdesc Corrupted flash image or firmware error during system boot + */ + l_errhdl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORRP_GETSECTIONINFO, + PNOR::RC_BAD_SECURE_MAGIC_NUM, + TO_UINT64(i_section), + TO_UINT64(l_badMagicHeader), + true /*Add HB SW Callout*/); + l_errhdl->collectTrace(PNOR_COMP_NAME); + break; + } + // skip secure header for secure sections at this point in time + o_info.vaddr += PAGESIZE; + // now that we've skipped the header we also need to adjust the + // size of the section to reflect that. + // Note: For unsecured sections, the header skip and size decrement + // was done previously in pnor_common.C + o_info.size -= PAGESIZE; + + // cache the value in SectionInfo struct so that we can + // parse the container header less often + o_info.secureProtectedPayloadSize = payloadTextSize; + } + else +#endif + { + o_info.vaddr = iv_TOC[id].virtAddr; + } + + o_info.flashAddr = iv_TOC[id].flashAddr; + o_info.size = iv_TOC[id].size; + o_info.eccProtected = ((iv_TOC[id].integrity & FFS_INTEG_ECC_PROTECT) + != 0) ? true : false; + o_info.sha512Version = ((iv_TOC[id].version & FFS_VERS_SHA512) + != 0) ? true : false; + o_info.sha512perEC = ((iv_TOC[id].version & FFS_VERS_SHA512_PER_EC) + != 0) ? true : false; + o_info.readOnly = ((iv_TOC[id].misc & FFS_MISC_READ_ONLY) + != 0) ? true : false; +// TODO securebootp9 the following field does not exist in p9. If the field is +// to be added to p9 we need to enable this line at that time, or remove and +// replace with appropriate code. +// o_info.reprovision = ((iv_TOC[id].misc & FFS_MISC_REPROVISION) +// != 0) ? true : false; + } + + } while(0); return l_errhdl; } diff --git a/src/usr/pnor/spnorrp.C b/src/usr/pnor/spnorrp.C new file mode 100644 index 000000000..71de27578 --- /dev/null +++ b/src/usr/pnor/spnorrp.C @@ -0,0 +1,948 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/spnorrp.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "pnorrp.H" +#include "spnorrp.H" +#include <pnor/pnor_reasoncodes.H> +#include <initservice/taskargs.H> +#include <initservice/initserviceif.H> +#include <sys/msg.h> +#include <trace/interface.H> +#include <errl/errlmanager.H> +#include <sys/mm.h> +#include <errno.h> +#include <util/align.H> +#include <config.h> +#include "pnor_common.H" +#include <console/consoleif.H> +#include <secureboot/service.H> +#include <secureboot/containerheader.H> +#include <secureboot/trustedbootif.H> +#include <secureboot/header.H> + +extern trace_desc_t* g_trac_pnor; + +// used to uniquley identify the secure PNOR RP message queue +const char* SPNORRP_MSG_Q = "spnorrpq"; + +// Easy macro replace for unit testing +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + +namespace PNOR +{ + //used by secure message queue for load/unload of secure PNOR sections + enum secure_msg_type + { + MSG_LOAD_SECTION = 0x02, + MSG_UNLOAD_SECTION = 0x03, + }; +}; + + +using namespace PNOR; + +/******************** + Helper Methods + ********************/ + +/** + * @brief Static function wrapper to pass into task_create + */ +void* secure_wait_for_message( void* unused ) +{ + TRACUCOMP(g_trac_pnor, "wait_for_message> " ); + Singleton<SPnorRP>::instance().waitForMessage(); + return NULL; +} + + +/******************** + Private/Protected Methods + ********************/ + +/** + * @brief Constructor + */ +SPnorRP::SPnorRP() +: +iv_msgQ(NULL) +,iv_startupRC(0) +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::SPnorRP> " ); + // setup everything in a separate function + initDaemon(); + + TRACFCOMP(g_trac_pnor, "< SPnorRP::PnorRP : Startup Errors=%X ", iv_startupRC ); +} + +/** + * @brief Destructor + */ +SPnorRP::~SPnorRP() +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::~SPnorRP> " ); + + // delete the message queue we created + if( iv_msgQ ) + { + msg_q_destroy( iv_msgQ ); + } + + // clean up the load records + for(std::map<SectionId, LoadRecord*>::iterator + i = iv_loadedSections.begin(); + i != iv_loadedSections.end(); + ++i) + { + LoadRecord* l_rec = (*i).second; + delete l_rec; + } + + TRACFCOMP(g_trac_pnor, "< SPnorRP::~SPnorRP" ); +} + +/** + * @brief A wrapper for mm_alloc_block that encapsulates error log creation. + */ +errlHndl_t SPnorRP::allocBlock(msg_q_t i_mq, void* i_va, uint64_t i_size) const +{ + errlHndl_t l_errhdl = NULL; + int rc = mm_alloc_block(i_mq, i_va, i_size ); + if( rc ) + { + TRACFCOMP( g_trac_pnor,"SPnorRP::allocBlock> Error " + "with mm_alloc_block at address 0x%.16llX : rc=%d", i_va, rc ); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid PNOR::MOD_SPNORRP_ALLOCATE_BLOCK + * @reasoncode PNOR::RC_EXTERNAL_ERROR + * @userdata1 Requested Address + * @userdata2 rc from mm_alloc_block + * @devdesc SPnorRP::initDaemon> Error from mm_alloc_block + * @custdesc A problem occurred while initializing secure PNOR + */ + l_errhdl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + PNOR::MOD_SPNORRP_ALLOCATE_BLOCK, + PNOR::RC_EXTERNAL_ERROR, + TO_UINT64(reinterpret_cast<uint64_t>(i_va)), + TO_UINT64(rc), + true); //Add HB SW Callout + l_errhdl->collectTrace(PNOR_COMP_NAME); + l_errhdl->collectTrace(SECURE_COMP_NAME); + } + return l_errhdl; +} + +/** + * @brief A wrapper for mm_set_permission that adds error log creation. + */ +errlHndl_t SPnorRP::setPermission(void* i_va, uint64_t i_size, + uint64_t i_accessType) const +{ + errlHndl_t l_errhdl = NULL; + int rc = mm_set_permission(reinterpret_cast<void*>(i_va), i_size, + i_accessType); + if ( rc ) + { + TRACFCOMP( g_trac_pnor, "SPnorRP::setPermission> Error " + "with mm_set_permission at address 0x%.16llX : rc=%d",i_va, rc ); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid PNOR::MOD_SPNORRP_SET_PERMISSION + * @reasoncode PNOR::RC_EXTERNAL_ERROR + * @userdata1 Requested Address + * @userdata2 rc from mm_set_permission + * @devdesc Could not set permissions of the + * given PNOR section + * @custdesc A problem occurred while initializing secure PNOR + */ + l_errhdl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + PNOR::MOD_SPNORRP_SET_PERMISSION, + PNOR::RC_EXTERNAL_ERROR, + TO_UINT64(reinterpret_cast<uint64_t>(i_va)), + TO_UINT64(rc), + true); // Add HB SW Callout + l_errhdl->collectTrace(PNOR_COMP_NAME); + l_errhdl->collectTrace(SECURE_COMP_NAME); + } + return l_errhdl; +} + +/** + * @brief Initialize the daemon + */ +void SPnorRP::initDaemon() +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::initDaemon> " ); + errlHndl_t l_errhdl = NULL; + + do + { + // create a message queue for secure space + iv_msgQ = msg_q_create(); + + // register it so that it can be discovered by loadSecureSection() + int rc = msg_q_register(iv_msgQ, SPNORRP_MSG_Q); + + assert(rc == 0); + + // create a Block for temp space + l_errhdl = allocBlock( NULL, reinterpret_cast<void*>(TEMP_VADDR), + PNOR_SIZE ); + if( l_errhdl ) + { + break; + } + + // set permissions for temp space + l_errhdl = setPermission(reinterpret_cast<void*>(TEMP_VADDR), + PNOR_SIZE, WRITABLE | ALLOCATE_FROM_ZERO); + if ( l_errhdl ) + { + break; + } + + // create a block for secure space + l_errhdl = allocBlock( iv_msgQ, reinterpret_cast<void*>(SBASE_VADDR), + PNOR_SIZE ); + if( l_errhdl ) + { + break; + } + + // set permissions for secure space + l_errhdl = setPermission( reinterpret_cast<void*>(SBASE_VADDR), + PNOR_SIZE, NO_ACCESS); + if ( l_errhdl ) + { + break; + } + + // start task to wait on the queue + task_create( secure_wait_for_message, NULL ); + + } while(0); + + if( l_errhdl ) + { + iv_startupRC = l_errhdl->reasonCode(); + errlCommit(l_errhdl,PNOR_COMP_ID); + } + + TRACUCOMP(g_trac_pnor, "< SPnorRP::initDaemon" ); +} + +/** + * @brief Load secure sections into temporary address space and verify them + */ +void SPnorRP::verifySections(LoadRecord* o_rec, SectionId i_id) +{ + SectionInfo_t l_info; + errlHndl_t l_errhdl = NULL; + bool failedVerify = false; + + do { + l_errhdl = getSectionInfo(i_id, l_info); + + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, + "< SPnorrRP::verifySections - getSectionInfo failed"); + + break; + } + TRACDCOMP(g_trac_pnor,"SPnorRP::verifySections getSectionInfo" + " succeeded for sec = %s", l_info.name); + + l_info.vaddr -= PAGESIZE; // back up a page to expose the secure header + l_info.size += PAGESIZE; // add a page to size to account for the header + + // it's a coding error if l_info.vaddr is not in secure space + assert(l_info.vaddr >= SBASE_VADDR, "Virtual address for section %s is" + " not in secure space. Bad ptr=0x%X", l_info.name, l_info.vaddr); + + // Note: A pointer to virtual memory in one PNOR space can be converted + // to a pointer to any of the other two PNOR spaces and visa versa. + // These are unsecured space, temp space, and secured space. They are + // evenly separated by VMM_VADDR_SPNOR_DELTA and in the above order. + // l_info.vaddr points to secure space, so we subtract a delta value + // from it to calculate its corresponding address in temp space. + uint8_t* l_tempAddr = reinterpret_cast<uint8_t*>(l_info.vaddr) + - VMM_VADDR_SPNOR_DELTA; + + // calcluate unsecured address from temp address + uint8_t* l_unsecuredAddr = l_tempAddr - VMM_VADDR_SPNOR_DELTA; + + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections section start address " + "in temp space is 0x%.16llX, " + "section start address in unsecured space is 0x%.16llX, " + "l_info.size = 0x%.16llX, " + "payload size = 0x%.16llX, ", + l_tempAddr, l_unsecuredAddr, l_info.size, + l_info.secureProtectedPayloadSize); + + TRACDBIN(g_trac_pnor,"SPnorRP::verifySections unsecured mem now: ", + l_unsecuredAddr, 128); + + TRACDCOMP(g_trac_pnor,"SPnorRP::verifySections about to do memcpy"); + + // copy from unsecured PNOR space to temp PNOR space + memcpy(l_tempAddr, l_unsecuredAddr, l_info.secureProtectedPayloadSize + + PAGESIZE); // plus header size + + SECUREBOOT::ContainerHeader l_conHdr(l_tempAddr); + size_t l_totalContainerSize = l_conHdr.totalContainerSize(); + + TRACDCOMP(g_trac_pnor, "SPnorRP::verifySections " + "Total container size = 0x%.16llX", l_totalContainerSize); + + assert(l_totalContainerSize >= PAGESIZE + + + l_info.secureProtectedPayloadSize, + "For section %s, total container size (%d) was less than header " + "size (4096) + payload text size (%d)", + l_info.name, + l_totalContainerSize, + l_info.secureProtectedPayloadSize); + + assert(l_info.size >= l_totalContainerSize, + "For section %s, logical section size (%d) was less than total " + "container size (%d)", + l_info.name, + l_info.size, + l_totalContainerSize); + + TRACDCOMP(g_trac_pnor,"SPnorRP::verifySections did memcpy"); + TRACDBIN(g_trac_pnor,"SPnorRP::verifySections temp mem now: ", + l_tempAddr, 128); + + // store secure space pointer in load record + o_rec->secAddr = reinterpret_cast<uint8_t*>(l_info.vaddr) + PAGESIZE; + + TRACFCOMP(g_trac_pnor,"section start address in secure space is " + "0x%.16llX",o_rec->secAddr); + + // verify while in temp space + if (SECUREBOOT::enabled()) + { + l_errhdl = SECUREBOOT::verifyContainer(l_tempAddr); + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"SPnorrRP::verifySections - section " + "with id 0x%08X failed verifyContainer", i_id); + failedVerify = true; + break; + } + + l_errhdl = miscSectionVerification(l_tempAddr, i_id); + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"SPnorrRP::verifySections - section " + " with id 0x%08X failed miscSectionVerification", i_id); + failedVerify = true; + break; + } + } + + // verification succeeded + + // parse container header now that it is verified + // store the payload text size in the section load record + // Note: the text size we get back is now trusted + o_rec->textSize = l_conHdr.payloadTextSize(); + l_totalContainerSize = l_conHdr.totalContainerSize(); + + assert(o_rec->textSize == l_info.secureProtectedPayloadSize); + + // pcr extension of PNOR hash + l_errhdl = TRUSTEDBOOT::extendPnorSectionHash(l_conHdr, + (l_tempAddr + PAGESIZE), + i_id); + if(l_errhdl) + { + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections extendPnorSectionHash failed"); + break; + } + + // remove secure header page in temp space + mm_remove_pages(RELEASE, l_tempAddr, PAGESIZE); + + // keep track of info size in load record + o_rec->infoSize = l_totalContainerSize - PAGESIZE; + + // set permissions on the secured pages to writable + l_errhdl = setPermission(o_rec->secAddr, o_rec->textSize, WRITABLE); + if(l_errhdl) + { + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections set permissions " + "failed on text section"); + break; + } + + // set permissions on the unsecured pages to write tracked so that any + // unprotected payload pages with dirty writes can flow back to PNOR. + uint64_t unprotectedPayloadSize = l_totalContainerSize + - PAGESIZE - o_rec->textSize; + if (unprotectedPayloadSize) // only write track a non-zero range + { + TRACFCOMP(g_trac_pnor,INFO_MRK " SPnorRP::verifySections " + "creating unprotected area (%d bytes) for section %s", + unprotectedPayloadSize, + l_info.name); + + // Split the mod math out of the assert as the trace would not + // display otherwise. + bool l_onPageBoundary = !(o_rec->textSize % PAGESIZE); + assert( l_onPageBoundary, "For section %s, payloadTextSize does " + "not fall on a page boundary and there is an unprotected " + "payload", + l_info.name); + + l_errhdl = setPermission(o_rec->secAddr + o_rec->textSize, + unprotectedPayloadSize, + WRITABLE | WRITE_TRACKED); + if(l_errhdl) + { + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections set permissions " + "failed on data section"); + break; + } + + // Register the write tracked memory range to be flushed on + // shutdown. + INITSERVICE::registerBlock(o_rec->secAddr + o_rec->textSize, + unprotectedPayloadSize, SPNOR_PRIORITY); + } + else + { + TRACFCOMP(g_trac_pnor,INFO_MRK " SPnorRP::verifySections not " + "creating unprotected area for section %s", + l_info.name); + } + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections set permissions " + "and register block complete"); + } while(0); + + if( l_errhdl ) + { + uint32_t l_errPlid = l_errhdl->plid(); + iv_startupRC = l_errhdl->reasonCode(); + TRACFCOMP(g_trac_pnor,ERR_MRK"SPnorRP::verifySections there was an error"); + if (failedVerify) + { + TRACFCOMP(g_trac_pnor,ERR_MRK"SPnorRP::verifySections failed verify"); + SECUREBOOT::handleSecurebootFailure(l_errhdl); + } + else + { + errlCommit(l_errhdl,PNOR_COMP_ID); + INITSERVICE::doShutdown(l_errPlid); + } + } +} + + + +/** + * @brief Message receiver for secure space + */ +void SPnorRP::waitForMessage() +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::waitForMessage>" ); + + errlHndl_t l_errhdl = NULL; + msg_t* message = NULL; + uint8_t* user_addr = NULL; + uint8_t* eff_addr = NULL; + int rc = 0; + uint64_t status_rc = 0; + LoadRecord l_rec; + l_rec.secAddr = NULL; + l_rec.textSize = 0; + l_rec.infoSize = 0; + + while(1) + { + status_rc = 0; + TRACUCOMP(g_trac_pnor, "SPnorRP::waitForMessage> waiting for message" ); + message = msg_wait( iv_msgQ ); + if( message ) + { + // data[0] = virtual address requested + // data[1] = address to place contents + eff_addr = reinterpret_cast<uint8_t*>(message->data[0]); + user_addr = reinterpret_cast<uint8_t*>(message->data[1]); + + switch(message->type) + { + case( MSG_MM_RP_READ ): + { + uint64_t delta = VMM_VADDR_SPNOR_DELTA; + + // check and see if our cached section information + // is obsolete and if so, change it + if ( (eff_addr < l_rec.secAddr) || + (eff_addr >= (l_rec.secAddr + l_rec.infoSize)) + ) + { + bool l_found = false; + // recalculate addresses + for(std::map<SectionId, LoadRecord*>::const_iterator + i = iv_loadedSections.begin(); + i != iv_loadedSections.end(); + ++i) + { + LoadRecord* l_record = (*i).second; + if ( (eff_addr >= l_record->secAddr) && + (eff_addr < (l_record->secAddr + + l_record->infoSize)) + ) + { + l_rec = *l_record; + l_found = true; + break; + } + } + if (!l_found) + { + TRACFCOMP( g_trac_pnor, + "SPnorRP::waitforMessage - address %p " + "out of bounds", eff_addr); + // not much we can do here unfortunately + // if we get here it is a coding mistake + status_rc = -EFAULT; + break; + } + + } + + TRACDCOMP( g_trac_pnor, "SPnorRP::waitForMessage got a" + " request to read from secure space - " + "message : user_addr=%p, eff_addr=%p, msgtype=%d, " + "textSize=0x%.16llX secAddr0x%.16llX", user_addr, + eff_addr, message->type, l_rec.textSize, + l_rec.secAddr); + + // determine the source of the data depending on + // whether it is part of the secure payload. + // by the way, this if could be removed to make this + // purely arithmetic + if (eff_addr >= (l_rec.secAddr + l_rec.textSize)) + { + delta += VMM_VADDR_SPNOR_DELTA; + } + TRACDCOMP( g_trac_pnor, "SPnorRP::waitForMessage " + "source address: %p", eff_addr - delta); + + // depending on the value of delta, memcpy from either + // temp space or unsecured pnor space over to secure + // pnor space + memcpy(user_addr, eff_addr - delta, PAGESIZE); + // if the page came from temp space then free up + // the temp page now that we're done with it + if (eff_addr < (l_rec.secAddr + l_rec.textSize)) + { + mm_remove_pages(RELEASE, eff_addr - delta, + PAGESIZE); + } + } + break; + + case( MSG_MM_RP_WRITE ): + { + TRACDCOMP( g_trac_pnor, "SPnorRP::waitForMessage " + "writing to an unsecured area of section."); + + // calculate unsecured address (in PnorRP) by + // subtracting two deltas from secure address + uint8_t* dest = eff_addr - VMM_VADDR_SPNOR_DELTA + - VMM_VADDR_SPNOR_DELTA; + memcpy(dest, user_addr, PAGESIZE); + } + break; + + case( PNOR::MSG_LOAD_SECTION ): + { + SectionId l_id = + static_cast<SectionId>(message->data[0]); + if (iv_loadedSections[l_id] == NULL) + { + LoadRecord* l_record = new LoadRecord; + verifySections(l_record,l_id); + iv_loadedSections[l_id] = l_record; + + // cache the record to use fields later as hints + l_rec = *l_record; + } + } + break; + + default: + TRACFCOMP( g_trac_pnor, ERR_MRK"SPnorRP::waitForMessage> " + "Unrecognized message type : user_addr=%p, eff_addr=%p," + " msgtype=%d", user_addr, eff_addr, message->type ); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid PNOR::MOD_SPNORRP_WAITFORMESSAGE + * @reasoncode PNOR::RC_INVALID_MESSAGE_TYPE + * @userdata1 Message type + * @userdata2 Requested Virtual Address + * @devdesc PnorRP::waitForMessage> Unrecognized + * message type + * @custdesc A problem occurred while accessing + * the boot flash. + */ + l_errhdl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + PNOR::MOD_SPNORRP_WAITFORMESSAGE, + PNOR::RC_INVALID_MESSAGE_TYPE, + TO_UINT64(message->type), + reinterpret_cast<uint64_t>(eff_addr), + true /*Add HB SW Callout*/); + l_errhdl->collectTrace(PNOR_COMP_NAME); + l_errhdl->collectTrace(SECURE_COMP_NAME); + status_rc = -EINVAL; /* Invalid argument */ + } + + assert(msg_is_async(message) == false); + + if( l_errhdl ) + { + errlCommit(l_errhdl,PNOR_COMP_ID); + } + + /* Expected Response: + * data[0] = virtual address requested or + * section id (if load message) + * data[1] = rc (0 or negative errno value) + * extra_data = Specific reason code. + */ + message->data[1] = status_rc; + message->extra_data = 0; + rc = msg_respond( iv_msgQ, message ); + if( rc ) + { + TRACFCOMP(g_trac_pnor, + "SPnorRP::waitForMessage> Error from msg_respond, " + "giving up : rc=%d", rc ); + break; + } + } + assert(message != NULL); + } + + TRACFCOMP(g_trac_pnor, "< SPnorRP::waitForMessage" ); +} + +/** + * @brief Loads requested PNOR section to secure virtual address space + */ +errlHndl_t PNOR::loadSecureSection(const SectionId i_section) +{ + // Send message to secure provider to load the section + errlHndl_t err = NULL; + + if (!isSecureSection(i_section)) + { + TRACFCOMP(g_trac_pnor,ERR_MRK"PNOR::loadSecureSection> called on " + "unsecured section"); + // TODO securebootp9 remove below temporary code after all of the + // sections in the below if condition have been fully ported and added + // to isSecureSection. + // start temporary code + if (i_section == PNOR::HB_EXT_CODE || + i_section == PNOR::HB_DATA || + i_section == PNOR::SBE_IPL || + i_section == PNOR::CENTAUR_SBE || + i_section == PNOR::PAYLOAD || + i_section == PNOR::OCC || + i_section == PNOR::HB_RUNTIME) + { + // For now, ignore the attempt to load this section securely. + // Returning from the middle of a function is excusable because + // it keeps the temp code in one place, making it easier to remove. + return NULL; + } + // end temporary code + // TODO securebootp9 revisit this assert code and replace with error log + // code if it is deemed that this assert could happen in the field + assert(false,"PNOR::loadSection> section %i is not a secure section", + i_section); + } + + msg_q_t spnorQ = msg_q_resolve(SPNORRP_MSG_Q); + + assert(spnorQ != NULL); + + msg_t* msg = msg_allocate(); + + assert(msg != NULL); + + msg->type = PNOR::MSG_LOAD_SECTION; + msg->data[0] = static_cast<uint64_t>(i_section); + int rc = msg_sendrecv(spnorQ, msg); + + TRACFCOMP(g_trac_pnor, "loadSecureSection i_section = %i",i_section); + + // TODO securebootp9 - Need to be able to receive an error from the + // message handler. Also, message handler should police whether the request + // is for a secure section or not and throw an error accordingly. + //if (0 == rc) + //{ + // err = reinterpret_cast<errlHndl_t>(msg->data[1]); + //} + //else remove the if clause below at some point + if (rc != 0) + { + + TRACFCOMP(g_trac_pnor,ERR_MRK"PNOR::loadSecureSection> Error from msg_sendrecv rc=%d", + rc ); + /* @errorlog + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid MOD_PNORRP_LOADSECURESECTION + * @reasoncode RC_EXTERNAL_ERROR + * @userdata1 returncode from msg_sendrecv() + * @userdata2[0:31] SPNOR message type [LOAD | UNLOAD] + * @userdata2[32:63] Section ID + * @devdesc Could not load/unload section. + * @custdesc Security failure: unable to securely load + * requested firmware. + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + MOD_PNORRP_LOADSECURESECTION, + RC_EXTERNAL_ERROR, + rc, + TWO_UINT32_TO_UINT64(PNOR::MSG_LOAD_SECTION, + i_section), + true /* Add HB Software Callout */); + err->collectTrace(PNOR_COMP_NAME); + } + msg_free(msg); + + return err; +} + +/** + * @brief Flushes any applicable pending writes and unloads requested PNOR + * section from secure virtual address space + */ +errlHndl_t PNOR::unloadSecureSection(const SectionId i_section) +{ + // @TODO RTC 156118 + // Replace with call to secure provider to unload the section + errlHndl_t pError=NULL; + return pError; +} + +errlHndl_t SPnorRP::miscSectionVerification(const uint8_t *i_vaddr, + SectionId i_secId) const +{ + errlHndl_t l_errl = NULL; + assert(i_vaddr != NULL); + + TRACFCOMP(g_trac_pnor, "SPnorRP::miscSectionVerification section=%d", i_secId); + +// TODO securebootp9 - remove the following #if 0 and address issues +#if 0 + // Do any additional verification needed for a specific PNOR section + switch (i_secId) { + case HB_EXT_CODE: + // Compare HBB and HBI versions. Pass the vaddr of HBI's hash page + // table by skipping past the container header. + l_errl = baseExtVersCheck((i_vaddr + PAGESIZE)); + break; + case SBKT: + // Ensure the SBKT partition has a valid key transition container + // Add PAGESIZE to skip outer container + l_errl = keyTransitionCheck((i_vaddr + PAGESIZE)); + break; + default: + break; + } +#endif + return l_errl; +} + +errlHndl_t SPnorRP::baseExtVersCheck(const uint8_t *i_vaddr) const +{ + errlHndl_t l_errl = NULL; + assert(i_vaddr != NULL); + + // Check if measured and build time hashes of HBB sw signatures match. + // Query the HBB header + const void* l_pHbbHeader = NULL; + SECUREBOOT::baseHeader().getHeader(l_pHbbHeader); + // Fatal code bug if either address is NULL + assert(l_pHbbHeader!=NULL,"ERORR: Cached header address is NULL"); + // Build a container header object from the raw header + SECUREBOOT::ContainerHeader l_hbbContainerHeader(l_pHbbHeader); + + // Calculate hash of HBB's sw signatures + SHA512_t l_hashSwSigs = {0}; + + SECUREBOOT::hashBlob(l_hbbContainerHeader.sw_sigs(), + l_hbbContainerHeader.totalSwKeysSize(), + l_hashSwSigs); + + // Get build time hash of HBB's sw signatures. The hash of HBB's sw + // signatures are stored in the first entry (SALT) of HBI's hash page + // table. The first entry of HBI's hash pagle starts immediately after the + // header so we can simply cast the vaddr to get our first entry (SALT) + // Note: It is expected that i_vaddr points to the start of HBI's hash + // page table. + const PAGE_TABLE_ENTRY_t* l_hashPageTableSaltEntry = + reinterpret_cast<const PAGE_TABLE_ENTRY_t*>(i_vaddr); + + // Throw an error if the hashes do not match to the truncated length + // of a hash page table entry. + if ( memcmp(l_hashSwSigs, l_hashPageTableSaltEntry, + HASH_PAGE_TABLE_ENTRY_SIZE) != 0 ) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"SPnorRP::baseExtVersCheck Hostboot Base and Extended image mismatch"); + TRACFBIN(g_trac_pnor,"SPnorRP::baseExtVersCheck Measured sw key hash", + l_hashSwSigs, HASH_PAGE_TABLE_ENTRY_SIZE); + TRACFBIN(g_trac_pnor,"SPnorRP::baseExtVersCheck HBI's hash page table salt entry", + l_hashPageTableSaltEntry, HASH_PAGE_TABLE_ENTRY_SIZE); + + // Memcpy needed for measured hash to avoid gcc error: dereferencing + // type-punned pointer will break strict-aliasing rules + uint64_t l_measuredHash = 0; + memcpy(&l_measuredHash, l_hashSwSigs, sizeof(l_measuredHash)); + /*@ errorlog + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid MOD_SPNORRP_BASE_EXT_VER_CHK + * @reasoncode RC_BASE_EXT_MISMATCH + * @userdata1 First 8 bytes of hash of measured SW signatures + * @userdata2 First 8 bytes of hash of stored SW signatures in + * hash page table + * @devdesc Hostboot Base and Extend code do not match versions. + * @custdesc Firmware level mismatch. + */ + l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + MOD_SPNORRP_BASE_EXT_VER_CHK, + RC_BASE_EXT_MISMATCH, + l_measuredHash, + TO_UINT64(*reinterpret_cast<const uint64_t*>(l_hashPageTableSaltEntry))); + l_errl->collectTrace(PNOR_COMP_NAME); + l_errl->collectTrace(SECURE_COMP_NAME); + } + + return l_errl; +} + +errlHndl_t SPnorRP::keyTransitionCheck(const uint8_t *i_vaddr) const +{ + errlHndl_t l_errl = NULL; + assert(i_vaddr != NULL); + + do { + // Check if the header flags have the key transition bit set + SECUREBOOT::ContainerHeader l_nestedConHdr(i_vaddr); + if (!l_nestedConHdr.sb_flags()->hw_key_transition) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SPnorRP::keyTransitionCheck() - Key transition flag not set"); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid MOD_SPNORRP_KEY_TRAN_CHK + * @reasoncode RC_KEY_TRAN_FLAG_UNSET + * @userdata1 0 + * @userdata2 0 + * @devdesc Key transition flag not set in nested SBKT container containing new hw keys + * @custdesc Secureboot key transition failure + */ + l_errl = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + MOD_SPNORRP_KEY_TRAN_CHK, + RC_KEY_TRAN_FLAG_UNSET, + 0, + 0, + true /*Add HB Software Callout*/ ); + l_errl->collectTrace(PNOR_COMP_NAME); + l_errl->collectTrace(SECURE_COMP_NAME); + break; + } + +// TODO securebootp9 - remove the following #if 0 and address issues +#if 1 + // Validate nested container is properly signed using new hw keys + l_errl = SECUREBOOT::verifyContainer(const_cast<uint8_t*>(i_vaddr), + l_nestedConHdr.hwKeyHash()); + if (l_errl) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SPnorRP::keyTransitionCheck() - failed verifyContainer"); + break; + } +#endif + }while(0); + + return l_errl; +} + +bool PNOR::cmpSecurebootMagicNumber(const uint8_t* i_vaddr) +{ + return memcmp(&MAGIC_NUMBER, i_vaddr, sizeof(MAGIC_NUMBER)) == 0; +} + +errlHndl_t PNOR::hasSecurebootMagicNumber(const SectionId i_section, + bool &o_valid) +{ + errlHndl_t l_errl = NULL; + SectionInfo_t l_info; + + // Force to false + o_valid = false; + + // This will not work for HBB + assert(i_section != HB_BASE_CODE, "hasSecurebootMagicNumber() does not work for HBB section"); + + bool isSecure = PNOR::isSecureSection(i_section); + do { + l_errl = getSectionInfo(i_section, l_info); + if (l_errl) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"PNOR::hasSecurebootMagicNumber(): - getSectionInfo failed"); + break; + } + + // Use PNOR vaddr + if(isSecure) + { + // back up a page to expose the secure header + l_info.vaddr = l_info.vaddr - VMM_VADDR_SPNOR_DELTA + - VMM_VADDR_SPNOR_DELTA + - PAGESIZE; + } + o_valid = cmpSecurebootMagicNumber(reinterpret_cast<uint8_t*> + (l_info.vaddr)); + }while(0); + + return l_errl; +} + diff --git a/src/usr/pnor/spnorrp.H b/src/usr/pnor/spnorrp.H new file mode 100644 index 000000000..9f7e533d5 --- /dev/null +++ b/src/usr/pnor/spnorrp.H @@ -0,0 +1,190 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/spnorrp.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PNOR_SPNORRP_H +#define __PNOR_SPNORRP_H +#include <pnor/pnorif.H> +#include <sys/msg.h> +#include <stdint.h> +#include <builtins.h> +#include <errl/errlentry.H> +#include <vmmconst.h> +#include <map> +#include "pnor_common.H" +#include "ffs.h" +#include <config.h> + +/** + * Secure PNOR Resource Provider + */ + +class SPnorRP +{ + public: + /** + * @brief Static Initializer + * @param[in] ref to errlHndl_t + */ + static void init( errlHndl_t &io_rtaskRetErrl ); + + /** + * @brief Returns true if the initial startup failed for some reason + * @param[out] Return code + * @return true if startup failed + */ + bool didStartupFail( uint64_t& o_rc ) const + { + if( iv_startupRC ) + { + o_rc = iv_startupRC; + return true; + } + return false; + }; + + + protected: + /** + * @brief Constructor, default TOC offsets to side A + */ + SPnorRP(); + + /** + * @brief Destructor + */ + ~SPnorRP(); + + private: + + enum + { + SBASE_VADDR = VMM_VADDR_SPNOR_RP, /**< 8GB = 0x200000000*/ + TEMP_VADDR = VMM_VADDR_SPNOR_TEMP, /**< 5GB = 0x140000000 */ + LAST_VADDR = SBASE_VADDR + PNOR::PNOR_SIZE, /**< End of our VA range */ + }; + + /* bitwise enumeration to keep track of verified sections */ + enum + { + HBI_SECTION = 0x1, + }; + + /** + * Pointer to the message queue where we receive messages for + * secure space. + */ + msg_q_t iv_msgQ; + + /** + * Remember that we failed during initial startup + * This is set by startup methods to indicate to constructor that + * something went wrong + */ + uint64_t iv_startupRC; + + /** + * Keep track of secured payload size and secure section addresses + */ + struct LoadRecord{ + uint8_t* secAddr; + size_t textSize; + size_t infoSize; + }; + std::map<PNOR::SectionId, LoadRecord*> iv_loadedSections; + + + /** + * @brief Initialize the daemon, called by constructor + */ + void initDaemon(); + + /** + * @brief Load secure sections into temporary address space and verify them + */ + void verifySections(LoadRecord* o_rec, PNOR::SectionId i_id); + + /** + * @brief Message receiver for secure space + */ + void waitForMessage(); + + // disable copy ctor + SPnorRP(const SPnorRP&); + + // disable assignment operator + SPnorRP& operator=(const SPnorRP&); + + // allow local helper function to call private methods + friend void* secure_wait_for_message( void* unused ); + + /** + * @brief A wrapper for mm_alloc_block that encapsulates error log + * creation. + */ + errlHndl_t allocBlock(msg_q_t mq, void* va, uint64_t size) const; + + /** + * @brief A wrapper for mm_set_permission that encapsulates error log + * creation. + */ + errlHndl_t setPermission(void* va, uint64_t size, + uint64_t accessType) const; + + /** + * @brief Handles any additional section specific verification checks. + * @param[in] i_vaddr - vaddr of PNOR section to verify. Includes header + * NULL will assert + * @param[in] i_secId - PNOR section id to verify + * @return errlHndl_t - NULL if success, errlHndl_t otherwise. + */ + errlHndl_t miscSectionVerification(const uint8_t *i_vaddr, + PNOR::SectionId i_secId) const; + + /** + * @brief Check if HBB and HBI were part of the same build + * Calculate HBB sw signatures hash and compare that to the build + * time hash of HBB sw signatures. The build time HBB hash is stored + * in the first entry (SALT) of HBI's hash page table. + * @param[in] i_vaddr - vaddr points to start of hash page table of HBI + * NOTE: Since this expects the vaddr to point to the + * start of the hash page table, the secureboot header + * must be skipped prior by the caller. + * NULL will assert + * @return errlHndl_t - NULL if success, errlHndl_t otherwise. + */ + errlHndl_t baseExtVersCheck(const uint8_t *i_vaddr) const; + + /** + * @brief Check if SBKT is properly formatted to then provide the new HW + * key hash to transition the system to. + * @param[in] i_vaddr - vaddr points to start SBKT's nested container + * NOTE: Since this expects the vaddr to point to the + * start of the nested container, the outer secureboot + * header must be skipped prior by the caller. + * NULL will assert + * @return errlHndl_t - NULL if success, errlHndl_t otherwise. + */ + errlHndl_t keyTransitionCheck(const uint8_t *i_vaddr) const; +}; +#endif diff --git a/src/usr/pnor/test/pnorrptest.H b/src/usr/pnor/test/pnorrptest.H index 8222063a6..bd0c8bc9d 100644 --- a/src/usr/pnor/test/pnorrptest.H +++ b/src/usr/pnor/test/pnorrptest.H @@ -814,10 +814,15 @@ class PnorRpTest : public CxxTest::TestSuite // APIs are callable; they should return success always // until 156118 implements the real support. At that time // this testcase should be updated. +#ifdef CONFIG_SECUREBOOT errlHndl_t pError=NULL; - do { + if (!PNOR::isSecureSection(PNOR::SBE_IPL)) + { + break; + } + pError = PNOR::loadSecureSection(PNOR::SBE_IPL); if(pError != NULL) { @@ -837,6 +842,7 @@ class PnorRpTest : public CxxTest::TestSuite } } while (0); +#endif } }; diff --git a/src/usr/secureboot/base/containerheader.C b/src/usr/secureboot/base/containerheader.C new file mode 100644 index 000000000..5f8e2fdc7 --- /dev/null +++ b/src/usr/secureboot/base/containerheader.C @@ -0,0 +1,248 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/secureboot/base/containerheader.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include <secureboot/containerheader.H> + +extern trace_desc_t* g_trac_secure; + +// Quick change for unit testing +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + +namespace SECUREBOOT +{ + +void ContainerHeader::parse_header(const void* i_header) +{ + assert(i_header != NULL); + const uint8_t* l_hdr = reinterpret_cast<const uint8_t*>(i_header); + + /*---- Parse ROM_container_raw ----*/ + // The rom code has a placeholder for the prefix in the first struct + size_t l_size = offsetof(ROM_container_raw, prefix); + safeMemCpyAndInc(&iv_headerInfo.hw_hdr, l_hdr, l_size); + + // Early check if magic number is valid, as a quick check to try and prevent + // any storage exceptions while parsing header. + assert(iv_headerInfo.hw_hdr.magic_number == MAGIC_NUMBER, + "ContainerHeader: magic number = 0x%08X not valid", + iv_headerInfo.hw_hdr.magic_number); + + /*---- Parse ROM_prefix_header_raw ----*/ + l_size = offsetof(ROM_prefix_header_raw, ecid); + safeMemCpyAndInc(&iv_headerInfo.hw_prefix_hdr, l_hdr, l_size); + + // Get ECID array + l_size = iv_headerInfo.hw_prefix_hdr.ecid_count * ECID_SIZE; + safeMemCpyAndInc(&iv_headerInfo.hw_prefix_hdr.ecid, l_hdr, l_size); + + /*---- Parse ROM_prefix_data_raw ----*/ + l_size = offsetof(ROM_prefix_data_raw, sw_pkey_p); + safeMemCpyAndInc(&iv_headerInfo.hw_prefix_data, l_hdr, l_size); + + // Get SW keys + l_size = iv_headerInfo.hw_prefix_hdr.sw_key_count * sizeof(ecc_key_t); + // Cache total software keys size + iv_totalSwKeysSize = l_size; + safeMemCpyAndInc(&iv_headerInfo.hw_prefix_data.sw_pkey_p, l_hdr, l_size); + + /*---- Parse ROM_sw_header_raw ----*/ + l_size = offsetof(ROM_sw_header_raw, ecid); + safeMemCpyAndInc(&iv_headerInfo.sw_hdr, l_hdr, l_size); + + // Get ECID array + l_size = iv_headerInfo.sw_hdr.ecid_count * ECID_SIZE; + safeMemCpyAndInc(&iv_headerInfo.sw_hdr.ecid, l_hdr, l_size); + + /*---- Parse ROM_sw_sig_raw ----*/ + safeMemCpyAndInc(&iv_headerInfo.sw_sig.sw_sig_p, l_hdr, iv_totalSwKeysSize); + + // Parse hw and sw flags + parseFlags(); + + // Generate hw hash key + genHwKeyHash(); + + // After parsing check if header is valid, do some quick bound checks + validate(); + + // Debug printing + print(); +} + +void ContainerHeader::print() const +{ +#ifdef HOSTBOOT_DEBUG + TRACFCOMP(g_trac_secure, ENTER_MRK"ContainerHeader::print"); + + TRACFCOMP(g_trac_secure,"header content size 0x%X", iv_hdrBytesRead); + + /*---- Print ROM_container_raw ----*/ + TRACFCOMP(g_trac_secure,"magic_number 0x%X", iv_headerInfo.hw_hdr.magic_number); + TRACFCOMP(g_trac_secure,"version 0x%X", iv_headerInfo.hw_hdr.version); + TRACFCOMP(g_trac_secure,"container_size 0x%X", iv_headerInfo.hw_hdr.container_size); + TRACFCOMP(g_trac_secure,"target_hrmor 0x%X", iv_headerInfo.hw_hdr.target_hrmor); + TRACFCOMP(g_trac_secure,"stack_pointer 0x%X", iv_headerInfo.hw_hdr.stack_pointer); + TRACFBIN(g_trac_secure,"hw_pkey_a", iv_headerInfo.hw_hdr.hw_pkey_a, 64); + TRACFBIN(g_trac_secure,"hw_pkey_b", iv_headerInfo.hw_hdr.hw_pkey_b, 64); + TRACFBIN(g_trac_secure,"hw_pkey_c", iv_headerInfo.hw_hdr.hw_pkey_c, 64); + + /*---- Print ROM_prefix_header_raw ----*/ + TRACFCOMP(g_trac_secure,"hw_flags 0x%X", iv_headerInfo.hw_prefix_hdr.flags); + TRACFCOMP(g_trac_secure,"sw_key_count 0x%X", iv_headerInfo.hw_prefix_hdr.sw_key_count); + TRACFBIN(g_trac_secure,"sw public key hash", iv_headerInfo.hw_prefix_hdr.payload_hash, SHA512_DIGEST_LENGTH); + + /*---- Print ROM_prefix_data_raw ----*/ + TRACFBIN(g_trac_secure,"sw_pkey_p", iv_headerInfo.hw_prefix_data.sw_pkey_p, sizeof(ecc_key_t)); + if (iv_headerInfo.hw_prefix_hdr.sw_key_count>1) + { + TRACFBIN(g_trac_secure,"sw_pkey_q", iv_headerInfo.hw_prefix_data.sw_pkey_q, sizeof(ecc_key_t)); + } + if (iv_headerInfo.hw_prefix_hdr.sw_key_count>2) + { + TRACFBIN(g_trac_secure,"sw_pkey_r", iv_headerInfo.hw_prefix_data.sw_pkey_r, sizeof(ecc_key_t)); + } + + /*---- Print ROM_sw_header_raw ----*/ + TRACFCOMP(g_trac_secure,"payload_size 0x%X", iv_headerInfo.sw_hdr.payload_size ); + TRACFBIN(g_trac_secure,"payload_hash", iv_headerInfo.sw_hdr.payload_hash, SHA512_DIGEST_LENGTH); + + /*---- Print ROM_sw_sig_raw ----*/ + TRACFBIN(g_trac_secure,"sw_sig_p", iv_headerInfo.sw_sig.sw_sig_p, sizeof(ecc_key_t)); + if (iv_headerInfo.hw_prefix_hdr.sw_key_count>1) + { + TRACFBIN(g_trac_secure,"sw_sig_q", iv_headerInfo.sw_sig.sw_sig_q, sizeof(ecc_key_t)); + } + if (iv_headerInfo.hw_prefix_hdr.sw_key_count>2) + { + TRACFBIN(g_trac_secure,"sw_sig_r", iv_headerInfo.sw_sig.sw_sig_r, sizeof(ecc_key_t)); + } + + TRACFCOMP(g_trac_secure, EXIT_MRK"ContainerHeader::print"); +#endif +} + +size_t ContainerHeader::totalContainerSize() const +{ + return iv_headerInfo.hw_hdr.container_size; +} + +const ecc_key_t* ContainerHeader::hw_keys() const +{ + return &iv_headerInfo.hw_hdr.hw_pkey_a; +} + +size_t ContainerHeader::payloadTextSize() const +{ + return iv_headerInfo.sw_hdr.payload_size; +} + +const SHA512_t* ContainerHeader::payloadTextHash() const +{ + return &iv_headerInfo.sw_hdr.payload_hash; +} + +size_t ContainerHeader::totalSwKeysSize() const +{ + return iv_totalSwKeysSize; +} + +const ecc_key_t* ContainerHeader::sw_keys() const +{ + return &iv_headerInfo.hw_prefix_data.sw_pkey_p; +} + +const SHA512_t* ContainerHeader::swKeyHash() const +{ + return &iv_headerInfo.hw_prefix_hdr.payload_hash; +} + +const ecc_key_t* ContainerHeader::sw_sigs() const +{ + return &iv_headerInfo.sw_sig.sw_sig_p; +} + +const sb_flags_t* ContainerHeader::sb_flags() const +{ + return &iv_sbFlags; +} + +const SHA512_t* ContainerHeader::hwKeyHash() const +{ + return &iv_hwKeyHash; +} + +void ContainerHeader::validate() +{ + iv_isValid = (iv_hdrBytesRead <= MAX_SECURE_HEADER_SIZE) + && (iv_headerInfo.hw_hdr.magic_number == MAGIC_NUMBER) + && (iv_headerInfo.hw_hdr.version == ROM_VERSION) + && (iv_headerInfo.hw_prefix_hdr.ver_alg.version == ROM_VERSION) + && (iv_headerInfo.hw_prefix_hdr.ver_alg.hash_alg == ROM_HASH_ALG) + && (iv_headerInfo.hw_prefix_hdr.ver_alg.sig_alg == ROM_SIG_ALG) + && (iv_headerInfo.hw_prefix_hdr.sw_key_count >= SW_KEY_COUNT_MIN) + && (iv_headerInfo.hw_prefix_hdr.sw_key_count <= SW_KEY_COUNT_MAX) + && (iv_headerInfo.sw_hdr.payload_size != 0); +} + +void ContainerHeader::safeMemCpyAndInc(void* i_dest, const uint8_t* &io_hdr, + const size_t i_size) +{ + assert(i_dest != NULL, "ContainerHeader: dest ptr NULL"); + assert(io_hdr != NULL, "ContainerHeader: current header location ptr NULL"); + assert(iv_pHdrStart != NULL, "ContainerHeader: start of header ptr NULL"); + + TRACDCOMP(g_trac_secure,"dest: 0x%X src: 0x%X size: 0x%X",i_dest, io_hdr, i_size); + + // Determine if the memcpy is within the bounds of the container header + iv_hdrBytesRead = io_hdr - iv_pHdrStart; + assert( (iv_hdrBytesRead + i_size) <= MAX_SECURE_HEADER_SIZE, + "ContainerHeader: memcpy is out of bounds of max header size"); + + memcpy(i_dest, io_hdr, i_size); + io_hdr += i_size; +} + +bool ContainerHeader::isValid() const +{ + return iv_isValid; +} + +void ContainerHeader::parseFlags() +{ + iv_sbFlags.hw_hb_fw = iv_headerInfo.hw_prefix_hdr.flags & HB_FW_FLAG; + iv_sbFlags.hw_opal = iv_headerInfo.hw_prefix_hdr.flags & OPAL_FLAG; + iv_sbFlags.hw_phyp = iv_headerInfo.hw_prefix_hdr.flags & PHYP_FLAG; + iv_sbFlags.hw_key_transition = iv_headerInfo.hw_prefix_hdr.flags + & KEY_TRANSITION_FLAG; +} + +void ContainerHeader::genHwKeyHash() +{ + // Generate and store hw hash key + SECUREBOOT::hashBlob(&iv_headerInfo.hw_hdr.hw_pkey_a, + totalHwKeysSize, iv_hwKeyHash); +} + +}; //end of SECUREBOOT namespace diff --git a/src/usr/secureboot/base/header.C b/src/usr/secureboot/base/header.C index 23cefcf84..37ba7ca72 100644 --- a/src/usr/secureboot/base/header.C +++ b/src/usr/secureboot/base/header.C @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2013,2014 */ +/* Contributors Listed Below - COPYRIGHT 2013,2016 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -20,13 +22,21 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ -#include "header.H" +#include <secureboot/header.H> #include <sys/mm.h> #include <sys/mmio.h> #include <kernel/console.H> namespace SECUREBOOT { + Header& baseHeader() + { + return Singleton<Header>::instance(); + } + + // TODO securebootp9 this implementation native to p9 appears to be doing + // approximately the same thing as p8's loadSecurely() method. We need to + // confirm and merge together or leave separate and merely remove comment. void Header::loadBaseHeader() { // Calculate original address of the secureboot header. @@ -54,4 +64,23 @@ namespace SECUREBOOT return; } + + // TODO securebootp9 this implementation of the follwoing two methods need + // to be added based on p8 code + void Header::loadSecurely() + { + } + + void Header::setNonSecurely( + const void* i_pHeader) + { + } + + void Header::getHeader( + const void*& o_pHeader ) const + { + // Fatal code bug if queried before loaded + assert(iv_data!=nullptr); + o_pHeader = iv_data; + } } diff --git a/src/usr/secureboot/base/makefile b/src/usr/secureboot/base/makefile index 1c13cc289..e2b143978 100644 --- a/src/usr/secureboot/base/makefile +++ b/src/usr/secureboot/base/makefile @@ -24,6 +24,7 @@ # IBM_PROLOG_END_TAG ROOTPATH = ../../../.. MODULE = secureboot_base +include ../common/common.mk SUBDIRS += test.d OBJS += service.o @@ -33,6 +34,10 @@ OBJS += securerom.o OBJS += rom_entry.o OBJS += trustedboot_base.o OBJS += $(if $(CONFIG_TPMDD),trustedbootMsg.o,) +OBJS += containerheader.o +OBJS += ${SECUREBOOT_COMMON_OBJS} + +VPATH += ../common EXTRAINCDIR += ${ROOTPATH}/src/usr/secureboot/trusted/base VPATH += ${ROOTPATH}/src/usr/secureboot/trusted/base diff --git a/src/usr/secureboot/base/securerom.C b/src/usr/secureboot/base/securerom.C index 82a72c185..dd859c6ab 100644 --- a/src/usr/secureboot/base/securerom.C +++ b/src/usr/secureboot/base/securerom.C @@ -62,23 +62,27 @@ errlHndl_t initializeSecureROM(void) return Singleton<SecureROM>::instance().initialize(); } + +// TODO securebootp9 - the method signature below was brought in from +// p8. There are many more changes need to this file however, in order to +// be considered up-to-date. /** * @brief Verify Signed Container */ -errlHndl_t verifyContainer(void * i_container, size_t i_size) +errlHndl_t verifyContainer(void * i_container, const sha2_hash_t* i_hwKeyHash) { TRACUCOMP(g_trac_secure, "verifyContainer(): i_container=%p, size=0x%x", i_container, i_size); return Singleton<SecureROM>::instance().verifyContainer(i_container, - i_size); + i_hwKeyHash); } /** * @brief Hash Signed Blob * */ -errlHndl_t hashBlob(void * i_blob, size_t i_size, SHA512_t io_buf) +errlHndl_t hashBlob(const void * i_blob, size_t i_size, SHA512_t io_buf) { return Singleton<SecureROM>::instance().hashBlob(i_blob, i_size, io_buf); @@ -297,10 +301,13 @@ errlHndl_t SecureROM::initialize() /** * @brief Verify Container against system hash keys */ -errlHndl_t SecureROM::verifyContainer(void * i_container, size_t i_size) +errlHndl_t SecureROM::verifyContainer(void * i_container, +// TODO securebootp9 - this is dummy parameter added to aid in p9 port +// need to replace the method below with up-to-date version + const sha2_hash_t* i_hwKeyHash) { TRACDCOMP(g_trac_secure,ENTER_MRK"SecureROM::verifyContainer(): " - "i_container=%p, size=0x%x", i_container, i_size); + "i_container=%p", i_container); errlHndl_t l_errl = NULL; @@ -410,7 +417,7 @@ errlHndl_t SecureROM::verifyContainer(void * i_container, size_t i_size) /** * @brief Hash Blob */ -errlHndl_t SecureROM::hashBlob(void * i_blob, size_t i_size, SHA512_t io_buf) +errlHndl_t SecureROM::hashBlob(const void * i_blob, size_t i_size, SHA512_t io_buf) const { TRACDCOMP(g_trac_secure,INFO_MRK"SecureROM::hashBlob() NOT " diff --git a/src/usr/secureboot/base/securerom.H b/src/usr/secureboot/base/securerom.H index cd5688cc4..bfb16f88b 100644 --- a/src/usr/secureboot/base/securerom.H +++ b/src/usr/secureboot/base/securerom.H @@ -139,11 +139,13 @@ class SecureROM * * @param[in] i_container Void pointer to effective address * of container - * @param[in] i_size Size of container + * @param[in] i_hwKeyHash Custom hw keys' hash to test against + * [default = nullptr, use current hw hash key * - * @return errlHndl_t NULL on success + * @return errlHndl_t NULL on success */ - errlHndl_t verifyContainer(void * i_container, size_t i_size); + errlHndl_t verifyContainer(void * i_container, + const sha2_hash_t* i_hwKeyHash = nullptr); /** * @brief Hash Blob @@ -155,7 +157,7 @@ class SecureROM * * @return errlHndl_t NULL on success */ - errlHndl_t hashBlob(void * i_blob, size_t i_size, SHA512_t io_buf); + errlHndl_t hashBlob(const void * i_blob, size_t i_size, SHA512_t io_buf) const; /** * @brief Retrieve the internal hardware hash key from secure ROM diff --git a/src/usr/secureboot/base/service.C b/src/usr/secureboot/base/service.C index 435ababfd..ade0e4a9f 100644 --- a/src/usr/secureboot/base/service.C +++ b/src/usr/secureboot/base/service.C @@ -28,50 +28,98 @@ #include <util/singleton.H> #include <secureboot/secure_reasoncodes.H> #include <config.h> - +#include <devicefw/userif.H> +#include <targeting/common/utilFilter.H> +#include <targeting/common/targetservice.H> +#include <errl/errlentry.H> +#include <errl/errlmanager.H> +#include <errl/errludtarget.H> +#include <initservice/initserviceif.H> #include "settings.H" -#include "header.H" +#include <secureboot/header.H> #include "purge.H" #include <kernel/misc.H> +#include <kernel/console.H> +#include <console/consoleif.H> + +extern trace_desc_t* g_trac_secure; + +// Quick change for unit testing +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + + +using namespace ERRORLOG; +using namespace TARGETING; namespace SECUREBOOT { - void* initializeBase(void* unused) + +// TODO securebootp9 - Do a diff of this file with the p8 version make sure +// all the missing parts are brought in. + +void* initializeBase(void* unused) +{ + errlHndl_t l_errl = NULL; + + do { - errlHndl_t l_errl = NULL; - do + // Load original secureboot header. + if (enabled()) { + Singleton<Header>::instance().loadBaseHeader(); + } - // Load original secureboot header. - if (enabled()) - { - Singleton<Header>::instance().loadBaseHeader(); - } - - // Extend memory footprint into lower portion of cache. - assert(0 == mm_extend(MM_EXTEND_PARTIAL_CACHE)); + // Extend memory footprint into lower portion of cache. + assert(0 == mm_extend(MM_EXTEND_PARTIAL_CACHE)); - // Don't extend more than 1/2 cache in VPO as fake PNOR is there - // Don't enable SecureROM in VPO + // Don't extend more than 1/2 cache in VPO as fake PNOR is there + // Don't enable SecureROM in VPO #ifndef CONFIG_P9_VPO_COMPILE - // Run dcbz on the entire 10MB cache - assert(0 == mm_extend(MM_EXTEND_FULL_CACHE)); - - // Initialize the Secure ROM - l_errl = initializeSecureROM(); - if (l_errl) - { - break; - } + // Run dcbz on the entire 10MB cache + assert(0 == mm_extend(MM_EXTEND_FULL_CACHE)); + + // Initialize the Secure ROM + l_errl = initializeSecureROM(); + if (l_errl) + { + break; + } +#endif + } while(0); + + return l_errl; +} + +bool enabled() +{ + return Singleton<Settings>::instance().getEnabled(); +} + +void handleSecurebootFailure(errlHndl_t &i_err) +{ + TRACFCOMP( g_trac_secure, ENTER_MRK"handleSecurebootFailure()"); + + assert(i_err != NULL, "Secureboot Failure has a NULL error log") + + // Grab errlog reason code before committing. + uint16_t l_rc = i_err->reasonCode(); + +#ifdef CONFIG_CONSOLE + CONSOLE::displayf(SECURE_COMP_NAME, "Secureboot Failure plid = 0x%08X, rc = 0x%04X\n", + i_err->plid(), l_rc); #endif - } while(0); + printk("Secureboot Failure plid = 0x%08X, rc = 0x%04X\n", + i_err->plid(),l_rc); - return l_errl; - } + // Add Verification callout + i_err->addProcedureCallout(HWAS::EPUB_PRC_FW_VERIFICATION_ERR, + HWAS::SRCI_PRIORITY_HIGH); + errlCommit(i_err, SECURE_COMP_ID); + + // Shutdown with Secureboot error status + INITSERVICE::doShutdown(l_rc); +} - bool enabled() - { - return Singleton<Settings>::instance().getEnabled(); - } } diff --git a/src/usr/secureboot/base/test/secureromtest.H b/src/usr/secureboot/base/test/secureromtest.H index 9de9aaa80..d324de8cc 100644 --- a/src/usr/secureboot/base/test/secureromtest.H +++ b/src/usr/secureboot/base/test/secureromtest.H @@ -136,8 +136,7 @@ class SecureROMTest : public CxxTest::TestSuite // Warn about the exception being handled during verification printkd("test_verify(): expect to see 'mfsr r2 to CFAR handled': "); - l_errl = l_sRom.verifyContainer( signedFile_pageAddr, - signedFile_size ); + l_errl = l_sRom.verifyContainer( signedFile_pageAddr); if (l_errl) { diff --git a/src/usr/secureboot/header.H b/src/usr/secureboot/header.H deleted file mode 100644 index a78f79b80..000000000 --- a/src/usr/secureboot/header.H +++ /dev/null @@ -1,53 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/usr/secureboot/header.H $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* COPYRIGHT International Business Machines Corp. 2013,2014 */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ -#ifndef __SECUREBOOT_HEADER_H -#define __SECUREBOOT_HEADER_H - -#include <stdint.h> - -/** @file header.H - * - * @brief Classes for manipulating Secureboot headers. - */ - -namespace SECUREBOOT -{ - /** @class Header - * @brief Class for storing the original Secureboot header for later use. - */ - class Header - { - public: - Header() : iv_data(NULL) {}; - ~Header() {}; - - /** @brief Extract header from original HRMOR - 1 page address. */ - void loadBaseHeader(); - - private: - /** Copy of the original secureboot header for the base image. */ - void* iv_data; - }; -}; - -#endif diff --git a/src/usr/secureboot/trusted/base/trustedboot_base.C b/src/usr/secureboot/trusted/base/trustedboot_base.C index 687aeb9f0..dbb47b6e5 100644 --- a/src/usr/secureboot/trusted/base/trustedboot_base.C +++ b/src/usr/secureboot/trusted/base/trustedboot_base.C @@ -255,4 +255,27 @@ errlHndl_t pcrExtend(TPM_Pcr i_pcr, return err; } +errlHndl_t extendPnorSectionHash(const SECUREBOOT::ContainerHeader& i_conHdr, + const void* i_vaddr, + const PNOR::SectionId i_sec) +{ + errlHndl_t l_errhdl = NULL; + + // TODO securebootp9 + // remove the following code and implement based on p8 code + TRACFCOMP(g_trac_trustedboot, "ExtendPnorSectionHash called for section %d and " + " address %.16llX with payload text size %i" + "but not unimplemented in p9", i_sec, i_vaddr); + + return l_errhdl; +} + +errlHndl_t extendBaseImage() +{ + errlHndl_t pError = NULL; + // TODO securebootp9 + // implement extendBaseImage based on p8 code + return pError; +} + } // end TRUSTEDBOOT |

