From 75e56cfd87740f1fa3f4de4ec557252b39f9072a Mon Sep 17 00:00:00 2001 From: Will Marone <137117762+wmaroneAMD@users.noreply.github.com> Date: Fri, 21 Jul 2023 10:58:19 -0700 Subject: [PATCH] Libcaliptra (#496) * libcaliptra Derived from the api example in hw-model/c-binding/examples/api, this is the initial skeleton of a more generic client API for Caliptra, intended to abstract away the details of the implementation. Client applications will include caliptra_api.h and implementations will provide definitions to the functions declared in caliptra_if.h, check the README.md for more information. * Add support for integration with the model This adds a generic reference to demonstrate client application use of the Caliptra C API, and an example implementation of an interface, in this case connecting the generic reference application to the hardware model. Build happens from within the examples/hwmodel/ directory. Run `make` and then run `./hwmodel` to execute the example. This also removes the more basic if_test directory as it is now redundant --- .github/workflows/build-test.yml | 3 + hw-model/c-binding/examples/Makefile | 2 +- libcaliptra/.gitignore | 5 + libcaliptra/Makefile | 27 +++ libcaliptra/README.md | 41 ++++ libcaliptra/examples/README.md | 23 ++ libcaliptra/examples/generic/main.c | 63 +++++ libcaliptra/examples/generic/main.mk | 31 +++ libcaliptra/examples/hwmodel/Makefile | 68 ++++++ libcaliptra/examples/hwmodel/README.md | 11 + libcaliptra/examples/hwmodel/interface.c | 125 ++++++++++ libcaliptra/inc/caliptra_api.h | 69 ++++++ libcaliptra/inc/caliptra_if.h | 40 ++++ libcaliptra/src/caliptra_api.c | 280 +++++++++++++++++++++++ libcaliptra/src/caliptra_fuses.h | 20 ++ libcaliptra/src/caliptra_mbox.h | 82 +++++++ 16 files changed, 889 insertions(+), 1 deletion(-) create mode 100644 libcaliptra/.gitignore create mode 100644 libcaliptra/Makefile create mode 100644 libcaliptra/README.md create mode 100644 libcaliptra/examples/README.md create mode 100644 libcaliptra/examples/generic/main.c create mode 100644 libcaliptra/examples/generic/main.mk create mode 100644 libcaliptra/examples/hwmodel/Makefile create mode 100644 libcaliptra/examples/hwmodel/README.md create mode 100644 libcaliptra/examples/hwmodel/interface.c create mode 100644 libcaliptra/inc/caliptra_api.h create mode 100644 libcaliptra/inc/caliptra_if.h create mode 100644 libcaliptra/src/caliptra_api.c create mode 100644 libcaliptra/src/caliptra_fuses.h create mode 100644 libcaliptra/src/caliptra_mbox.h diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 6e9303ee3b..be1fcf0939 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -125,3 +125,6 @@ jobs: git submodule update --init (cd hw-model/c-binding/examples && make run) + - name: Caliptra C API Hwmodel Integration Test + run: | + (cd libcaliptra/examples/hwmodel && make && ./hwmodel) diff --git a/hw-model/c-binding/examples/Makefile b/hw-model/c-binding/examples/Makefile index 24d0b01d2d..a4a1928da8 100644 --- a/hw-model/c-binding/examples/Makefile +++ b/hw-model/c-binding/examples/Makefile @@ -34,7 +34,7 @@ $(OUT)/caliptra_model.h: $(OUT)/%.o: $(SOURCE) $(MKDIR) - $(CC) ${CFLAGS} -c $< -o $@ + $(CC) ${CFLAGS} -g -c $< -o $@ $(TARGET): $(OUT)/caliptra_model.h $(OBJS) $(CC) -o $(TARGET) $(OBJS) $(CFLAGS) -Wl,-L$(OUT)/debug -lcaliptra_hw_model_c_binding -lpthread -lstdc++ -ldl -lm diff --git a/libcaliptra/.gitignore b/libcaliptra/.gitignore new file mode 100644 index 0000000000..417d1e2e42 --- /dev/null +++ b/libcaliptra/.gitignore @@ -0,0 +1,5 @@ +inc/caliptra_model.h +src/caliptra_api.o +libcaliptra/examples/generic/main.o +libcaliptra/examples/hwmodel/hwmodel +libcaliptra/examples/hwmodel/interface.o diff --git a/libcaliptra/Makefile b/libcaliptra/Makefile new file mode 100644 index 0000000000..8b5b5bdcb1 --- /dev/null +++ b/libcaliptra/Makefile @@ -0,0 +1,27 @@ +Q=@ + +ifneq ($(MAKECMDGOALS),clean) +ifndef RTL_SOC_IFC_INCLUDE_PATH +$(error RTL_SOC_IFC_INCLUDE_PATH must be defined and point to a location where caliptra_top_reg.h can be found) +endif +endif + +LIBCALIPTRA = libcaliptra.a + +SOURCE += src/caliptra_api.c +OBJS := $(patsubst %.c,%.o, $(filter %.c,$(SOURCE))) + +INCLUDES = -I$(RTL_SOC_IFC_INCLUDE_PATH) +INCLUDES += -Iinc + +$(LIBCALIPTRA): $(OBJS) + @echo [AR] $@ + $(Q)ar -cq $@ $(OBJS) + +%.o: %.c + @echo [CC] $< \-\> $@ + $(Q)$(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -g -c $< -o $@ + +clean: + @echo [CLEAN] $(OBJS) $(LIBCALIPTRA) + $(Q)rm -f $(OBJS) $(LIBCALIPTRA) diff --git a/libcaliptra/README.md b/libcaliptra/README.md new file mode 100644 index 0000000000..b1a66ac4c0 --- /dev/null +++ b/libcaliptra/README.md @@ -0,0 +1,41 @@ +# libcaliptra + +## Purpose + +libcaliptra is an abstraction layer between SoC applications and the Caliptra implementation in hardware. + +## Structure + +libcaliptra exists in two parts, the API and the Interface. + +### API + +Specified in caliptra_api.h and defined in caliptra_api.c + +Provides abstract APIs and functionality to SoC applications, independent of hardware details. + +### IF + +Specified in caliptra_if.h and used by caliptra_api.c + +The caliptra implementation must supply the definitions for the functions named in caliptra_if.h + +## Build + +To compile the API, the following must be provided: + +* Standard C headers +* Access to the caliptra_top_reg.h header + +Run `make RTL_SOC_IFC_INCLUDE_PATH=` to generate libcaliptra.a + +## Link + +To link the API, the following must be provided: + +* A main application utilizing these functions +* An interface implementation + +## Implementation and consumer examples + +See examples/README.md for details on specific examples. diff --git a/libcaliptra/examples/README.md b/libcaliptra/examples/README.md new file mode 100644 index 0000000000..1c889be0da --- /dev/null +++ b/libcaliptra/examples/README.md @@ -0,0 +1,23 @@ +# Caliptra C API Examples + +In this directory you will find a basic example on how to interact with the +Caliptra C API and adapt it to your target platform. + +## Generic + +`generic/` + +The generic example contains the main() function, basic Caliptra startup, and firmware interaction. + +> NOTE: The current test executes a command that is ignored by ROM and not yet implemented by firmware. + +## hwmodel + +`hwmodel/` + +This is an implementation of the Caliptra C API Interface functions that target the hardware model. It abstracts out model specific details including: +* ROM and Firmware image file opening +** The paths are set at compile time, see the Makefile for details +* Model-specific behavior (loading of ROM) +* Model object management + diff --git a/libcaliptra/examples/generic/main.c b/libcaliptra/examples/generic/main.c new file mode 100644 index 0000000000..844db4aa42 --- /dev/null +++ b/libcaliptra/examples/generic/main.c @@ -0,0 +1,63 @@ +// Licensed under the Apache-2.0 license +#include +#include +#include +#include +#include +#include + +#include "caliptra_api.h" + +// Interface defined values +extern struct caliptra_fuses fuses; // Device-specific location of Caliptra fuse data +extern struct caliptra_buffer image_bundle; // Device-specific location of Caliptra firmware + +int main(int argc, char *argv[]) +{ + int status; + + fuses = (struct caliptra_fuses){0}; + + if ((status = caliptra_init_fuses(&fuses)) != 0) + { + printf("Failed to init fuses: %d\n", status); + return status; + } + + // Initialize FSM GO + caliptra_bootfsm_go(); + + // Wait until ready for FW + caliptra_ready_for_firmware(); + + // Load Image Bundle + // FW_PATH is defined on the compiler command line + caliptra_upload_fw(&image_bundle); + + uint32_t FIPS_VERSION = 0x46505652; + + int mb_result; + uint32_t fips_ver; + struct caliptra_buffer buf = { + .data = (uint8_t*)&fips_ver, + .len = sizeof(fips_ver), + }; + + // Run Until RT is ready to receive commands + while(1) { + caliptra_wait(); + mb_result = caliptra_mailbox_execute(FIPS_VERSION, &buf, NULL); + + if (mb_result != -EIO) + { + printf("Caliptra C API Integration Test Failed: %x\n", mb_result); + return -1; + } + + break; + } + printf("Caliptra C API Integration Test Passed \n"); + return 0; +} + + diff --git a/libcaliptra/examples/generic/main.mk b/libcaliptra/examples/generic/main.mk new file mode 100644 index 0000000000..6a775de8e9 --- /dev/null +++ b/libcaliptra/examples/generic/main.mk @@ -0,0 +1,31 @@ +Q=@ + +SOURCE += ../generic/main.c ../../src/caliptra_api.c + +LIBCALIPTRA_ROOT = ../.. +LIBCALIPTRA_INC = $(LIBCALIPTRA_ROOT)/inc + +OBJS := $(patsubst %.c,%.o, $(filter %.c,$(SOURCE))) + +# SOC REFERENCE +RTL_SOC_IFC_INCLUDE_PATH = ../../../hw-latest/caliptra-rtl/src/soc_ifc/rtl + +# INCLUDES +INCLUDES += -I$(RTL_SOC_IFC_INCLUDE_PATH) -I$(LIBCALIPTRA_INC) + +.PHONY = run clean + +$(TARGET): $(OBJS) $(DEPS) + @echo [LINK] $(TARGET) + $(Q)$(CC) -o $(TARGET) $(OBJS) $(CFLAGS) + +$(CALIPTRA_API): + $(Q)make -C ../../ + +%.o: %.c $(DEPS) + @echo [CC] $< \-\> $@ + $(Q)$(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -g -c $< -o $@ + +clean: + @echo [CLEAN] $(OBJS) $(TARGET) + $(Q)rm -f $(OBJS) $(TARGET) diff --git a/libcaliptra/examples/hwmodel/Makefile b/libcaliptra/examples/hwmodel/Makefile new file mode 100644 index 0000000000..e437d9891f --- /dev/null +++ b/libcaliptra/examples/hwmodel/Makefile @@ -0,0 +1,68 @@ +TARGET = hwmodel + +.DEFAULT_GOAL = $(TARGET) + +LIBCALIPTRA_ROOT = ../../ +LIBCALIPTRA_INC = + +OUTPUT_DIR = ../../../target/debug + +# ROM AND FW DIR +ROM_FW_DIR = $(OUTPUT_DIR) + +ROM_FILE = $(ROM_FW_DIR)/caliptra_rom.bin +FW_FILE = $(ROM_FW_DIR)/image_bundle.bin + +BUILDER_PATH = ../../../builder + +# ROM AND FW FILES +# +# These paths are encoded into the binary so the generic +# main sources don't need a command line. +DEFINES = -DROM_PATH=\"$(ROM_FILE)\" +DEFINES += -DFW_PATH=\"$(FW_FILE)\" + +# HW MODEL +HWMODEL_DIR = $(OUTPUT_DIR) +HWMODEL_HEADER_DIR = ../../../hw-model/c-binding/out +HWMODEL_INCLUDE = -I$(HWMODEL_HEADER_DIR) +HWMODEL_LIB = -Wl,-L$(HWMODEL_DIR) -lcaliptra_hw_model_c_binding +HWMODEL_FLAGS = -lpthread -lstdc++ -ldl -lm +HWMODEL_HEADER = $(HWMODEL_HEADER_DIR)/caliptra_model.h +HWMODEL_BINDING_LIB_OBJ = $(HWMODEL_DIR)/libcaliptra_hw_model_c_binding.a + +# DEPENDENCIES +DEPS += $(HWMODEL_BINDING_LIB_OBJ) $(HWMODEL_HEADER) $(ROM_FILE) $(FW_FILE) + +# INCLUDES +INCLUDES += $(HWMODEL_INCLUDE) + +SOURCE += interface.c + +CFLAGS += $(HWMODEL_INCLUDE) $(HWMODEL_LIB) $(HWMODEL_FLAGS) + +$(ROM_FILE) $(FW_FILE): + @echo [IMAGE] caliptra_rom.bin image_bundle.bin + $(Q)make -C ../../../rom/dev + $(Q)cd ../../../runtime && ./build.sh + $(Q)cargo --config="$(EXTRA_CARGO_CONFIG)" run --manifest-path=$(BUILDER_PATH)/Cargo.toml --bin image -- --rom $(ROM_FW_DIR)/caliptra_rom.bin --fw $(ROM_FW_DIR)/image_bundle.bin + +$(HWMODEL_BINDING_LIB_OBJ): + @echo "[CARGO] c-binding" + $(Q)cd ../../../hw-model/c-binding + $(Q)cargo build + +EXTRA_CARGO_CONFIG = target.'cfg(all())'.rustflags = [\"-Dwarnings\"] + +$(TARGET): $(ROM_FILE) $(FW_FILE) + +$(HWMODEL_HEADER): + @echo "[CARGO] hw-model" + $(Q)cd ../../../hw-model + $(Q)cargo --config="$(EXTRA_CARGO_CONFIG)" build + +run: $(TARGET) + @echo [RUN] $(TARGET) + $(Q)./$(TARGET) + +include ../generic/main.mk diff --git a/libcaliptra/examples/hwmodel/README.md b/libcaliptra/examples/hwmodel/README.md new file mode 100644 index 0000000000..41442a5e39 --- /dev/null +++ b/libcaliptra/examples/hwmodel/README.md @@ -0,0 +1,11 @@ +# hwmodel + +This example defines the Caliptra interface functions, allowing the generic application to communicate with the hardware model without having to directly manage model-specific details. + +# Prerequisites + +The c-binding, rom, and firmware must all be built. + +# Build + +Running "make" will compile the Caliptra C API, the interface, and link the application against the C binding. diff --git a/libcaliptra/examples/hwmodel/interface.c b/libcaliptra/examples/hwmodel/interface.c new file mode 100644 index 0000000000..edc8389636 --- /dev/null +++ b/libcaliptra/examples/hwmodel/interface.c @@ -0,0 +1,125 @@ +//Licensed under the Apache-2.0 license + +#define HWMODEL 1 + +#include +#include +#include +#include +#include +#include + +#include "caliptra_model.h" +#include "caliptra_api.h" + +#define CALIPTRA_STATUS_OK 0 + +// Implementation specifics + +struct caliptra_model_init_params init_params; +struct caliptra_fuses fuses = {0}; +struct caliptra_buffer image_bundle; + +static bool caliptra_model_init_complete = false; + +static struct caliptra_buffer read_file_or_exit(const char* path) +{ + // Open File in Read Only Mode + FILE *fp = fopen(path, "r"); + if (!fp) { + printf("Cannot find file %s \n", path); + exit(-ENOENT); + } + + struct caliptra_buffer buffer = {0}; + + // Get File Size + fseek(fp, 0L, SEEK_END); + buffer.len = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + // Allocate Buffer Memory + buffer.data = malloc(buffer.len); + if (!buffer.data) { + printf("Cannot allocate memory for buffer->data \n"); + exit(-ENOMEM); + } + + // Read Data in Buffer + fread((char *)buffer.data, buffer.len, 1, fp); + + return buffer; +} + +struct caliptra_model* hwmod_get_or_init(void) +{ + const char *rom_path = ROM_PATH; + const char *fw_path = FW_PATH; + static struct caliptra_model *model = NULL; + + if (model == NULL) + { + // Initialize Params + // HW model only + // ROM_PATH is defined on the compiler command line + struct caliptra_model_init_params init_params = { + .rom = read_file_or_exit(rom_path), + .dccm = {.data = NULL, .len = 0}, + .iccm = {.data = NULL, .len = 0}, + }; + + image_bundle = (struct caliptra_buffer)read_file_or_exit(fw_path); + + int status = caliptra_model_init_default(init_params, &model); + } + + return model; +} + +// Memory + +/** + * caliptra_write_u32 + * + * Writes a uint32_t value to the specified address. + * + * @param[in] address Memory address to write + * @param[in] data Data to write at address + * + * @return 0 if successful, other if error (TBD) + */ +int caliptra_write_u32(uint32_t address, uint32_t data) +{ + struct caliptra_model *m = hwmod_get_or_init(); + + int result = caliptra_model_apb_write_u32(m, address, (int)data); + + caliptra_model_step(m); + + return result; +} + +/** + * caliptra_read_u32 + * + * Reads a uint32_t value from the specified address. + * + * @param[in] address Memory address to read + * @param[in] data Pointer to a uint32_t to store the data + * + * @return 0 if successful, other if error (TBD) + */ +int caliptra_read_u32(uint32_t address, uint32_t *data) +{ + return caliptra_model_apb_read_u32(hwmod_get_or_init(), address, (int*)data); +} + +/** + * caliptra_wait + * + * Pend the current operation. + */ +void caliptra_wait(void) +{ + caliptra_model_step(hwmod_get_or_init()); +} diff --git a/libcaliptra/inc/caliptra_api.h b/libcaliptra/inc/caliptra_api.h new file mode 100644 index 0000000000..d64a346752 --- /dev/null +++ b/libcaliptra/inc/caliptra_api.h @@ -0,0 +1,69 @@ +// Licensed under the Apache-2.0 license +#pragma once + +#include +#include + +#include "caliptra_if.h" + +/** + * caliptra_buffer + * + * Transfer buffer for Caliptra mailbox commands + */ +#if !defined(HWMODEL) +typedef struct caliptra_buffer { + const uint8_t *data; //< Pointer to a buffer with data to send/space to receive + uintptr_t len; //< Size of the buffer +} caliptra_buffer; +#endif + +/** + * DeviceLifecycle + * + * Device life cycle states + */ +enum DeviceLifecycle { + Unprovisioned = 0, + Manufacturing = 1, + Reserved2 = 2, + Production = 3, +}; + +/** + * caliptra_fuses + * + * Fuse data to be written to Caliptra registers + */ +struct caliptra_fuses { + uint32_t uds_seed[12]; + uint32_t field_entropy[8]; + uint32_t key_manifest_pk_hash[12]; + uint32_t key_manifest_pk_hash_mask : 4; + uint32_t rsvd : 28; + uint32_t owner_pk_hash[12]; + uint32_t fmc_key_manifest_svn; + uint32_t runtime_svn[4]; + bool anti_rollback_disable; + uint32_t idevid_cert_attr[24]; + uint32_t idevid_manuf_hsm_id[4]; + enum DeviceLifecycle life_cycle; +}; + +// Query if ROM is ready for fuses +bool caliptra_ready_for_fuses(void); + +// Initialize Caliptra fuses prior to boot +int caliptra_init_fuses(struct caliptra_fuses *fuses); + +// Write into Caliptra BootFSM Go Register +int caliptra_bootfsm_go(); + +// Query if ROM is ready for firmware +bool caliptra_ready_for_firmware(void); + +// Upload Caliptra Firmware +int caliptra_upload_fw(struct caliptra_buffer *fw_buffer); + +// Execute Mailbox Command +int caliptra_mailbox_execute(uint32_t cmd, struct caliptra_buffer *mbox_tx_buffer, struct caliptra_buffer *mbox_rx_buffer); diff --git a/libcaliptra/inc/caliptra_if.h b/libcaliptra/inc/caliptra_if.h new file mode 100644 index 0000000000..e32e1c4bf6 --- /dev/null +++ b/libcaliptra/inc/caliptra_if.h @@ -0,0 +1,40 @@ +//Licensed under the Apache-2.0 license +#pragma once + +#include +#include + +#define CALIPTRA_STATUS_OK 0 + +// Memory + +/** + * caliptra_write_u32 + * + * Writes a uint32_t value to the specified address. + * + * @param[in] address Memory address to write + * @param[in] data Data to write at address + * + * @return 0 if successful, other if error (TBD) + */ +int caliptra_write_u32(uint32_t address, uint32_t data); + +/** + * caliptra_read_u32 + * + * Reads a uint32_t value from the specified address. + * + * @param[in] address Memory address to read + * @param[in] data Pointer to a uint32_t to store the data + * + * @return 0 if successful, other if error (TBD) + */ +int caliptra_read_u32(uint32_t address, uint32_t *data); + +/** + * caliptra_wait + * + * Pend the current operation. + */ +void caliptra_wait(void); diff --git a/libcaliptra/src/caliptra_api.c b/libcaliptra/src/caliptra_api.c new file mode 100644 index 0000000000..3ca015cddf --- /dev/null +++ b/libcaliptra/src/caliptra_api.c @@ -0,0 +1,280 @@ +// Licensed under the Apache-2.0 license +#include +#include +#include +#include +#include +#include +#include "caliptra_if.h" +#include "caliptra_api.h" +#include "caliptra_fuses.h" +#include "caliptra_mbox.h" + +#define CALIPTRA_STATUS_NOT_READY 0 + +static inline uint32_t caliptra_read_status(void) +{ + uint32_t status; + + caliptra_read_u32(CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS, &status); + + return status; +} + +/** + * caliptra_ready_for_fuses + * + * Reports if the Caliptra hardware is ready for fuse data + * + * @return bool True if ready, false otherwise + */ +bool caliptra_ready_for_fuses(void) +{ + uint32_t status; + + status = caliptra_read_status(); + + if ((status & CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS) + == GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FUSES_MASK) + { + return true; + } + + return false; + } + +/** + * caliptra_init_fuses + * + * Initialize fuses based on contents of "fuses" argument + * + * @param[in] fuses Valid caliptra_fuses structure + * + * @return int 0 if successful, -EINVAL if fuses is null, -EPERM if caliptra is not ready for fuses, -EIO if still ready after fuses are written + */ +int caliptra_init_fuses(struct caliptra_fuses *fuses) +{ + // Parameter check + if (!fuses) { + return -EINVAL; + } + + // Check whether caliptra is ready for fuses + // + // This check is disabled until the below ticket is resolved. + // https://github.com/chipsalliance/caliptra-sw/issues/508 + // + //if (!caliptra_ready_for_fuses()) + // return -EPERM; + + // Write Fuses + caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_UDS_SEED_0, fuses->uds_seed, sizeof(fuses->uds_seed)); + caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_FIELD_ENTROPY_0, fuses->field_entropy, sizeof(fuses->field_entropy)); + caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_KEY_MANIFEST_PK_HASH_0, fuses->key_manifest_pk_hash, sizeof(fuses->key_manifest_pk_hash)); + caliptra_fuse_write(GENERIC_AND_FUSE_REG_FUSE_KEY_MANIFEST_PK_HASH_MASK, fuses->key_manifest_pk_hash_mask); + caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_OWNER_PK_HASH_0, fuses->owner_pk_hash, sizeof(fuses->owner_pk_hash)); + caliptra_fuse_write(GENERIC_AND_FUSE_REG_FUSE_FMC_KEY_MANIFEST_SVN, fuses->fmc_key_manifest_svn); + caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_FMC_KEY_MANIFEST_SVN, fuses->runtime_svn, sizeof(fuses->runtime_svn)); // https://github.com/chipsalliance/caliptra-sw/issues/529 + caliptra_fuse_write(GENERIC_AND_FUSE_REG_FUSE_ANTI_ROLLBACK_DISABLE, (uint32_t)fuses->anti_rollback_disable); + caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_IDEVID_CERT_ATTR_0, fuses->idevid_cert_attr, sizeof(fuses->idevid_cert_attr)); + caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_IDEVID_MANUF_HSM_ID_0, fuses->idevid_manuf_hsm_id, sizeof(fuses->idevid_manuf_hsm_id)); + caliptra_fuse_write(GENERIC_AND_FUSE_REG_FUSE_LIFE_CYCLE, (uint32_t)fuses->life_cycle); + + // Write to Caliptra Fuse Done + caliptra_write_u32(CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_FUSE_WR_DONE, 1); + + // No longer ready for fuses + if (caliptra_ready_for_fuses()) + return -EIO; + + return 0; +} + +/** + * caliptra_bootfsm_go + * + * Initiate caliptra hw startup + * + * @return 0 if successful + */ +int caliptra_bootfsm_go() +{ + // Write BOOTFSM_GO Register + caliptra_write_u32(CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_BOOTFSM_GO, 1); + + //TODO: Check registers/provide async completion mechanism + + return 0; +} + +/** + * caliptra_mailbox_write_fifo + * + * Transfer contents of buffer into the mailbox FIFO + * + * @param[in] buffer Pointer to a valid caliptra_buffer struct + * + * @return int -EINVAL if the buffer is too large. + */ +static int caliptra_mailbox_write_fifo(struct caliptra_buffer *buffer) +{ + // Check against max size + const uint32_t MBOX_SIZE = (128u * 1024u); + + if (buffer->len > MBOX_SIZE) { + return -EINVAL; + } + + // Write DLEN + caliptra_mbox_write_dlen(buffer->len); + + uint32_t remaining_len = buffer->len; + uint32_t *data_dw = (uint32_t *)buffer->data; + + // Copy DWord multiples + while (remaining_len > sizeof(uint32_t)) { + caliptra_mbox_write(MBOX_CSR_MBOX_DATAIN, *data_dw++); + remaining_len -= sizeof(uint32_t); + } + + // if un-aligned dword remainder... + if (remaining_len) { + uint32_t data = 0; + memcpy(&data, data_dw, remaining_len); + caliptra_mbox_write(MBOX_CSR_MBOX_DATAIN, data); + } + + return 0; +} + +/** + * caliptra_mailbox_read_buffer + * + * Read a mailbxo FIFO into a buffer + * + * @param[in] buffer A pointer to a valid caliptra_buffer struct + * + * @return int 0 if successful, -EINVAL if the buffer is too small or the buffer pointer is invalid. + */ +static int caliptra_mailbox_read_buffer(struct caliptra_buffer *buffer) +{ + uint32_t remaining_len = caliptra_mbox_read_dlen(); + + // Check that the buffer is not null + if (buffer == NULL) + return -EINVAL; + + // Check we have enough room in the buffer + if (buffer->len < remaining_len || !buffer->data) + return -EINVAL; + + uint32_t *data_dw = (uint32_t *)buffer->data; + + // Copy DWord multiples + while (remaining_len > sizeof(uint32_t)) { + *data_dw++ = caliptra_mbox_read(MBOX_CSR_MBOX_DATAOUT); + remaining_len -= sizeof(uint32_t); + } + + // if un-aligned dword reminder... + if (remaining_len) { + uint32_t data = caliptra_mbox_read(MBOX_CSR_MBOX_DATAOUT); + memcpy(data_dw, &data, remaining_len); + } + + return 0; +} + +/** + * caliptra_mailbox_execute + * + * Execute a mailbox command and send/retrieve a buffer + * + * @param[in] cmd Caliptra command opcode + * @param[in] mbox_tx_buffer Transmit buffer + * @param[in] mbox_rx_buffer Receive buffer + * + * @return 0 if successful, -EBUSY if the mailbox is locked, -EIO if the command has failed or data is not available or the FSM is not include + */ +int caliptra_mailbox_execute(uint32_t cmd, struct caliptra_buffer *mbox_tx_buffer, struct caliptra_buffer *mbox_rx_buffer) +{ + // If mbox already locked return + if (caliptra_mbox_is_lock()) { + return -EBUSY; + } + + // Write Cmd and Tx Buffer + caliptra_mbox_write_cmd(cmd); + caliptra_mailbox_write_fifo(mbox_tx_buffer); + + // Set Execute bit and wait + caliptra_mbox_write_execute_wait(true); + + // Check the Mailbox Status + uint32_t status = caliptra_mbox_read_status(); + if (status == CALIPTRA_MBOX_STATUS_CMD_FAILURE) { + caliptra_mbox_write_execute(false); + return -EIO; + } else if(status == CALIPTRA_MBOX_STATUS_CMD_COMPLETE) { + caliptra_mbox_write_execute(false); + return 0; + } else if (status != CALIPTRA_MBOX_STATUS_DATA_READY) { + return -EIO; + } + + // Read Buffer + caliptra_mailbox_read_buffer(mbox_rx_buffer); + + // Execute False and wait + caliptra_mbox_write_execute_wait(false); + + if (caliptra_mbox_read_status_fsm() != CALIPTRA_MBOX_STATUS_FSM_IDLE) + return -EIO; + + return 0; +} + +/** + * caliptra_ready_for_firmware + * + * Reports if the Caliptra hardware is ready for firmware upload + * + * @return bool True if ready, false otherwise + */ +bool caliptra_ready_for_firmware(void) +{ + uint32_t status; + bool ready; + + do + { + status = caliptra_read_status(); + + if ((status & GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FW_MASK) + == GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FW_MASK) + { + ready = true; + } + else + { + caliptra_wait(); + } + } while(ready == false); + + return true; +} + +/** + * caliptra_upload_fw + * + * Upload firmware to the Caliptra device + * + * @param[in] fw_buffer Buffer containing Caliptra firmware + * + * @return See caliptra_mailbo, mb_resultx_execute for possible results. + */ +int caliptra_upload_fw(struct caliptra_buffer *fw_buffer) +{ + const uint32_t FW_LOAD_CMD_OPCODE = 0x46574C44u; + return caliptra_mailbox_execute(FW_LOAD_CMD_OPCODE, fw_buffer, NULL); +} diff --git a/libcaliptra/src/caliptra_fuses.h b/libcaliptra/src/caliptra_fuses.h new file mode 100644 index 0000000000..ee5ddea577 --- /dev/null +++ b/libcaliptra/src/caliptra_fuses.h @@ -0,0 +1,20 @@ +// Licensed under the Apache-2.0 license + +#pragma once + +#include "caliptra_api.h" + +// WARNING: THESE APIS ARE INTENDED FOR SIMULATION ONLY. +// SOC FW MUST HAVE NO ACCESS TO THOSE APIS. +// A HW STATE MACHINE SHOULD BE USED TO SEND FUSE VALUES TO CALIPTRA OVER APB BUS + +static inline void caliptra_fuse_write(uint32_t offset, uint32_t data) +{ + caliptra_write_u32((offset + CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_BASE_ADDR), data); +} + +static inline void caliptra_fuse_array_write(uint32_t offset, uint32_t *data, uint32_t size) +{ + for (uint32_t idx= 0; idx < size; idx +=sizeof(uint32_t)) + caliptra_fuse_write((offset + idx ), data[idx]); +} diff --git a/libcaliptra/src/caliptra_mbox.h b/libcaliptra/src/caliptra_mbox.h new file mode 100644 index 0000000000..69f09ff8d0 --- /dev/null +++ b/libcaliptra/src/caliptra_mbox.h @@ -0,0 +1,82 @@ +// Licensed under the Apache-2.0 license +#pragma once + +/** + * Mailbox helper functions + * + * All of the below functions map to register reads and writes. + * + * TODO: Investigate interrupts for notification on mailbox + * command completion. + */ + +#define CALIPTRA_MBOX_STATUS_BUSY 0 +#define CALIPTRA_MBOX_STATUS_DATA_READY 1 +#define CALIPTRA_MBOX_STATUS_CMD_COMPLETE 2 +#define CALIPTRA_MBOX_STATUS_CMD_FAILURE 3 + +#define CALIPTRA_MBOX_STATUS_FSM_IDLE 0 +#define CALIPTRA_MBOX_STATUS_FSM_READY_FOR_CMD 1 +#define CALIPTRA_MBOX_STATUS_FSM_READY_FOR_DATA 2 +#define CALIPTRA_MBOX_STATUS_FSM_READY_FOR_DLEN 3 +#define CALIPTRA_MBOX_STATUS_FSM_EXECUTE_SOC 4 +#define CALIPTRA_MBOX_STATUS_FSM_EXECUTE_UC 6 + +static inline void caliptra_mbox_write(uint32_t offset, uint32_t data) +{ + caliptra_write_u32((offset + CALIPTRA_TOP_REG_MBOX_CSR_BASE_ADDR), data); +} + +static inline uint32_t caliptra_mbox_read(uint32_t offset) +{ + uint32_t data; + caliptra_read_u32((offset + CALIPTRA_TOP_REG_MBOX_CSR_BASE_ADDR), &data); + return data; +} + +static inline bool caliptra_mbox_is_lock() +{ + return (caliptra_mbox_read(MBOX_CSR_MBOX_LOCK) & MBOX_CSR_MBOX_LOCK_LOCK_MASK); +} + +static inline void caliptra_mbox_write_cmd(uint32_t cmd) +{ + caliptra_mbox_write(MBOX_CSR_MBOX_CMD, cmd); +} + +static inline void caliptra_mbox_write_execute(bool ex) +{ + caliptra_mbox_write(MBOX_CSR_MBOX_EXECUTE, ex); +} + +static inline uint8_t caliptra_mbox_write_execute_wait(bool ex) +{ + caliptra_mbox_write(MBOX_CSR_MBOX_EXECUTE, ex); + uint8_t status; + while((status = (uint8_t)(caliptra_mbox_read(MBOX_CSR_MBOX_STATUS) & MBOX_CSR_MBOX_STATUS_STATUS_MASK)) == CALIPTRA_MBOX_STATUS_BUSY) + { + caliptra_wait(); + } + + return status; +} + +static inline uint8_t caliptra_mbox_read_status(void) +{ + return (uint8_t)(caliptra_mbox_read(MBOX_CSR_MBOX_STATUS) & MBOX_CSR_MBOX_STATUS_STATUS_MASK); +} + +static inline uint8_t caliptra_mbox_read_status_fsm(void) +{ + return (uint8_t)(caliptra_mbox_read(MBOX_CSR_MBOX_STATUS) >> 16 & MBOX_CSR_MBOX_STATUS_STATUS_MASK); +} + +static inline uint32_t caliptra_mbox_read_dlen(void) +{ + return caliptra_mbox_read(MBOX_CSR_MBOX_DLEN); +} + +static inline void caliptra_mbox_write_dlen(uint32_t dlen) +{ + caliptra_mbox_write(MBOX_CSR_MBOX_DLEN, dlen); +}