core/manifest/pfm/pfm_flash.c (1,276 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stddef.h> #include <stdlib.h> #include <string.h> #include "pfm_flash.h" #include "pfm_format.h" #include "platform_api.h" #include "common/buffer_util.h" #include "common/unused.h" #include "flash/flash_util.h" #include "manifest/manifest_flash.h" /** * Static array indicating the manifest contains no firmware identifiers. */ static const char *NO_FW_IDS[] = {NULL}; /** * Check a v2 formatted PFM for required elements. * * @param pfm_flash The v2 PFM to check. This must have already been verified to be valid. * @param hash Hash engine to use for element verification. * * @return 0 if the PFM is structured correctly or an error code. */ static int pfm_flash_verify_v2_elements (const struct pfm_flash *pfm_flash, const struct hash_engine *hash) { uint8_t format; uint8_t *element = (uint8_t*) &pfm_flash->state->flash_dev; int status; status = manifest_flash_read_element_data (&pfm_flash->base_flash, hash, PFM_FLASH_DEVICE, 0, MANIFEST_NO_PARENT, 0, NULL, &format, NULL, &element, sizeof (pfm_flash->state->flash_dev)); if (ROT_IS_ERROR (status) && (status != MANIFEST_ELEMENT_NOT_FOUND)) { return status; } if ((size_t) status < sizeof (pfm_flash->state->flash_dev)) { return PFM_MALFORMED_FLASH_DEV_ELEMENT; } if (status != MANIFEST_ELEMENT_NOT_FOUND) { pfm_flash->state->flash_dev_format = format; } else { pfm_flash->state->flash_dev_format = -1; } return 0; } int pfm_flash_verify (const struct manifest *pfm, const struct hash_engine *hash, const struct signature_verification *verification, uint8_t *hash_out, size_t hash_length) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; int status; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } status = manifest_flash_verify (&pfm_flash->base_flash, hash, verification, hash_out, hash_length); if (status != 0) { return status; } if (pfm_flash->state->base.header.magic == PFM_MAGIC_NUM) { struct pfm_allowable_firmware_header fw_section; struct pfm_key_manifest_header key_section; struct pfm_platform_header platform_section; uint32_t next_addr; /* Check the contents of the PFM to make sure they make sense. */ next_addr = pfm_flash->base_flash.addr + sizeof (struct manifest_header); status = pfm_flash->base_flash.flash->read (pfm_flash->base_flash.flash, next_addr, (uint8_t*) &fw_section, sizeof (fw_section)); if (status != 0) { goto error; } next_addr += fw_section.length; status = pfm_flash->base_flash.flash->read (pfm_flash->base_flash.flash, next_addr, (uint8_t*) &key_section, sizeof (key_section)); if (status != 0) { goto error; } next_addr += key_section.length; status = pfm_flash->base_flash.flash->read (pfm_flash->base_flash.flash, next_addr, (uint8_t*) &platform_section, sizeof (platform_section)); if (status != 0) { goto error; } if (platform_section.id_length > pfm_flash->base_flash.max_platform_id) { status = MANIFEST_PLAT_ID_BUFFER_TOO_SMALL; goto error; } if (pfm_flash->state->base.header.length != (sizeof (struct manifest_header) + fw_section.length + key_section.length + platform_section.length + pfm_flash->state->base.header.sig_length)) { status = MANIFEST_MALFORMED; goto error; } next_addr += sizeof (struct pfm_platform_header); status = pfm_flash->base_flash.flash->read (pfm_flash->base_flash.flash, next_addr, (uint8_t*) pfm_flash->base_flash.platform_id, platform_section.id_length); if (status != 0) { goto error; } pfm_flash->base_flash.platform_id[platform_section.id_length] = '\0'; } else { status = pfm_flash_verify_v2_elements (pfm_flash, hash); if (status != 0) { goto error; } } return 0; error: pfm_flash->state->base.manifest_valid = false; return status; } int pfm_flash_verify_v2_only (const struct manifest *pfm, const struct hash_engine *hash, const struct signature_verification *verification, uint8_t *hash_out, size_t hash_length) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; int status; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } status = manifest_flash_v2_verify (&pfm_flash->base_flash, hash, verification, hash_out, hash_length); if (status != 0) { return status; } status = pfm_flash_verify_v2_elements (pfm_flash, hash); if (status != 0) { goto error; } return 0; error: pfm_flash->state->base.manifest_valid = false; return status; } int pfm_flash_get_id (const struct manifest *pfm, uint32_t *id) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } return manifest_flash_get_id (&pfm_flash->base_flash, id); } int pfm_flash_get_platform_id (const struct manifest *pfm, char **id, size_t length) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } return manifest_flash_get_platform_id (&pfm_flash->base_flash, id, length); } void pfm_flash_free_platform_id (const struct manifest *manifest, char *id) { UNUSED (manifest); UNUSED (id); /* Don't need to do anything. Manifest allocated buffers use the internal static buffer. */ } int pfm_flash_get_hash (const struct manifest *pfm, const struct hash_engine *hash, uint8_t *hash_out, size_t hash_length) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } return manifest_flash_get_hash (&pfm_flash->base_flash, hash, hash_out, hash_length); } int pfm_flash_get_signature (const struct manifest *pfm, uint8_t *signature, size_t length) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } return manifest_flash_get_signature (&pfm_flash->base_flash, signature, length); } int pfm_flash_is_empty (const struct manifest *pfm) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; int status; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } if (pfm_flash->state->base.header.magic == PFM_MAGIC_NUM) { uint8_t check; status = pfm_flash->base.buffer_supported_versions (&pfm_flash->base, NULL, 0, 1, &check); if ((status == 0) || (status == 1)) { status = !status; } } else { status = (pfm_flash->state->flash_dev_format < 0) || (pfm_flash->state->flash_dev.fw_count == 0); } return status; } int pfm_flash_is_empty_v2_only (const struct manifest *pfm) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if (pfm_flash == NULL) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } return (pfm_flash->state->flash_dev_format < 0) || (pfm_flash->state->flash_dev.fw_count == 0); } void pfm_flash_free_firmware (const struct pfm *pfm, struct pfm_firmware *fw) { size_t i; UNUSED (pfm); if ((fw != NULL) && (fw->ids != NULL) && (fw->ids != NO_FW_IDS)) { for (i = 0; i < fw->count; i++) { platform_free ((void*) fw->ids[i]); } platform_free (fw->ids); memset (fw, 0, sizeof (*fw)); } } /** * Get the list of firmware components in a v1 formatted PFM. * * @param pfm The PFM to query. * @param fw Output for the the list of firmware. * * @return Always succeeds and returns 0. */ static int pfm_flash_get_firmware_v1 (const struct pfm_flash *pfm, struct pfm_firmware *fw) { UNUSED (pfm); fw->ids = NO_FW_IDS; fw->count = 1; return 0; } /** * Read the next firmware element in a v2 formatted PFM. * * @param pfm The PFM to query. * @param entry On input, the entry to start searching. On output, the entry that was read. * @param fw_element Output for the firmware element data. * * @return 0 if the element was successfully read or an error code. */ static int pfm_flash_read_firmware_element_v2 (const struct pfm_flash *pfm, uint8_t *entry, struct pfm_firmware_element *fw_element) { uint8_t *element = (uint8_t*) fw_element; int id_pad; int status; status = manifest_flash_read_element_data (&pfm->base_flash, pfm->base_flash.hash, PFM_FIRMWARE, *entry, MANIFEST_NO_PARENT, 0, entry, NULL, NULL, &element, sizeof (*fw_element)); if (ROT_IS_ERROR (status)) { return status; } if ((size_t) status < (sizeof (*fw_element) - sizeof (fw_element->id))) { return PFM_MALFORMED_FIRMWARE_ELEMENT; } id_pad = fw_element->id_length % 4; if (id_pad != 0) { id_pad = 4 - id_pad; } if ((size_t) status < ((sizeof (*fw_element) - sizeof (fw_element->id)) + fw_element->id_length + id_pad)) { return PFM_MALFORMED_FIRMWARE_ELEMENT; } return 0; } /** * Get the list of firmware components in a v2 formatted PFM. * * @param pfm The PFM to query. * @param fw Output for the list of firmware. * * @return 0 if the list was generated successfully or an error code. */ static int pfm_flash_get_firmware_v2 (const struct pfm_flash *pfm, struct pfm_firmware *fw) { struct pfm_firmware_element fw_element; uint8_t last = 0; size_t i; int status; if ((pfm->state->flash_dev_format >= 0) && (pfm->state->flash_dev.fw_count != 0)) { fw->count = pfm->state->flash_dev.fw_count; fw->ids = platform_calloc (fw->count, sizeof (char*)); if (fw->ids == NULL) { return PFM_NO_MEMORY; } for (i = 0; i < fw->count; i++, last++) { status = pfm_flash_read_firmware_element_v2 (pfm, &last, &fw_element); if (status != 0) { goto error; } fw_element.id[fw_element.id_length] = '\0'; fw->ids[i] = strdup ((char*) fw_element.id); if (fw->ids[i] == NULL) { status = PFM_NO_MEMORY; goto error; } } } else { memset (fw, 0, sizeof (*fw)); } return 0; error: pfm_flash_free_firmware (&pfm->base, fw); return status; } int pfm_flash_get_firmware (const struct pfm *pfm, struct pfm_firmware *fw) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (fw == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } if (pfm_flash->state->base.header.magic == PFM_MAGIC_NUM) { return pfm_flash_get_firmware_v1 (pfm_flash, fw); } else { return pfm_flash_get_firmware_v2 (pfm_flash, fw); } } int pfm_flash_get_firmware_v2_only (const struct pfm *pfm, struct pfm_firmware *fw) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (fw == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } return pfm_flash_get_firmware_v2 (pfm_flash, fw); } void pfm_flash_free_fw_versions (const struct pfm *pfm, struct pfm_firmware_versions *ver_list) { size_t i; UNUSED (pfm); if ((ver_list != NULL) && (ver_list->versions != NULL)) { for (i = 0; i < ver_list->count; i++) { platform_free ((void*) ver_list->versions[i].fw_version_id); } platform_free ((void*) ver_list->versions); memset (ver_list, 0, sizeof (*ver_list)); } } /** * Get the list of supported firmware versions from a v1 formatted PFM. * * @param pfm The PFM to query. * @param ver_list Output for the list of supported firmware versions. Null to buffer the output. * @param offset Offset to start buffering version strings. * @param length Maximum length of version strings to buffer. * @param ver_out Output for buffering the list of versions. Not used if allocating a list. * @param bytes Output for the number of bytes that were buffered. * * @return 0 if the version list was successfully generated or an error code. */ static int pfm_flash_get_supported_versions_v1 (const struct pfm_flash *pfm, struct pfm_firmware_versions *ver_list, size_t offset, size_t length, uint8_t *ver_out, int *bytes) { struct manifest_header header; struct pfm_allowable_firmware_header fw_section; struct pfm_firmware_header fw_header; struct pfm_firmware_version *version_list = NULL; uint8_t version_str[MANIFEST_MAX_STRING]; int i; uint32_t next_addr; int status; status = pfm->base_flash.flash->read (pfm->base_flash.flash, pfm->base_flash.addr, (uint8_t*) &header, sizeof (header)); if (status != 0) { return status; } if (header.magic != PFM_MAGIC_NUM) { return MANIFEST_BAD_MAGIC_NUMBER; } status = pfm->base_flash.flash->read (pfm->base_flash.flash, pfm->base_flash.addr + sizeof (struct manifest_header), (uint8_t*) &fw_section, sizeof (fw_section)); if (status != 0) { return status; } if (fw_section.fw_count == 0) { if (ver_list) { memset (ver_list, 0, sizeof (*ver_list)); } return 0; } if (ver_list) { version_list = platform_calloc (fw_section.fw_count, sizeof (struct pfm_firmware_version)); if (version_list == NULL) { return PFM_NO_MEMORY; } ver_list->versions = version_list; ver_list->count = fw_section.fw_count; } next_addr = pfm->base_flash.addr + sizeof (struct manifest_header) + sizeof (struct pfm_allowable_firmware_header); i = 0; while ((i < fw_section.fw_count) && (length > 0)) { status = pfm->base_flash.flash->read (pfm->base_flash.flash, next_addr, (uint8_t*) &fw_header, sizeof (fw_header)); if (status != 0) { goto error; } status = pfm->base_flash.flash->read (pfm->base_flash.flash, next_addr + sizeof (struct pfm_firmware_header), version_str, fw_header.version_length); if (status != 0) { goto error; } version_str[fw_header.version_length] = '\0'; if (ver_list) { version_list[i].version_addr = fw_header.version_addr; version_list[i].blank_byte = fw_header.blank_byte; version_list[i].fw_version_id = strdup ((char*) version_str); if (version_list[i].fw_version_id == NULL) { status = PFM_NO_MEMORY; goto error; } } else { *bytes += buffer_copy (version_str, fw_header.version_length + 1, &offset, &length, &ver_out[*bytes]); } next_addr += fw_header.length; i++; } return 0; error: pfm_flash_free_fw_versions (&pfm->base, ver_list); return status; } /** * Find the firmware element for the specified ID. The PFM must be in v2 format. * * @param pfm The PFM to query. * @param fw The firmware ID to find. This can be null to return the first firmware element. * @param fw_element Output for the firmware element data. * @param entry Output for the entry index following the matching firmware element. * * @return 0 if the firmware element was found or an error code. */ static int pfm_flash_find_firmware_element_v2 (const struct pfm_flash *pfm, const char *fw, struct pfm_firmware_element *fw_element, uint8_t *entry) { int status; *entry = 0; do { status = pfm_flash_read_firmware_element_v2 (pfm, entry, fw_element); if (status != 0) { if (status == MANIFEST_ELEMENT_NOT_FOUND) { return PFM_UNKNOWN_FIRMWARE; } else { return status; } } (*entry)++; fw_element->id[fw_element->id_length] = '\0'; } while ((fw != NULL) && (strcmp (fw, (char*) fw_element->id) != 0)); return 0; } /** * Read the next firmware version element in a v2 formatted PFM. * * @param pfm The PFM to query. * @param entry On input, the entry to start searching. On output, the entry that was read. * @param ver_element Output for the firmware version element data. * @param element_len Optional output for the amount of element data read. * * @return 0 if the element was successfully read or an error code. */ static int pfm_flash_read_firmware_version_element_v2 (const struct pfm_flash *pfm, uint8_t *entry, struct pfm_firmware_version_element *ver_element, size_t *element_len) { uint8_t *element = (uint8_t*) ver_element; size_t ver_len; int ver_pad; int status; status = manifest_flash_read_element_data (&pfm->base_flash, pfm->base_flash.hash, PFM_FIRMWARE_VERSION, *entry, PFM_FIRMWARE, 0, entry, NULL, &ver_len, &element, sizeof (*ver_element)); if (ROT_IS_ERROR (status)) { return status; } if ((size_t) status < (sizeof (*ver_element) - (sizeof (ver_element->version)))) { return PFM_MALFORMED_FW_VER_ELEMENT; } ver_pad = ver_element->version_length % 4; if (ver_pad != 0) { ver_pad = 4 - ver_pad; } if (ver_len < ((sizeof (*ver_element) - sizeof (ver_element->version)) + ver_element->version_length + ver_pad + (sizeof (struct pfm_fw_version_element_rw_region) * ver_element->rw_count))) { return PFM_MALFORMED_FW_VER_ELEMENT; } if (element_len) { *element_len = status; } return 0; } /** * Get the list of supported firmware versions from a v2 formatted PFM. * * @param pfm The PFM to query. * @param fw The firmware ID to query. This can be null to default to the first firmware ID. * @param ver_list Output for the list of supported firmware versions. Null to buffer the output. * @param offset Offset to start buffering version strings. Updated on output. * @param length Maximum length of version strings to buffer. Updated on output. * @param ver_out Output for buffering the list of versions. Not used if allocating a list. * @param bytes Output for the number of bytes that were buffered. * * @return 0 if the version list was successfully generated or an error code. */ static int pfm_flash_get_supported_versions_v2 (const struct pfm_flash *pfm, const char *fw, struct pfm_firmware_versions *ver_list, size_t *offset, size_t *length, uint8_t *ver_out, int *bytes) { union { struct pfm_firmware_element fw_element; struct pfm_firmware_version_element ver_element; } buffer; struct pfm_firmware_version *version_list = NULL; uint8_t entry; int i; int count; int status; if ((pfm->state->flash_dev_format < 0) || (pfm->state->flash_dev.fw_count == 0)) { if (fw == NULL) { if (ver_list) { memset (ver_list, 0, sizeof (*ver_list)); } return 0; } else { return PFM_UNKNOWN_FIRMWARE; } } status = pfm_flash_find_firmware_element_v2 (pfm, fw, &buffer.fw_element, &entry); if (status != 0) { return status; } if (buffer.fw_element.version_count == 0) { if (ver_list) { memset (ver_list, 0, sizeof (*ver_list)); } return 0; } count = buffer.fw_element.version_count; if (ver_list) { ver_list->count = count; version_list = platform_calloc (ver_list->count, sizeof (struct pfm_firmware_version)); if (version_list == NULL) { return PFM_NO_MEMORY; } ver_list->versions = version_list; } i = 0; while ((i < count) && ((length == NULL) || (*length > 0))) { status = pfm_flash_read_firmware_version_element_v2 (pfm, &entry, &buffer.ver_element, NULL); if (status != 0) { goto error; } buffer.ver_element.version[buffer.ver_element.version_length] = '\0'; if (ver_list) { version_list[i].blank_byte = pfm->state->flash_dev.blank_byte; version_list[i].version_addr = buffer.ver_element.version_addr; version_list[i].fw_version_id = strdup ((char*) buffer.ver_element.version); if (version_list[i].fw_version_id == NULL) { status = PFM_NO_MEMORY; goto error; } } else { *bytes += buffer_copy (buffer.ver_element.version, buffer.ver_element.version_length + 1, offset, length, &ver_out[*bytes]); } i++; entry++; } return 0; error: pfm_flash_free_fw_versions (&pfm->base, ver_list); return status; } int pfm_flash_get_supported_versions (const struct pfm *pfm, const char *fw, struct pfm_firmware_versions *ver_list) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (ver_list == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } if (pfm_flash->state->base.header.magic == PFM_MAGIC_NUM) { return pfm_flash_get_supported_versions_v1 (pfm_flash, ver_list, 0, 1, NULL, NULL); } else { return pfm_flash_get_supported_versions_v2 (pfm_flash, fw, ver_list, NULL, NULL, NULL, NULL); } } int pfm_flash_get_supported_versions_v2_only (const struct pfm *pfm, const char *fw, struct pfm_firmware_versions *ver_list) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (ver_list == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } return pfm_flash_get_supported_versions_v2 (pfm_flash, fw, ver_list, NULL, NULL, NULL, NULL); } /** * Populate a buffer with the list of supported firmware versions from a v2 formatted PFM. * * @param pfm The PFM to query. * @param fw The firmware to query. If this is null, all firmware components will be queried * and the firmware IDs will also be stored in the buffer. * @param offset The offset within the overall list of firmware versions that should be * returned. * @param length The maximum length of version information that should be returned. * @param ver_list Output buffer for the list of supported versions. * @param bytes_out Output for the number of bytes that were buffered. * * @return 0 if the list was populated successfully or an error code. */ static int pfm_flash_buffer_supported_versions_v2 (const struct pfm_flash *pfm, const char *fw, size_t offset, size_t length, uint8_t *ver_list, int *bytes_out) { struct pfm_firmware fw_list; int bytes = 0; int status; size_t i; if (fw == NULL) { status = pfm_flash_get_firmware_v2 (pfm, &fw_list); if (status != 0) { return status; } i = 0; while ((i < fw_list.count) && (length > 0) && (status == 0)) { bytes += buffer_copy ((const uint8_t*) fw_list.ids[i], strlen (fw_list.ids[i]) + 1, &offset, &length, &ver_list[bytes]); if (length > 0) { status = pfm_flash_get_supported_versions_v2 (pfm, fw_list.ids[i], NULL, &offset, &length, ver_list, &bytes); } i++; } pfm_flash_free_firmware (&pfm->base, &fw_list); } else { status = pfm_flash_get_supported_versions_v2 (pfm, fw, NULL, &offset, &length, ver_list, &bytes); } *bytes_out = bytes; return status; } int pfm_flash_buffer_supported_versions (const struct pfm *pfm, const char *fw, size_t offset, size_t length, uint8_t *ver_list) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; int bytes = 0; int status; if ((pfm_flash == NULL) || (ver_list == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } if (pfm_flash->state->base.header.magic == PFM_MAGIC_NUM) { if (fw != NULL) { return PFM_UNKNOWN_FIRMWARE; } status = pfm_flash_get_supported_versions_v1 (pfm_flash, NULL, offset, length, ver_list, &bytes); } else { status = pfm_flash_buffer_supported_versions_v2 (pfm_flash, fw, offset, length, ver_list, &bytes); } return (status == 0) ? bytes : status; } int pfm_flash_buffer_supported_versions_v2_only (const struct pfm *pfm, const char *fw, size_t offset, size_t length, uint8_t *ver_list) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; int bytes = 0; int status; if ((pfm_flash == NULL) || (ver_list == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } status = pfm_flash_buffer_supported_versions_v2 (pfm_flash, fw, offset, length, ver_list, &bytes); return (status == 0) ? bytes : status; } /** * Find the version entry in the PFM that matches the expected version identifier. The PFM must be * in v1 format. * * @param pfm The PFM instance to search. * @param version The version identifier to find. * @param fw_header The output buffer for the firmware header of the matching version. * @param fw_addr This will be updated with the address offset of the matching firmware header. * @param manifest_addr This will be updated with the address offset of the key manifest in the PFM. * This can be NULL to not return this information. * * @return 0 if a matching entry was found or an error code. */ static int pfm_flash_find_version_entry_v1 (const struct manifest_flash *pfm, const char *version, struct pfm_firmware_header *fw_header, uint32_t *fw_addr, uint32_t *manifest_addr) { struct manifest_header header; struct pfm_allowable_firmware_header fw_section; char *check; size_t check_len; int i; int status; uint8_t found; check_len = strlen (version); if (check_len == 0) { return PFM_INVALID_ARGUMENT; } *fw_addr = pfm->addr; status = pfm->flash->read (pfm->flash, *fw_addr, (uint8_t*) &header, sizeof (header)); if (status != 0) { return status; } if (header.magic != PFM_MAGIC_NUM) { return MANIFEST_BAD_MAGIC_NUMBER; } *fw_addr += sizeof (struct manifest_header); status = pfm->flash->read (pfm->flash, *fw_addr, (uint8_t*) &fw_section, sizeof (fw_section)); if (status != 0) { return status; } if (manifest_addr != NULL) { *manifest_addr = *fw_addr + fw_section.length; } check = platform_malloc (check_len + 1); if (check == NULL) { return PFM_NO_MEMORY; } i = 0; found = 0; *fw_addr += sizeof (struct pfm_allowable_firmware_header); while (!found && (i < fw_section.fw_count)) { status = pfm->flash->read (pfm->flash, *fw_addr, (uint8_t*) fw_header, sizeof (*fw_header)); if (status != 0) { goto error; } if (fw_header->version_length == check_len) { status = pfm->flash->read (pfm->flash, *fw_addr + sizeof (struct pfm_firmware_header), (uint8_t*) check, fw_header->version_length); if (status != 0) { goto error; } check[fw_header->version_length] = '\0'; if (strcmp (version, check) == 0) { found = 1; } } if (!found) { *fw_addr += fw_header->length; i++; } } platform_free (check); if (!found) { return PFM_UNSUPPORTED_VERSION; } return 0; error: platform_free (check); return status; } /** * Read a flash region definition from flash. * * @param pfm The PFM instance to read. * @param addr The address of the region definition in flash. * @param region The region information that will be updated from flash. * * @return 0 if the region was read successfully or an error code. */ static int pfm_flash_read_region_v1 (const struct manifest_flash *pfm, uint32_t addr, struct flash_region *region) { struct pfm_flash_region rw_region; int status; status = pfm->flash->read (pfm->flash, addr, (uint8_t*) &rw_region, sizeof (rw_region)); if (status != 0) { return status; } region->start_addr = rw_region.start_addr; region->length = (rw_region.end_addr - rw_region.start_addr) + 1; return 0; } /** * Read multiple flash region definitions from flash. * * @param pfm The PFM instance to read. * @param count The number of regions to read. * @param region_list The list of regions to populate with data from flash. * @param addr The starting address to read from. This will be updated with the address after the * last read region definition. * * @return 0 if the flash regions were read successfully or an error code. */ static int pfm_flash_read_multiple_regions_v1 (const struct manifest_flash *pfm, size_t count, struct flash_region *region_list, uint32_t *addr) { size_t i; int status; for (i = 0; i < count; i++) { status = pfm_flash_read_region_v1 (pfm, *addr, &region_list[i]); if (status != 0) { return status; } *addr += sizeof (struct pfm_flash_region); } return 0; } void pfm_flash_free_read_write_regions (const struct pfm *pfm, struct pfm_read_write_regions *writable) { UNUSED (pfm); if (writable != NULL) { platform_free ((void*) writable->regions); platform_free ((void*) writable->properties); memset (writable, 0, sizeof (*writable)); } } /** * Get the list of read/write regions for a firmware version from a v1 formatted PFM. * * @param pfm The PFM to query. * @param version The firmware version to query. * @param writable Output for the list of read/write regions. * * @return 0 if the list was successfully generated or an error code. */ static int pfm_flash_get_read_write_regions_v1 (const struct pfm_flash *pfm, const char *version, struct pfm_read_write_regions *writable) { struct pfm_firmware_header fw_header; struct flash_region *region_list; uint32_t next_addr; int status; status = pfm_flash_find_version_entry_v1 (&pfm->base_flash, version, &fw_header, &next_addr, NULL); if (status != 0) { return status; } region_list = platform_calloc (fw_header.rw_count, sizeof (struct flash_region)); if (region_list == NULL) { return PFM_NO_MEMORY; } writable->regions = region_list; writable->count = fw_header.rw_count; writable->properties = platform_calloc (fw_header.rw_count, sizeof (struct pfm_read_write)); if (writable->properties == NULL) { status = PFM_NO_MEMORY; goto error; } next_addr += sizeof (struct pfm_firmware_header) + fw_header.version_length; if ((fw_header.version_length % 4) != 0) { next_addr += (4 - (fw_header.version_length % 4)); } status = pfm_flash_read_multiple_regions_v1 (&pfm->base_flash, fw_header.rw_count, region_list, &next_addr); if (status != 0) { goto error; } return 0; error: pfm_flash_free_read_write_regions (&pfm->base, writable); return status; } /** * Find the firmware version element for the specified ID. The PFM must be in v2 format. * * @param pfm The PFM to query. * @param version The version ID to find. * @param entry On input, the entry index to start searching. On output, the entry index for the * firmware version element. * @param ver_element Output for the firmware version element data. * @param element_len Optional output for the amount of element data read. * * @return 0 if the firmware version element was found or an error code. */ static int pfm_flash_find_firmware_version_element_v2 (const struct pfm_flash *pfm, const char *version, uint8_t *entry, struct pfm_firmware_version_element *ver_element, size_t *element_len) { uint8_t temp; int status; do { status = pfm_flash_read_firmware_version_element_v2 (pfm, entry, ver_element, element_len); if (status != 0) { if (status == MANIFEST_CHILD_NOT_FOUND) { return PFM_UNSUPPORTED_VERSION; } else { return status; } } (*entry)++; temp = ver_element->version[ver_element->version_length]; ver_element->version[ver_element->version_length] = '\0'; } while (strcmp (version, (char*) ver_element->version) != 0); /* Restore the modified byte to ensure the original data is returned in the buffer. */ ver_element->version[ver_element->version_length] = temp; (*entry)--; return 0; } /** * Get the list of read/write regions for a firmware version from a v2 formatted PFM. * * @param pfm The PFM to query. * @param fw The firmware ID to query. This can be null to default to the first firmware ID. * @param version The firmware version to query. * @param writable Output for the list of read/write regions. * * @return 0 if the list was successfully generated or an error code. */ static int pfm_flash_get_read_write_regions_v2 (const struct pfm_flash *pfm, const char *fw, const char *version, struct pfm_read_write_regions *writable) { union { struct pfm_firmware_element fw_element; struct pfm_firmware_version_element ver_element; } buffer; struct pfm_fw_version_element_rw_region *rw_region; struct flash_region *region_list; struct pfm_read_write *prop_list; uint8_t *element; uint8_t entry; size_t i; int version_pad; int rw_len; uint32_t offset; int status; if ((pfm->state->flash_dev_format < 0) || (pfm->state->flash_dev.fw_count == 0)) { return PFM_UNKNOWN_FIRMWARE; } status = pfm_flash_find_firmware_element_v2 (pfm, fw, &buffer.fw_element, &entry); if (status != 0) { return status; } status = pfm_flash_find_firmware_version_element_v2 (pfm, version, &entry, &buffer.ver_element, NULL); if (status != 0) { return status; } if (buffer.ver_element.rw_count == 0) { memset (writable, 0, sizeof (*writable)); return 0; } rw_len = buffer.ver_element.rw_count * sizeof (struct pfm_fw_version_element_rw_region); if (rw_len > MANIFEST_MAX_STRING) { /* We do not have enough buffer available to read all R/W region information in a single * read. We could loop through reading one buffer's worth at a time, but this is overly * complicated and unnecessary. That many R/W regions is unrealistic and doesn't need to be * supported. */ return PFM_READ_WRITE_UNSUPPORTED; } version_pad = buffer.ver_element.version_length % 4; if (version_pad != 0) { version_pad = 4 - version_pad; } if ((MANIFEST_MAX_STRING - (buffer.ver_element.version_length + version_pad)) < rw_len) { /* The R/W region definitions are not contained within the first element read. Reuse the * version buffer to read the element again, starting at the R/W regions. */ element = buffer.ver_element.version; offset = (sizeof (buffer.ver_element) - sizeof (buffer.ver_element.version)) + buffer.ver_element.version_length + version_pad; status = manifest_flash_read_element_data (&pfm->base_flash, pfm->base_flash.hash, PFM_FIRMWARE_VERSION, entry, PFM_FIRMWARE, offset, NULL, NULL, NULL, &element, sizeof (buffer.ver_element.version)); if (ROT_IS_ERROR (status)) { return status; } buffer.ver_element.version_length = 0; version_pad = 0; } writable->count = buffer.ver_element.rw_count; writable->regions = platform_calloc (writable->count, sizeof (struct flash_region)); if (writable->regions == NULL) { return PFM_NO_MEMORY; } writable->properties = platform_calloc (writable->count, sizeof (struct pfm_read_write)); if (writable->properties == NULL) { status = PFM_NO_MEMORY; goto error; } region_list = (struct flash_region*) writable->regions; prop_list = (struct pfm_read_write*) writable->properties; rw_region = (struct pfm_fw_version_element_rw_region*) &buffer.ver_element.version[buffer.ver_element.version_length + version_pad]; for (i = 0; i < writable->count; i++) { if (rw_region[i].region.end_addr <= rw_region[i].region.start_addr) { status = PFM_MALFORMED_FW_VER_ELEMENT; goto error; } region_list[i].start_addr = rw_region[i].region.start_addr; region_list[i].length = (rw_region[i].region.end_addr - rw_region[i].region.start_addr) + 1; prop_list[i].on_failure = pfm_get_rw_operation_on_failure (&rw_region[i]); } return 0; error: pfm_flash_free_read_write_regions (&pfm->base, writable); return status; } int pfm_flash_get_read_write_regions (const struct pfm *pfm, const char *fw, const char *version, struct pfm_read_write_regions *writable) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (version == NULL) || (writable == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } if (pfm_flash->state->base.header.magic == PFM_MAGIC_NUM) { return pfm_flash_get_read_write_regions_v1 (pfm_flash, version, writable); } else { return pfm_flash_get_read_write_regions_v2 (pfm_flash, fw, version, writable); } } int pfm_flash_get_read_write_regions_v2_only (const struct pfm *pfm, const char *fw, const char *version, struct pfm_read_write_regions *writable) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (version == NULL) || (writable == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } return pfm_flash_get_read_write_regions_v2 (pfm_flash, fw, version, writable); } void pfm_flash_free_firmware_images (const struct pfm *pfm, struct pfm_image_list *img_list) { size_t i; UNUSED (pfm); if (img_list != NULL) { if (img_list->images_sig != NULL) { for (i = 0; i < img_list->count; i++) { platform_free ((void*) img_list->images_sig[i].regions); } platform_free ((void*) img_list->images_sig); } if (img_list->images_hash != NULL) { for (i = 0; i < img_list->count; i++) { platform_free ((void*) img_list->images_hash[i].regions); } platform_free ((void*) img_list->images_hash); } memset (img_list, 0, sizeof (*img_list)); } } /** * Get the list of signed images for a version of firmware from a v1 formatted PFM. * * @param pfm The PFM to query. * @param version The firmware version to query. * @param img_list Output for the list of signed images. * * @return 0 if the list was successfully generated or an error code. */ static int pfm_flash_get_firmware_images_v1 (const struct pfm_flash *pfm, const char *version, struct pfm_image_list *img_list) { struct pfm_firmware_header fw_header; struct pfm_image_header img_header; struct pfm_key_manifest_header key_section; struct pfm_public_key_header key_header; struct rsa_public_key key; struct pfm_image_signature *images; struct flash_region *region_list = NULL; uint32_t next_addr; uint32_t key_addr; int status; int i; int j; int matched; status = pfm_flash_find_version_entry_v1 (&pfm->base_flash, version, &fw_header, &next_addr, &key_addr); if (status != 0) { return status; } images = platform_calloc (fw_header.img_count, sizeof (struct pfm_image_signature)); if (images == NULL) { return PFM_NO_MEMORY; } img_list->images_hash = NULL; img_list->images_sig = images; img_list->count = fw_header.img_count; next_addr += sizeof (struct pfm_firmware_header) + fw_header.version_length + (sizeof (struct pfm_flash_region) * fw_header.rw_count); if ((fw_header.version_length % 4) != 0) { next_addr += 4 - (fw_header.version_length % 4); } for (i = 0; i < fw_header.img_count; i++) { status = pfm->base_flash.flash->read (pfm->base_flash.flash, next_addr, (uint8_t*) &img_header, sizeof (img_header)); if (status != 0) { goto error; } if (img_header.sig_length > sizeof (images[i].signature)) { status = PFM_FW_IMAGE_UNSUPPORTED; goto error; } region_list = platform_calloc (img_header.region_count, sizeof (struct flash_region)); if (region_list == NULL) { status = PFM_NO_MEMORY; goto error; } images[i].regions = region_list; images[i].count = img_header.region_count; images[i].always_validate = !!(img_header.flags & PFM_IMAGE_MUST_VALIDATE); images[i].sig_length = img_header.sig_length; images[i].key.mod_length = (0xffU << 24) | img_header.key_id; next_addr += sizeof (struct pfm_image_header); status = pfm->base_flash.flash->read (pfm->base_flash.flash, next_addr, images[i].signature, img_header.sig_length); if (status != 0) { goto error; } next_addr += img_header.sig_length; status = pfm_flash_read_multiple_regions_v1 (&pfm->base_flash, img_header.region_count, region_list, &next_addr); if (status != 0) { goto error; } } status = pfm->base_flash.flash->read (pfm->base_flash.flash, key_addr, (uint8_t*) &key_section, sizeof (key_section)); if (status != 0) { goto error; } i = 0; matched = 0; next_addr = key_addr + sizeof (struct pfm_key_manifest_header); while ((i < key_section.key_count) && (matched < fw_header.img_count)) { status = pfm->base_flash.flash->read (pfm->base_flash.flash, next_addr, (uint8_t*) &key_header, sizeof (key_header)); if (status != 0) { goto error; } if (key_header.key_length > sizeof (key.modulus)) { status = PFM_KEY_UNSUPPORTED; goto error; } next_addr += sizeof (struct pfm_public_key_header); status = pfm->base_flash.flash->read (pfm->base_flash.flash, next_addr, key.modulus, key_header.key_length); if (status != 0) { goto error; } key.exponent = key_header.key_exponent; key.mod_length = key_header.key_length; for (j = 0; j < fw_header.img_count; j++) { if (((images[j].key.mod_length >> 24) & 0xff) == 0xff) { if ((images[j].key.mod_length & 0xff) == key_header.id) { images[j].key = key; matched++; } } } i++; next_addr += key_header.key_length; } if (matched < fw_header.img_count) { status = PFM_UNKNOWN_KEY_ID; goto error; } return 0; error: pfm_flash_free_firmware_images (&pfm->base, img_list); return status; } /** * Determine the total length of a image definition within a firmware version element. * * @param img The image header. * * @return The total length of the image description. */ static uint32_t pfm_flash_get_image_length_v2 (const struct pfm_fw_version_element_image *img) { uint32_t length = sizeof (*img) + (sizeof (struct pfm_flash_region) * img->region_count); switch (img->hash_type) { case MANIFEST_HASH_SHA256: length += SHA256_HASH_LENGTH; break; case MANIFEST_HASH_SHA384: length += SHA384_HASH_LENGTH; break; case MANIFEST_HASH_SHA512: length += SHA512_HASH_LENGTH; break; } return length; } /** * Get the list of signed images for a version of firmware from a v2 formatted PFM. * * @param pfm The PFM to query. * @param fw The firmware ID to query. This can be null to default to the first firmware ID. * @param version The firmware version to query. * @param img_list Output for the list of signed images. * * @return 0 if the list was successfully generated or an error code. */ static int pfm_flash_get_firmware_images_v2 (const struct pfm_flash *pfm, const char *fw, const char *version, struct pfm_image_list *img_list) { union { struct pfm_firmware_element fw_element; struct pfm_firmware_version_element ver_element; } buffer; struct pfm_fw_version_element_image *img; struct pfm_flash_region *img_regions; struct pfm_image_hash *images; struct flash_region *region_list; uint8_t *element; uint8_t entry; size_t element_len; int version_pad; uint32_t offset; uint32_t buf_offset; size_t img_len; size_t i; size_t j; int status; if ((pfm->state->flash_dev_format < 0) || (pfm->state->flash_dev.fw_count == 0)) { return PFM_UNKNOWN_FIRMWARE; } status = pfm_flash_find_firmware_element_v2 (pfm, fw, &buffer.fw_element, &entry); if (status != 0) { return status; } status = pfm_flash_find_firmware_version_element_v2 (pfm, version, &entry, &buffer.ver_element, &element_len); if (status != 0) { return status; } img_list->count = buffer.ver_element.img_count; img_list->images_sig = NULL; img_list->images_hash = platform_calloc (img_list->count, sizeof (struct pfm_image_hash)); if (img_list->images_hash == NULL) { return PFM_NO_MEMORY; } version_pad = buffer.ver_element.version_length % 4; if (version_pad != 0) { version_pad = 4 - version_pad; } offset = (sizeof (buffer.ver_element) - sizeof (buffer.ver_element.version)) + buffer.ver_element.version_length + version_pad + (sizeof (struct pfm_fw_version_element_rw_region) * buffer.ver_element.rw_count); buf_offset = offset - (sizeof (buffer.ver_element) - sizeof (buffer.ver_element.version)); element_len -= (sizeof (buffer.ver_element) - sizeof (buffer.ver_element.version)); for (i = 0; i < img_list->count; i++) { img = (struct pfm_fw_version_element_image*) &buffer.ver_element.version[buf_offset]; if (buf_offset >= sizeof (buffer.ver_element.version)) { img = NULL; } else if ((buf_offset + pfm_flash_get_image_length_v2 (img)) > sizeof (buffer.ver_element.version)) { img = NULL; } if (img == NULL) { /* The complete image definition does not reside in memory. We may have part of the * image information or none of it. Either way, use the version buffer to read the * element again starting at the beginning of the image information. */ element = buffer.ver_element.version; status = manifest_flash_read_element_data (&pfm->base_flash, pfm->base_flash.hash, PFM_FIRMWARE_VERSION, entry, PFM_FIRMWARE, offset, NULL, NULL, NULL, &element, sizeof (buffer.ver_element.version)); if (ROT_IS_ERROR (status)) { goto error; } element_len = status; buf_offset = 0; img = (struct pfm_fw_version_element_image*) buffer.ver_element.version; } if ((element_len - buf_offset) < sizeof (struct pfm_fw_version_element_image)) { status = PFM_MALFORMED_FW_VER_ELEMENT; goto error; } if (img->region_count == 0) { status = PFM_FW_IMAGE_UNSUPPORTED; goto error; } img_len = pfm_flash_get_image_length_v2 (img); if (img_len > sizeof (buffer.ver_element.version)) { /* We cannot read the entire image information in a single read, so we cannot support * this image. Handling larger image definitions would greatly complicate processing * for very little gain. An image that needs to be defined with that many regions would * be an extremely rare case. If it does happen, it can be worked around by adding more * image definitions with separate hashes instead of using many regions and a single * hash. */ status = PFM_FW_IMAGE_UNSUPPORTED; goto error; } if ((element_len - buf_offset) < img_len) { status = PFM_MALFORMED_FW_VER_ELEMENT; goto error; } buf_offset += sizeof (struct pfm_fw_version_element_image); images = (struct pfm_image_hash*) img_list->images_hash; images[i].count = img->region_count; images[i].regions = platform_calloc (images[i].count, sizeof (struct flash_region)); if (images[i].regions == NULL) { status = PFM_NO_MEMORY; goto error; } region_list = (struct flash_region*) images[i].regions; switch (img->hash_type) { case MANIFEST_HASH_SHA256: images[i].hash_type = HASH_TYPE_SHA256; images[i].hash_length = SHA256_HASH_LENGTH; break; case MANIFEST_HASH_SHA384: images[i].hash_type = HASH_TYPE_SHA384; images[i].hash_length = SHA384_HASH_LENGTH; break; case MANIFEST_HASH_SHA512: images[i].hash_type = HASH_TYPE_SHA512; images[i].hash_length = SHA512_HASH_LENGTH; break; default: status = PFM_UNKNOWN_HASH_TYPE; goto error; } memcpy (images[i].hash, &buffer.ver_element.version[buf_offset], images[i].hash_length); images[i].always_validate = img->flags & PFM_IMAGE_MUST_VALIDATE; buf_offset += images[i].hash_length; img_regions = (struct pfm_flash_region*) &buffer.ver_element.version[buf_offset]; for (j = 0; j < images[i].count; j++) { if (img_regions[j].end_addr <= img_regions[j].start_addr) { status = PFM_MALFORMED_FW_VER_ELEMENT; goto error; } region_list[j].start_addr = img_regions[j].start_addr; region_list[j].length = (img_regions[j].end_addr - img_regions[j].start_addr) + 1; } buf_offset += (sizeof (struct pfm_flash_region) * images[i].count); offset += img_len; } return 0; error: pfm_flash_free_firmware_images (&pfm->base, img_list); return status; } int pfm_flash_get_firmware_images (const struct pfm *pfm, const char *fw, const char *version, struct pfm_image_list *img_list) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (version == NULL) || (img_list == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } if (pfm_flash->state->base.header.magic == PFM_MAGIC_NUM) { return pfm_flash_get_firmware_images_v1 (pfm_flash, version, img_list); } else { return pfm_flash_get_firmware_images_v2 (pfm_flash, fw, version, img_list); } } int pfm_flash_get_firmware_images_v2_only (const struct pfm *pfm, const char *fw, const char *version, struct pfm_image_list *img_list) { const struct pfm_flash *pfm_flash = (const struct pfm_flash*) pfm; if ((pfm_flash == NULL) || (version == NULL) || (img_list == NULL)) { return PFM_INVALID_ARGUMENT; } if (!pfm_flash->state->base.manifest_valid) { return MANIFEST_NO_MANIFEST; } return pfm_flash_get_firmware_images_v2 (pfm_flash, fw, version, img_list); } /** * Initialize the interface to a PFM residing in flash memory. Both PFM version 1 and 2 will be * supported. * * @param pfm The PFM instance to initialize. * @param state Variable context for the PFM instance. This must be uninitialized. * @param flash The flash device that contains the PFM. * @param hash A hash engine to use for validating run-time access to PFM information. If it is * possible for any PFM information to be requested concurrently by different threads, this hash * engine MUST be thread-safe. There is no internal synchronization around the hashing operations. * @param base_addr The starting address of the PFM storage location. * @param signature_cache Buffer to hold the manifest signature. * @param max_signature The maximum supported length for a manifest signature. * @param platform_id_cache Buffer to hold the manifest platform ID. * @param max_platform_id The maximum platform ID length supported, including the NULL terminator. * * @return 0 if the PFM instance was initialized successfully or an error code. */ int pfm_flash_init (struct pfm_flash *pfm, struct pfm_flash_state *state, const struct flash *flash, const struct hash_engine *hash, uint32_t base_addr, uint8_t *signature_cache, size_t max_signature, uint8_t *platform_id_cache, size_t max_platform_id) { int status; if ((pfm == NULL) || (state == NULL)) { return PFM_INVALID_ARGUMENT; } memset (pfm, 0, sizeof (struct pfm_flash)); status = manifest_flash_v2_init (&pfm->base_flash, &state->base, flash, hash, base_addr, PFM_MAGIC_NUM, PFM_V2_MAGIC_NUM, signature_cache, max_signature, platform_id_cache, max_platform_id); if (status != 0) { return status; } pfm->base.base.verify = pfm_flash_verify; pfm->base.base.get_id = pfm_flash_get_id; pfm->base.base.get_platform_id = pfm_flash_get_platform_id; pfm->base.base.free_platform_id = pfm_flash_free_platform_id; pfm->base.base.get_hash = pfm_flash_get_hash; pfm->base.base.get_signature = pfm_flash_get_signature; pfm->base.base.is_empty = pfm_flash_is_empty; pfm->base.get_firmware = pfm_flash_get_firmware; pfm->base.free_firmware = pfm_flash_free_firmware; pfm->base.get_supported_versions = pfm_flash_get_supported_versions; pfm->base.free_fw_versions = pfm_flash_free_fw_versions; pfm->base.buffer_supported_versions = pfm_flash_buffer_supported_versions; pfm->base.get_read_write_regions = pfm_flash_get_read_write_regions; pfm->base.free_read_write_regions = pfm_flash_free_read_write_regions; pfm->base.get_firmware_images = pfm_flash_get_firmware_images; pfm->base.free_firmware_images = pfm_flash_free_firmware_images; pfm->state = state; return 0; } /** * Initialize the interface to a PFM residing in flash memory. Only PFM version 2 will be * supported. * * @param pfm The PFM instance to initialize. * @param state Variable context for the PFM instance. This must be uninitialized. * @param flash The flash device that contains the PFM. * @param hash A hash engine to use for validating run-time access to PFM information. If it is * possible for any PFM information to be requested concurrently by different threads, this hash * engine MUST be thread-safe. There is no internal synchronization around the hashing operations. * @param base_addr The starting address of the PFM storage location. * @param signature_cache Buffer to hold the manifest signature. * @param max_signature The maximum supported length for a manifest signature. * @param platform_id_cache Buffer to hold the manifest platform ID. * @param max_platform_id The maximum platform ID length supported, including the NULL terminator. * * @return 0 if the PFM instance was initialized successfully or an error code. */ int pfm_flash_v2_init (struct pfm_flash *pfm, struct pfm_flash_state *state, const struct flash *flash, const struct hash_engine *hash, uint32_t base_addr, uint8_t *signature_cache, size_t max_signature, uint8_t *platform_id_cache, size_t max_platform_id) { int status; if ((pfm == NULL) || (state == NULL)) { return PFM_INVALID_ARGUMENT; } memset (pfm, 0, sizeof (struct pfm_flash)); status = manifest_flash_v2_init (&pfm->base_flash, &state->base, flash, hash, base_addr, MANIFEST_NOT_SUPPORTED, PFM_V2_MAGIC_NUM, signature_cache, max_signature, platform_id_cache, max_platform_id); if (status != 0) { return status; } pfm->base.base.verify = pfm_flash_verify_v2_only; pfm->base.base.get_id = pfm_flash_get_id; pfm->base.base.get_platform_id = pfm_flash_get_platform_id; pfm->base.base.free_platform_id = pfm_flash_free_platform_id; pfm->base.base.get_hash = pfm_flash_get_hash; pfm->base.base.get_signature = pfm_flash_get_signature; pfm->base.base.is_empty = pfm_flash_is_empty_v2_only; pfm->base.get_firmware = pfm_flash_get_firmware_v2_only; pfm->base.free_firmware = pfm_flash_free_firmware; pfm->base.get_supported_versions = pfm_flash_get_supported_versions_v2_only; pfm->base.free_fw_versions = pfm_flash_free_fw_versions; pfm->base.buffer_supported_versions = pfm_flash_buffer_supported_versions_v2_only; pfm->base.get_read_write_regions = pfm_flash_get_read_write_regions_v2_only; pfm->base.free_read_write_regions = pfm_flash_free_read_write_regions; pfm->base.get_firmware_images = pfm_flash_get_firmware_images_v2_only; pfm->base.free_firmware_images = pfm_flash_free_firmware_images; pfm->state = state; memset (pfm->state, 0, sizeof (*pfm->state)); return 0; } /** * Initialize only the variable state for a PFM on flash. The rest of the handler is assumed to * have already been initialized. * * This would generally be used with a statically initialized instance. * * @param pfm The PFM that contains the state to initialize. * * @return 0 if the state was successfully initialized or an error code. */ int pfm_flash_init_state (const struct pfm_flash *pfm) { if ((pfm == NULL) || (pfm->state == NULL)) { return PFM_INVALID_ARGUMENT; } memset (pfm->state, 0, sizeof (*pfm->state)); return manifest_flash_init_state (&pfm->base_flash); } /** * Release the resources used by the PFM interface. * * @param pfm The PFM instance to release. */ void pfm_flash_release (const struct pfm_flash *pfm) { if (pfm != NULL) { manifest_flash_release (&pfm->base_flash); } }