core/manifest/pcd/pcd_flash.c (328 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 "pcd_flash.h"
#include "pcd_format.h"
#include "platform_api.h"
#include "cmd_interface/device_manager.h"
#include "common/buffer_util.h"
#include "common/unused.h"
#include "flash/flash_util.h"
#include "manifest/manifest_flash.h"
int pcd_flash_verify (const struct manifest *pcd, const struct hash_engine *hash,
const struct signature_verification *verification, uint8_t *hash_out, size_t hash_length)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
if (pcd_flash == NULL) {
return PCD_INVALID_ARGUMENT;
}
/* PCD only supports v2 manifests. */
return manifest_flash_v2_verify (&pcd_flash->base_flash, hash, verification, hash_out,
hash_length);
}
int pcd_flash_get_id (const struct manifest *pcd, uint32_t *id)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
if (pcd_flash == NULL) {
return PCD_INVALID_ARGUMENT;
}
return manifest_flash_get_id (&pcd_flash->base_flash, id);
}
int pcd_flash_get_platform_id (const struct manifest *pcd, char **id, size_t length)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
if (pcd_flash == NULL) {
return PCD_INVALID_ARGUMENT;
}
return manifest_flash_get_platform_id (&pcd_flash->base_flash, id, length);
}
void pcd_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 pcd_flash_get_hash (const struct manifest *pcd, const struct hash_engine *hash,
uint8_t *hash_out, size_t hash_length)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
if (pcd_flash == NULL) {
return PCD_INVALID_ARGUMENT;
}
return manifest_flash_get_hash (&pcd_flash->base_flash, hash, hash_out, hash_length);
}
int pcd_flash_get_signature (const struct manifest *pcd, uint8_t *signature, size_t length)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
if (pcd_flash == NULL) {
return PCD_INVALID_ARGUMENT;
}
return manifest_flash_get_signature (&pcd_flash->base_flash, signature, length);
}
int pcd_flash_is_empty (const struct manifest *pcd)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
if (pcd_flash == NULL) {
return PCD_INVALID_ARGUMENT;
}
if (!pcd_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
/* Every PCD must have a platform ID. If that is all we have, then it is an empty manifest. */
return (pcd_flash->base_flash.state->toc_header.entry_count == 1);
}
/**
* Helper function that grabs RoT element information from PCD.
*
* @param pcd The PCD instance to utilize.
* @param rot_element_ptr Pointer to a pcd_rot_element instance.
* @param found Optional buffer to contain index of RoT element if found, set to NULL if unused.
* @param format Output describing the format version of the RoT element.
*
* @return 0 if completed successfully or an error code.
*/
static int pcd_flash_get_rot_element_ptr (const struct pcd *pcd, uint8_t *rot_element_ptr,
uint8_t *found, uint8_t *format)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
int status;
status = manifest_flash_read_element_data (&pcd_flash->base_flash, pcd_flash->base_flash.hash,
PCD_ROT, 0, MANIFEST_NO_PARENT, 0, found, format, NULL, &rot_element_ptr,
sizeof (struct pcd_rot_element_v2));
if (ROT_IS_ERROR (status)) {
return status;
}
if (((*format == 1) && (status < (int) (sizeof (struct pcd_rot_element_v1)))) ||
((*format >= 2) && (status < (int) (sizeof (struct pcd_rot_element_v2))))) {
return PCD_MALFORMED_ROT_ELEMENT;
}
return 0;
}
int pcd_flash_get_rot_info (const struct pcd *pcd, struct pcd_rot_info *info)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
struct pcd_rot_element_v2 rot_element;
uint8_t format;
int status;
if ((pcd_flash == NULL) || (info == NULL)) {
return PCD_INVALID_ARGUMENT;
}
if (!pcd_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
status = pcd_flash_get_rot_element_ptr (pcd, (uint8_t*) &rot_element, NULL, &format);
if (status != 0) {
return status;
}
// Set default fields for unused fields in v1 format
if (format == 1) {
rot_element.attestation_success_retry = PCD_FLASH_ATTESTATION_SUCCESS_RETRY_DEFAULT;
rot_element.attestation_fail_retry = PCD_FLASH_ATTESTATION_FAIL_RETRY_DEFAULT;
rot_element.discovery_fail_retry = PCD_FLASH_DISCOVERY_FAIL_RETRY_DEFAULT;
rot_element.mctp_ctrl_timeout = PCD_FLASH_MCTP_CTRL_TIMEOUT_DEFAULT;
rot_element.mctp_bridge_get_table_wait = PCD_FLASH_MCTP_BRIDGE_GET_TABLE_WAIT_DEFAULT;
rot_element.mctp_bridge_additional_timeout =
PCD_FLASH_MCTP_BRIDGE_ADDITIONAL_TIMEOUT_DEFAULT;
rot_element.attestation_rsp_not_ready_max_duration =
PCD_FLASH_ATTESTATION_RSP_NOT_READY_MAX_DURATION_DEFAULT;
rot_element.attestation_rsp_not_ready_max_retry =
PCD_FLASH_ATTESTATION_RSP_NOT_READY_MAX_RETRY_DEFAULT;
}
info->is_pa_rot = (pcd_get_rot_type (&rot_element.v1) == PCD_ROT_TYPE_PA_ROT);
info->port_count = rot_element.v1.port_count;
info->components_count = rot_element.v1.components_count;
info->i2c_slave_addr = rot_element.v1.rot_address;
info->eid = rot_element.v1.rot_eid;
info->bridge_i2c_addr = rot_element.v1.bridge_address;
info->bridge_eid = rot_element.v1.bridge_eid;
info->attestation_success_retry = rot_element.attestation_success_retry;
info->attestation_fail_retry = rot_element.attestation_fail_retry;
info->discovery_fail_retry = rot_element.discovery_fail_retry;
info->mctp_ctrl_timeout = rot_element.mctp_ctrl_timeout;
info->mctp_bridge_get_table_wait = rot_element.mctp_bridge_get_table_wait;
info->mctp_bridge_additional_timeout = rot_element.mctp_bridge_additional_timeout;
info->attestation_rsp_not_ready_max_duration =
rot_element.attestation_rsp_not_ready_max_duration;
info->attestation_rsp_not_ready_max_retry = rot_element.attestation_rsp_not_ready_max_retry;
return 0;
}
int pcd_flash_get_port_info (const struct pcd *pcd, uint8_t port_id, struct pcd_port_info *info)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
struct pcd_port_element port_element;
uint8_t *port_element_ptr = (uint8_t*) &port_element;
struct pcd_rot_element_v2 rot_element;
uint8_t found;
uint8_t rot_element_format;
int start = 0;
int i_port;
int status;
if ((pcd_flash == NULL) || (info == NULL)) {
return PCD_INVALID_ARGUMENT;
}
if (!pcd_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
status = pcd_flash_get_rot_element_ptr (pcd, (uint8_t*) &rot_element, &found,
&rot_element_format);
if (status != 0) {
return status;
}
if (rot_element.v1.port_count == 0) {
return PCD_INVALID_PORT;
}
start = found + 1;
for (i_port = 0; i_port < rot_element.v1.port_count; ++i_port) {
status = manifest_flash_read_element_data (&pcd_flash->base_flash,
pcd_flash->base_flash.hash, PCD_SPI_FLASH_PORT, start, PCD_ROT, 0, &found, NULL, NULL,
&port_element_ptr, sizeof (struct pcd_port_element));
if (status == MANIFEST_CHILD_NOT_FOUND) {
return PCD_INVALID_PORT;
}
if (ROT_IS_ERROR (status)) {
return status;
}
if (((size_t) status) < (sizeof (struct pcd_port_element))) {
return PCD_MALFORMED_PORT_ELEMENT;
}
if (port_element.port_id != port_id) {
start = found + 1;
continue;
}
info->spi_freq = port_element.spi_frequency_hz;
info->flash_mode = pcd_get_port_flash_mode (&port_element);
info->reset_ctrl = pcd_get_port_reset_control (&port_element);
info->runtime_verification = pcd_get_port_runtime_verification (&port_element);
info->watchdog_monitoring = pcd_get_port_watchdog_monitoring (&port_element);
info->host_reset_action = pcd_get_port_host_reset_action (&port_element);
info->policy = port_element.policy;
info->pulse_interval = port_element.pulse_interval;
return 0;
}
return PCD_INVALID_PORT;
}
int pcd_flash_get_power_controller_info (const struct pcd *pcd,
struct pcd_power_controller_info *info)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
struct pcd_power_controller_element power_controller_element;
uint8_t *power_controller_element_ptr = (uint8_t*) &power_controller_element;
int status;
if ((pcd_flash == NULL) || (info == NULL)) {
return PCD_INVALID_ARGUMENT;
}
if (!pcd_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
status = manifest_flash_read_element_data (&pcd_flash->base_flash, pcd_flash->base_flash.hash,
PCD_POWER_CONTROLLER, 0, MANIFEST_NO_PARENT, 0, NULL, NULL, NULL,
&power_controller_element_ptr, sizeof (struct pcd_power_controller_element));
if (ROT_IS_ERROR (status)) {
return status;
}
info->mux_count = power_controller_element.i2c.mux_count;
info->i2c_mode = pcd_get_i2c_interface_i2c_mode (&power_controller_element.i2c);
info->bus = power_controller_element.i2c.bus;
info->address = power_controller_element.i2c.address;
info->eid = power_controller_element.i2c.eid;
return 0;
}
int pcd_flash_get_next_mctp_bridge_component (const struct pcd *pcd,
struct pcd_mctp_bridge_components_info *component, bool first)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
struct pcd_mctp_bridge_component_element bridge_component;
struct pcd_mctp_bridge_component_connection *connection;
uint8_t *element_ptr = (uint8_t*) &bridge_component;
uint8_t *start_ptr;
int status;
if ((pcd_flash == NULL) || (component == NULL)) {
return PCD_INVALID_ARGUMENT;
}
if (!pcd_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
start_ptr = (uint8_t*) &component->context;
if (first) {
*start_ptr = 0;
}
status = manifest_flash_read_element_data (&pcd_flash->base_flash, pcd_flash->base_flash.hash,
PCD_COMPONENT_MCTP_BRIDGE, *start_ptr, MANIFEST_NO_PARENT, 0, start_ptr, NULL, NULL,
&element_ptr, sizeof (struct pcd_mctp_bridge_component_element));
if (ROT_IS_ERROR (status)) {
return status;
}
if ((size_t) status < (sizeof (struct pcd_mctp_bridge_component_element))) {
return PCD_MALFORMED_BRIDGE_COMPONENT_ELEMENT;
}
*start_ptr = *start_ptr + 1;
connection = pcd_get_mctp_bridge_component_connection (element_ptr, status);
component->component_id = bridge_component.component.component_id;
component->components_count = connection->components_count;
component->pci_device_id = connection->device_id;
component->pci_vid = connection->vendor_id;
component->pci_subsystem_id = connection->subsystem_device_id;
component->pci_subsystem_vid = connection->subsystem_vendor_id;
return 0;
}
int pcd_flash_buffer_supported_components (const struct pcd *pcd, size_t offset, size_t length,
uint8_t *pcd_component_ids)
{
const struct pcd_flash *pcd_flash = (const struct pcd_flash*) pcd;
struct pcd_rot_info rot_info;
struct pcd_mctp_bridge_components_info component;
struct pcd_supported_component supported_component;
size_t i_components = 0;
size_t component_len = 0;
int status;
if ((pcd_flash == NULL) || (pcd_component_ids == NULL) || (length == 0)) {
return PCD_INVALID_ARGUMENT;
}
if (!pcd_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
status = pcd_flash_get_rot_info (pcd, &rot_info);
if (status != 0) {
return status;
}
while ((i_components < rot_info.components_count) && (length > 0)) {
status = pcd_flash_get_next_mctp_bridge_component (pcd, &component, (i_components == 0));
if (status != 0) {
return status;
}
supported_component.component_id = component.component_id;
supported_component.component_count = component.components_count;
component_len += buffer_copy ((uint8_t*) &supported_component, sizeof (supported_component),
&offset, &length, &pcd_component_ids[component_len]);
i_components++;
}
return component_len;
}
/**
* Initialize the interface to a PCD residing in flash memory. PCDs only support manifest version
* 2.
*
* @param pcd The PCD instance to initialize.
* @param state Variable context for the PCD instance. This must be uninitialized.
* @param flash The flash device that contains the PCD.
* @param hash A hash engine to use for validating run-time access to PCD information. If it is
* possible for any PCD 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 PCD 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 PCD instance was initialized successfully or an error code.
*/
int pcd_flash_init (struct pcd_flash *pcd, struct pcd_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 ((pcd == NULL) || (state == NULL)) {
return PCD_INVALID_ARGUMENT;
}
memset (pcd, 0, sizeof (struct pcd_flash));
status = manifest_flash_v2_init (&pcd->base_flash, &state->base, flash, hash, base_addr,
MANIFEST_NOT_SUPPORTED, PCD_V2_MAGIC_NUM, signature_cache, max_signature, platform_id_cache,
max_platform_id);
if (status != 0) {
return status;
}
pcd->base.base.verify = pcd_flash_verify;
pcd->base.base.get_id = pcd_flash_get_id;
pcd->base.base.get_platform_id = pcd_flash_get_platform_id;
pcd->base.base.free_platform_id = pcd_flash_free_platform_id;
pcd->base.base.get_hash = pcd_flash_get_hash;
pcd->base.base.get_signature = pcd_flash_get_signature;
pcd->base.base.is_empty = pcd_flash_is_empty;
pcd->base.buffer_supported_components = pcd_flash_buffer_supported_components;
pcd->base.get_next_mctp_bridge_component = pcd_flash_get_next_mctp_bridge_component;
pcd->base.get_port_info = pcd_flash_get_port_info;
pcd->base.get_rot_info = pcd_flash_get_rot_info;
pcd->base.get_power_controller_info = pcd_flash_get_power_controller_info;
return 0;
}
/**
* Initialize only the variable state for a PCD 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 pcd The PCD that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int pcd_flash_init_state (const struct pcd_flash *pcd)
{
if (pcd == NULL) {
return PCD_INVALID_ARGUMENT;
}
return manifest_flash_init_state (&pcd->base_flash);
}
/**
* Release the resources used by the PCD interface.
*
* @param pcd The PCD instance to release.
*/
void pcd_flash_release (const struct pcd_flash *pcd)
{
if (pcd != NULL) {
manifest_flash_release (&pcd->base_flash);
}
}