From b22e6898e1493eb00d0f0de6d48655d744264cb6 Mon Sep 17 00:00:00 2001 From: Yi Chou Date: Tue, 11 Apr 2023 15:57:08 +0800 Subject: [PATCH] feat(cros_widevine): add ChromeOS widevine SMC handler The ChromeOS will use the SMC to pass some secrets from firmware to optee. Change-Id: Iaf3357d40a7ed22415926acd9d7979df24dd81f1 Signed-off-by: Yi Chou --- bl31/bl31.mk | 4 + .../oem/chromeos/widevine_smc_handlers.h | 65 ++++++++++++ services/oem/chromeos/widevine_smc_handlers.c | 98 +++++++++++++++++++ services/spd/opteed/opteed.mk | 8 ++ services/spd/opteed/opteed_main.c | 65 +++++++++++- 5 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 include/services/oem/chromeos/widevine_smc_handlers.h create mode 100644 services/oem/chromeos/widevine_smc_handlers.c diff --git a/bl31/bl31.mk b/bl31/bl31.mk index 40e3df8b2..9959a3ef5 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -161,6 +161,10 @@ BL31_SOURCES += services/std_svc/drtm/drtm_main.c \ ${MBEDTLS_SOURCES} endif +ifeq ($(CROS_WIDEVINE_SMC),1) +BL31_SOURCES += services/oem/chromeos/widevine_smc_handlers.c +endif + BL31_DEFAULT_LINKER_SCRIPT_SOURCE := bl31/bl31.ld.S ifneq ($(findstring gcc,$(notdir $(LD))),) diff --git a/include/services/oem/chromeos/widevine_smc_handlers.h b/include/services/oem/chromeos/widevine_smc_handlers.h new file mode 100644 index 000000000..a5251d76c --- /dev/null +++ b/include/services/oem/chromeos/widevine_smc_handlers.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, The ChromiumOS Authors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CROS_WIDEVINE_SMC_HANDLERS_H +#define CROS_WIDEVINE_SMC_HANDLERS_H + +#include + +/******************************************************************************* + * Defines for CrOS OEM Service queries + ******************************************************************************/ + +/* 0xC300C050 - 0xC300C05F are CrOS OEM service calls */ +#define CROS_OEM_SMC_ID 0xC050 +#define CROS_OEM_SMC_CALL_ID(func_num) \ + ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) | \ + ((SMC_64) << FUNCID_CC_SHIFT) | (OEN_OEM_START << FUNCID_OEN_SHIFT) | \ + (CROS_OEM_SMC_ID) | ((func_num) & FUNCID_NUM_MASK)) + +enum cros_drm_set { + CROS_DRM_SET_TPM_AUTH_PUB = 0U, + CROS_DRM_SET_HARDWARE_UNIQUE_KEY = 1U, + CROS_DRM_SET_ROOT_OF_TRUST = 2U, +}; + +/******************************************************************************* + * Defines for runtime services func ids + ******************************************************************************/ + +/* Sets the TPM auth public key. The maximum size is 128 bytes. + * |x1| is the length of the data, |x2| is the physical address of the data. + */ +#define CROS_OEM_SMC_DRM_SET_TPM_AUTH_PUB_FUNC_ID \ + CROS_OEM_SMC_CALL_ID(CROS_DRM_SET_TPM_AUTH_PUB) + +/* Sets the hardware unique key. The maximum size is 32 bytes. + * |x1| is the length of the data, |x2| is the physical address of the data. + */ +#define CROS_OEM_SMC_DRM_SET_HARDWARE_UNIQUE_KEY_FUNC_ID \ + CROS_OEM_SMC_CALL_ID(CROS_DRM_SET_HARDWARE_UNIQUE_KEY) + +/* Sets the widevine root of trust. The maximum size is 32 bytes. + * |x1| is the length of the data, |x2| is the physical address of the data. + */ +#define CROS_OEM_SMC_DRM_SET_ROOT_OF_TRUST_FUNC_ID \ + CROS_OEM_SMC_CALL_ID(CROS_DRM_SET_ROOT_OF_TRUST) + +#define is_cros_oem_smc(_call_id) (((_call_id) & 0xFFF0U) == CROS_OEM_SMC_ID) + +struct cros_oem_data { + uint8_t *buffer; + const uint32_t max_length; + uint32_t length; +}; + +extern struct cros_oem_data cros_oem_tpm_auth_pk; + +extern struct cros_oem_data cros_oem_huk; + +extern struct cros_oem_data cros_oem_rot; + +#endif /* CROS_WIDEVINE_SMC_HANDLERS_H */ diff --git a/services/oem/chromeos/widevine_smc_handlers.c b/services/oem/chromeos/widevine_smc_handlers.c new file mode 100644 index 000000000..83c6ccc94 --- /dev/null +++ b/services/oem/chromeos/widevine_smc_handlers.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024, The ChromiumOS Authors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define CROS_OEM_TPM_AUTH_PK_MAX_LEN 128 +#define CROS_OEM_HUK_LEN 32 +#define CROS_OEM_ROT_LEN 32 + +static uint8_t cros_oem_tpm_auth_pk_buffer[CROS_OEM_TPM_AUTH_PK_MAX_LEN]; +static uint8_t cros_oem_huk_buffer[CROS_OEM_HUK_LEN]; +static uint8_t cros_oem_rot_len_buffer[CROS_OEM_ROT_LEN]; + +struct cros_oem_data cros_oem_tpm_auth_pk = { + .buffer = cros_oem_tpm_auth_pk_buffer, + .max_length = sizeof(cros_oem_tpm_auth_pk_buffer), +}; + +struct cros_oem_data cros_oem_huk = { + .buffer = cros_oem_huk_buffer, + .max_length = sizeof(cros_oem_huk_buffer), +}; + +struct cros_oem_data cros_oem_rot = { + .buffer = cros_oem_rot_len_buffer, + .max_length = sizeof(cros_oem_rot_len_buffer), +}; + +static uintptr_t cros_write_data(struct cros_oem_data *data, + u_register_t length, u_register_t address, + void *handle) +{ + uintptr_t aligned_address; + uintptr_t aligned_size; + int32_t rc; + + if (data->length) { + SMC_RET1(handle, PSCI_E_ALREADY_ON); + } + + if (length > data->max_length) { + SMC_RET1(handle, PSCI_E_INVALID_PARAMS); + } + + aligned_address = page_align(address, DOWN); + aligned_size = page_align(length + (address - aligned_address), UP); + + /* + * We do not validate the passed in address because we are trusting the + * non-secure world at this point still. + */ + rc = mmap_add_dynamic_region(aligned_address, aligned_address, + aligned_size, MT_MEMORY | MT_RO | MT_NS); + if (rc != 0) { + SMC_RET1(handle, PSCI_E_INVALID_ADDRESS); + } + + memcpy(data->buffer, (void *)address, length); + data->length = length; + + mmap_remove_dynamic_region(aligned_address, aligned_size); + SMC_RET1(handle, SMC_OK); +} + +/* Handler for servicing specific SMC calls. */ +static uintptr_t cros_oem_svc_smc_handler(uint32_t smc_fid, u_register_t x1, + u_register_t x2, u_register_t x3, + u_register_t x4, void *cookie, + void *handle, u_register_t flags) +{ + switch (smc_fid) { + case CROS_OEM_SMC_DRM_SET_TPM_AUTH_PUB_FUNC_ID: + return cros_write_data(&cros_oem_tpm_auth_pk, x1, x2, handle); + case CROS_OEM_SMC_DRM_SET_HARDWARE_UNIQUE_KEY_FUNC_ID: + return cros_write_data(&cros_oem_huk, x1, x2, handle); + case CROS_OEM_SMC_DRM_SET_ROOT_OF_TRUST_FUNC_ID: + return cros_write_data(&cros_oem_rot, x1, x2, handle); + default: + WARN("Unimplemented OEM Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +/* Register OEM Service Calls as runtime service */ +DECLARE_RT_SVC(cros_oem_svc_smc_handler, OEN_OEM_START, OEN_OEM_END, + SMC_TYPE_FAST, NULL, cros_oem_svc_smc_handler); diff --git a/services/spd/opteed/opteed.mk b/services/spd/opteed/opteed.mk index f394744e9..289b3e77d 100644 --- a/services/spd/opteed/opteed.mk +++ b/services/spd/opteed/opteed.mk @@ -33,3 +33,11 @@ $(eval $(call add_define,PLAT_XLAT_TABLES_DYNAMIC)) $(eval $(call add_define,OPTEE_ALLOW_SMC_LOAD)) include lib/libfdt/libfdt.mk endif + +CROS_WIDEVINE_SMC := 0 +ifeq ($(CROS_WIDEVINE_SMC),1) +ifeq ($(OPTEE_ALLOW_SMC_LOAD),0) +$(error When CROS_WIDEVINE_SMC=1, OPTEE_ALLOW_SMC_LOAD must also be 1) +endif +$(eval $(call add_define,CROS_WIDEVINE_SMC)) +endif diff --git a/services/spd/opteed/opteed_main.c b/services/spd/opteed/opteed_main.c index ab9896ec2..83b001a62 100644 --- a/services/spd/opteed/opteed_main.c +++ b/services/spd/opteed/opteed_main.c @@ -33,6 +33,7 @@ #include #endif /* OPTEE_ALLOW_SMC_LOAD */ #include +#include #include #include "opteed_private.h" @@ -61,7 +62,7 @@ DEFINE_SVC_UUID2(optee_image_load_uuid, 0xb1eafba3, 0x5d31, 0x4612, 0xb9, 0x06, 0xc4, 0xc7, 0xa4, 0xbe, 0x3c, 0xc0); -#define OPTEED_FDT_SIZE 256 +#define OPTEED_FDT_SIZE 1024 static uint8_t fdt_buf[OPTEED_FDT_SIZE] __aligned(CACHE_WRITEBACK_GRANULE); #else @@ -299,6 +300,62 @@ static int add_coreboot_node(void *fdt) } #endif /* COREBOOT */ +#if CROS_WIDEVINE_SMC +/* + * Adds a options/widevine node with the widevine table information to a device + * tree. Returns zero on success or if there is no widevine table information; + * failure code otherwise. + */ +static int add_options_widevine_node(void *fdt) +{ + int ret; + + ret = fdt_begin_node(fdt, "options"); + if (ret) + return ret; + + ret = fdt_begin_node(fdt, "op-tee"); + if (ret) + return ret; + + ret = fdt_begin_node(fdt, "widevine"); + if (ret) + return ret; + + if (cros_oem_tpm_auth_pk.length) { + ret = fdt_property(fdt, "tcg,tpm-auth-public-key", + cros_oem_tpm_auth_pk.buffer, + cros_oem_tpm_auth_pk.length); + if (ret) + return ret; + } + + if (cros_oem_huk.length) { + ret = fdt_property(fdt, "op-tee,hardware-unique-key", + cros_oem_huk.buffer, cros_oem_huk.length); + if (ret) + return ret; + } + + if (cros_oem_rot.length) { + ret = fdt_property(fdt, "google,widevine-root-of-trust-ecc-p256", + cros_oem_rot.buffer, cros_oem_rot.length); + if (ret) + return ret; + } + + ret = fdt_end_node(fdt); + if (ret) + return ret; + + ret = fdt_end_node(fdt); + if (ret) + return ret; + + return fdt_end_node(fdt); +} +#endif /* CROS_WIDEVINE_SMC */ + /* * Creates a device tree for passing into OP-TEE. Currently is populated with * the coreboot table address. @@ -326,6 +383,12 @@ static int create_opteed_dt(void) return ret; #endif /* COREBOOT */ +#if CROS_WIDEVINE_SMC + ret = add_options_widevine_node(fdt_buf); + if (ret) + return ret; +#endif /* CROS_WIDEVINE_SMC */ + ret = fdt_end_node(fdt_buf); if (ret) return ret;