aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/libstb/cvc.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/libstb/cvc.c')
-rw-r--r--roms/skiboot/libstb/cvc.c377
1 files changed, 377 insertions, 0 deletions
diff --git a/roms/skiboot/libstb/cvc.c b/roms/skiboot/libstb/cvc.c
new file mode 100644
index 000000000..663e53953
--- /dev/null
+++ b/roms/skiboot/libstb/cvc.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2018 IBM Corp. */
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) "STB: " fmt
+#endif
+
+#include <skiboot.h>
+#include <string.h>
+#include <opal-api.h>
+#include <chip.h>
+#include <xscom.h>
+#include <inttypes.h>
+#include "secureboot.h"
+#include "cvc.h"
+#include "mbedtls/sha512.h"
+
+/*
+ * Assembly interfaces to call into the Container Verification Code.
+ * func_ptr: CVC base address + offset
+ */
+ROM_response __cvc_verify_v1(void *func_ptr, ROM_container_raw *container,
+ ROM_hw_params *params);
+void __cvc_sha512_v1(void *func_ptr, const uint8_t *data, size_t len,
+ uint8_t *digest);
+
+struct container_verification_code {
+ uint64_t start_addr;
+ uint64_t end_addr;
+ struct list_head service_list;
+};
+
+static struct container_verification_code *cvc = NULL;
+static bool softrom = false;
+static void *secure_rom_mem = NULL;
+
+static struct dt_node *cvc_resv_mem = NULL;
+static struct dt_node *cvc_node = NULL;
+
+struct cvc_service {
+ int id;
+ uint64_t addr; /* base_addr + offset */
+ uint32_t version;
+ struct list_node link;
+};
+
+static struct {
+ enum cvc_service_id id;
+ const char *name;
+} cvc_service_map[] = {
+ { CVC_SHA512_SERVICE, "sha512" },
+ { CVC_VERIFY_SERVICE, "verify" },
+};
+
+static struct cvc_service *cvc_find_service(enum cvc_service_id id)
+{
+ struct cvc_service *service;
+ if (!cvc)
+ return NULL;
+
+ list_for_each(&cvc->service_list, service, link) {
+ if (service->id == id)
+ return service;
+ }
+ return NULL;
+}
+
+static const char *cvc_service_map_name(enum cvc_service_id id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cvc_service_map); i++) {
+ if (cvc_service_map[i].id == id)
+ return cvc_service_map[i].name;
+ }
+ return NULL;
+}
+
+static void cvc_register(uint64_t start_addr, uint64_t end_addr)
+{
+ if (cvc)
+ return;
+
+ cvc = malloc(sizeof(struct container_verification_code));
+ assert(cvc);
+ cvc->start_addr = start_addr;
+ cvc->end_addr = end_addr;
+ list_head_init(&cvc->service_list);
+ prlog(PR_INFO, "Found CVC @ %" PRIx64 "-%" PRIx64 "\n",
+ start_addr, end_addr);
+}
+
+static void cvc_service_register(uint32_t id, uint32_t offset, uint32_t version)
+{
+ struct cvc_service *service;
+ const char *name;
+
+ if (!cvc)
+ return;
+
+ /* Service already registered? */
+ if (cvc_find_service(id))
+ return;
+
+ if (cvc->start_addr + offset > cvc->end_addr) {
+ prlog(PR_WARNING, "CVC service @ %x out of range, "
+ "id=%d\n", offset, id);
+ return;
+ }
+
+ name = cvc_service_map_name(id);
+ if (!name) {
+ prlog(PR_ERR, "CVC service %d not supported\n", id);
+ return;
+ }
+
+ service = malloc(sizeof(struct cvc_service));
+ assert(service);
+ service->id = id;
+ service->version = version;
+ service->addr = cvc->start_addr + offset;
+ list_add_tail(&cvc->service_list, &service->link);
+ prlog(PR_INFO, "Found CVC-%s @ %" PRIx64 ", version=%d\n",
+ name, service->addr, service->version);
+}
+
+static int cvc_reserved_mem_init(struct dt_node *parent) {
+ struct dt_node *node, *service;
+ struct dt_node *reserved_mem;
+ struct dt_node *exports;
+ uint32_t phandle;
+ uint64_t addr, size;
+
+ reserved_mem = dt_find_by_path(dt_root, "/ibm,hostboot/reserved-memory");
+ if (!reserved_mem) {
+ prlog(PR_ERR, "/ibm,hostboot/reserved-memory not found\n");
+ return -1;
+ }
+
+ /*
+ * The container verification code is stored in a hostboot reserved
+ * memory which is pointed by the property
+ * /ibm,secureboot/ibm,container-verification-code/memory-region
+ */
+ dt_for_each_child(parent, node) {
+ if (dt_node_is_compatible(node, "ibm,container-verification-code")) {
+ phandle = dt_prop_get_u32(node, "memory-region");
+ cvc_resv_mem = dt_find_by_phandle(reserved_mem, phandle);
+ cvc_node = node;
+ break;
+ }
+ }
+ if (!cvc_resv_mem) {
+ prlog(PR_ERR, "CVC not found in /ibm,hostboot/reserved-memory\n");
+ return -1;
+ }
+ addr = dt_get_address(cvc_resv_mem, 0, &size);
+ cvc_register(addr, addr + size-1);
+
+ exports = dt_find_by_path(dt_root, "/ibm,opal/firmware/exports");
+ if (!exports) {
+ prerror("OCC: dt node /ibm,opal/firmware/exports not found\n");
+ return false;
+ }
+ dt_add_property_u64s(exports, "cvc", addr, size - 1);
+
+ /*
+ * Each child of the CVC node describes a CVC service
+ */
+ dt_for_each_child(node, service) {
+ uint32_t version, offset;
+
+ version = dt_prop_get_u32(service, "version");
+ offset = dt_prop_get_u32(service, "reg");
+
+ if (dt_node_is_compatible(service, "ibm,cvc-sha512"))
+ cvc_service_register(CVC_SHA512_SERVICE, offset, version);
+ else if (dt_node_is_compatible(service, "ibm,cvc-verify"))
+ cvc_service_register(CVC_VERIFY_SERVICE, offset, version);
+ else
+ prlog(PR_DEBUG, "unknown %s\n", service->name);
+ }
+
+ return 0;
+}
+
+#define SECURE_ROM_MEMORY_SIZE (16 * 1024)
+#define SECURE_ROM_XSCOM_ADDRESS 0x02020017
+
+#define SECURE_ROM_SHA512_OFFSET 0x20
+#define SECURE_ROM_VERIFY_OFFSET 0x30
+
+static int cvc_secure_rom_init(void) {
+ const uint32_t reg_addr = SECURE_ROM_XSCOM_ADDRESS;
+ struct dt_node *exports;
+ struct proc_chip *chip;
+ uint64_t reg_data;
+
+ if (!secure_rom_mem) {
+ secure_rom_mem = malloc(SECURE_ROM_MEMORY_SIZE);
+ assert(secure_rom_mem);
+ }
+ /*
+ * The logic that contains the ROM within the processor is implemented
+ * in a way that it only responds to CI (cache inhibited) operations.
+ * Due to performance issues we copy the verification code from the
+ * secure ROM to RAM. We use memcpy_from_ci() to do that.
+ */
+ chip = next_chip(NULL);
+ xscom_read(chip->id, reg_addr, &reg_data);
+ memcpy_from_ci(secure_rom_mem, (void*) reg_data,
+ SECURE_ROM_MEMORY_SIZE);
+ cvc_register((uint64_t)secure_rom_mem,
+ (uint64_t)secure_rom_mem + SECURE_ROM_MEMORY_SIZE-1);
+
+ exports = dt_find_by_path(dt_root, "/ibm,opal/firmware/exports");
+ if (!exports) {
+ prerror("OCC: dt node /ibm,opal/firmware/exports not found\n");
+ return false;
+ }
+
+ dt_add_property_u64s(exports, "securerom", (uint64_t)secure_rom_mem,
+ SECURE_ROM_MEMORY_SIZE-1);
+
+ cvc_service_register(CVC_SHA512_SERVICE, SECURE_ROM_SHA512_OFFSET, 1);
+ cvc_service_register(CVC_VERIFY_SERVICE, SECURE_ROM_VERIFY_OFFSET, 1);
+ return 0;
+}
+
+void cvc_update_reserved_memory_phandle(void) {
+ struct dt_node *reserved_mem;
+
+ if (!cvc_resv_mem || !cvc_node)
+ return;
+
+ /*
+ * The linux documentation, reserved-memory.txt, says that memory-region
+ * is a phandle that pairs to a children of /reserved-memory
+ */
+ reserved_mem = dt_find_by_path(dt_root, "/reserved-memory");
+ if (!reserved_mem) {
+ prlog(PR_ERR, "/reserved-memory not found\n");
+ return;
+ }
+ cvc_resv_mem = dt_find_by_name(reserved_mem, cvc_resv_mem->name);
+ if (cvc_resv_mem) {
+ dt_check_del_prop(cvc_node, "memory-region");
+ dt_add_property_cells(cvc_node, "memory-region", cvc_resv_mem->phandle);
+ } else {
+ prlog(PR_WARNING, "CVC not found in /reserved-memory\n");
+ return;
+ }
+
+ cvc_resv_mem = NULL;
+ cvc_node = NULL;
+}
+
+int cvc_init(void)
+{
+ struct dt_node *node;
+ int version;
+ int rc = 0;
+
+ if (cvc)
+ return 0;
+
+ node = dt_find_by_path(dt_root, "/ibm,secureboot");
+ if (!node)
+ return -1;
+
+ if (!secureboot_is_compatible(node, &version, NULL)) {
+ /**
+ * @fwts-label CVCNotCompatible
+ * @fwts-advice Compatible CVC driver not found. Probably,
+ * hostboot/mambo/skiboot has updated the
+ * /ibm,secureboot/compatible without adding a driver that
+ * supports it.
+ */
+ prlog(PR_ERR, "%s FAILED, /ibm,secureboot not compatible.\n",
+ __func__);
+ return -1;
+ }
+
+ /* Only in P8 the CVC is stored in a secure ROM */
+ if (version == IBM_SECUREBOOT_V1 &&
+ proc_gen == proc_gen_p8) {
+ rc = cvc_secure_rom_init();
+ } else if (version == IBM_SECUREBOOT_SOFTROM) {
+ softrom = true;
+ } else if (version == IBM_SECUREBOOT_V2) {
+ rc = cvc_reserved_mem_init(node);
+ } else {
+ prlog(PR_ERR, "%s FAILED. /ibm,secureboot not supported\n",
+ __func__);
+ return -1;
+ }
+ return rc;
+}
+
+int call_cvc_sha512(const uint8_t *data, size_t data_len, uint8_t *digest,
+ size_t digest_size)
+{
+ struct cvc_service *service;
+
+ if (!data || !digest || digest_size < SHA512_DIGEST_LENGTH)
+ return OPAL_PARAMETER;
+
+ if (data_len <= 0)
+ return OPAL_SUCCESS;
+
+ memset(digest, 0, SHA512_DIGEST_LENGTH);
+ if (softrom) {
+ mbedtls_sha512_context ctx;
+ mbedtls_sha512_init(&ctx);
+ mbedtls_sha512_starts(&ctx, 0); // SHA512 = 0
+ mbedtls_sha512_update(&ctx, data, data_len);
+ mbedtls_sha512_finish(&ctx, digest);
+ mbedtls_sha512_free(&ctx);
+ return OPAL_SUCCESS;
+ }
+
+ service = cvc_find_service(CVC_SHA512_SERVICE);
+
+ if (!service)
+ return OPAL_UNSUPPORTED;
+
+ if (service->version == 1) {
+ unsigned long msr = mfmsr();
+ __cvc_sha512_v1((void*) service->addr, data, data_len, digest);
+ assert(msr == mfmsr());
+ } else {
+ return OPAL_UNSUPPORTED;
+ }
+
+ return OPAL_SUCCESS;
+}
+
+int call_cvc_verify(void *container, size_t len, const void *hw_key_hash,
+ size_t hw_key_hash_size, __be64 *log)
+{
+ ROM_hw_params hw_params;
+ ROM_response rc;
+ struct cvc_service *service;
+
+ if (!container || len < SECURE_BOOT_HEADERS_SIZE ||
+ !hw_key_hash || hw_key_hash_size <= 0)
+ return OPAL_PARAMETER;
+
+ if (softrom)
+ return OPAL_UNSUPPORTED;
+
+ service = cvc_find_service(CVC_VERIFY_SERVICE);
+
+ if (!service)
+ return OPAL_UNSUPPORTED;
+
+ memset(&hw_params, 0, sizeof(ROM_hw_params));
+ memcpy(&hw_params.hw_key_hash, hw_key_hash, hw_key_hash_size);
+
+ if (service->version == 1) {
+ unsigned long msr = mfmsr();
+ rc = __cvc_verify_v1((void*) service->addr,
+ (ROM_container_raw*) container,
+ &hw_params);
+ assert(msr == mfmsr());
+ } else {
+ return OPAL_UNSUPPORTED;
+ }
+
+ if (log)
+ *log = hw_params.log;
+
+ if (rc != ROM_DONE)
+ return OPAL_PARTIAL;
+
+ return OPAL_SUCCESS;
+}