// SPDX-License-Identifier: GPL-2.0-or-later /* * Client driver for Qualcomm UEFI Secure Application (qcom.tz.uefisecapp). * Provides access to UEFI variables on platforms where they are secured by the * aforementioned Secure Execution Environment (SEE) application. * * Copyright (C) 2023 Maximilian Luz <[email protected]> */ #include <linux/efi.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/ucs2_string.h> #include <linux/firmware/qcom/qcom_qseecom.h> #include <linux/firmware/qcom/qcom_scm.h> #include <linux/firmware/qcom/qcom_tzmem.h> /* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */ /* Maximum length of name string with null-terminator */ #define QSEE_MAX_NAME_LEN … #define QSEE_CMD_UEFI(x) … #define QSEE_CMD_UEFI_GET_VARIABLE … #define QSEE_CMD_UEFI_SET_VARIABLE … #define QSEE_CMD_UEFI_GET_NEXT_VARIABLE … #define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO … /** * struct qsee_req_uefi_get_variable - Request for GetVariable command. * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_GET_VARIABLE. * @length: Length of the request in bytes, including this struct and any * parameters (name, GUID) stored after it as well as any padding * thereof for alignment. * @name_offset: Offset from the start of this struct to where the variable * name is stored (as utf-16 string), in bytes. * @name_size: Size of the name parameter in bytes, including null-terminator. * @guid_offset: Offset from the start of this struct to where the GUID * parameter is stored, in bytes. * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). * @data_size: Size of the output buffer, in bytes. */ struct qsee_req_uefi_get_variable { … } __packed; /** * struct qsee_rsp_uefi_get_variable - Response for GetVariable command. * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_GET_VARIABLE. * @length: Length of the response in bytes, including this struct and the * returned data. * @status: Status of this command. * @attributes: EFI variable attributes. * @data_offset: Offset from the start of this struct to where the data is * stored, in bytes. * @data_size: Size of the returned data, in bytes. In case status indicates * that the buffer is too small, this will be the size required * to store the EFI variable data. */ struct qsee_rsp_uefi_get_variable { … } __packed; /** * struct qsee_req_uefi_set_variable - Request for the SetVariable command. * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_SET_VARIABLE. * @length: Length of the request in bytes, including this struct and any * parameters (name, GUID, data) stored after it as well as any * padding thereof required for alignment. * @name_offset: Offset from the start of this struct to where the variable * name is stored (as utf-16 string), in bytes. * @name_size: Size of the name parameter in bytes, including null-terminator. * @guid_offset: Offset from the start of this struct to where the GUID * parameter is stored, in bytes. * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). * @attributes: The EFI variable attributes to set for this variable. * @data_offset: Offset from the start of this struct to where the EFI variable * data is stored, in bytes. * @data_size: Size of EFI variable data, in bytes. * */ struct qsee_req_uefi_set_variable { … } __packed; /** * struct qsee_rsp_uefi_set_variable - Response for the SetVariable command. * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_SET_VARIABLE. * @length: The length of this response, i.e. the size of this struct in * bytes. * @status: Status of this command. * @_unknown1: Unknown response field. * @_unknown2: Unknown response field. */ struct qsee_rsp_uefi_set_variable { … } __packed; /** * struct qsee_req_uefi_get_next_variable - Request for the * GetNextVariableName command. * @command_id: The ID of the command. Must be * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. * @length: Length of the request in bytes, including this struct and any * parameters (name, GUID) stored after it as well as any padding * thereof for alignment. * @guid_offset: Offset from the start of this struct to where the GUID * parameter is stored, in bytes. * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). * @name_offset: Offset from the start of this struct to where the variable * name is stored (as utf-16 string), in bytes. * @name_size: Size of the name parameter in bytes, including null-terminator. */ struct qsee_req_uefi_get_next_variable { … } __packed; /** * struct qsee_rsp_uefi_get_next_variable - Response for the * GetNextVariableName command. * @command_id: The ID of the command. Should be * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE. * @length: Length of the response in bytes, including this struct and any * parameters (name, GUID) stored after it as well as any padding * thereof for alignment. * @status: Status of this command. * @guid_offset: Offset from the start of this struct to where the GUID * parameter is stored, in bytes. * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t). * @name_offset: Offset from the start of this struct to where the variable * name is stored (as utf-16 string), in bytes. * @name_size: Size of the name parameter in bytes, including null-terminator. */ struct qsee_rsp_uefi_get_next_variable { … } __packed; /** * struct qsee_req_uefi_query_variable_info - Response for the * GetNextVariableName command. * @command_id: The ID of the command. Must be * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. * @length: The length of this request, i.e. the size of this struct in * bytes. * @attributes: The storage attributes to query the info for. */ struct qsee_req_uefi_query_variable_info { … } __packed; /** * struct qsee_rsp_uefi_query_variable_info - Response for the * GetNextVariableName command. * @command_id: The ID of the command. Must be * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO. * @length: The length of this response, i.e. the size of this * struct in bytes. * @status: Status of this command. * @_pad: Padding. * @storage_space: Full storage space size, in bytes. * @remaining_space: Free storage space available, in bytes. * @max_variable_size: Maximum variable data size, in bytes. */ struct qsee_rsp_uefi_query_variable_info { … } __packed; /* -- Alignment helpers ----------------------------------------------------- */ /* * Helper macro to ensure proper alignment of types (fields and arrays) when * stored in some (contiguous) buffer. * * Note: The driver from which this one has been reverse-engineered expects an * alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t, * however, has an alignment of 4 byte (32 bits). So far, this seems to work * fine here. See also the comment on the typedef of efi_guid_t. * * Note: It looks like uefisecapp is quite picky about how the memory passed to * it is structured and aligned. In particular the request/response setup used * for QSEE_CMD_UEFI_GET_VARIABLE. While qcom_qseecom_app_send(), in theory, * accepts separate buffers/addresses for the request and response parts, in * practice, however, it seems to expect them to be both part of a larger * contiguous block. We initially allocated separate buffers for the request * and response but this caused the QSEE_CMD_UEFI_GET_VARIABLE command to * either not write any response to the response buffer or outright crash the * device. Therefore, we now allocate a single contiguous block of DMA memory * for both and properly align the data using the macros below. In particular, * request and response structs are aligned at 8 byte (via __reqdata_offs()), * following the driver that this has been reverse-engineered from. */ #define qcuefi_buf_align_fields(fields...) … #define __field_impl(size, align, offset) … #define __array_offs(type, count, offset) … #define __array_offs_aligned(type, count, align, offset) … #define __reqdata_offs(size, offset) … #define __array(type, count) … #define __field_offs(type, offset) … #define __field(type) … /* -- UEFI app interface. --------------------------------------------------- */ struct qcuefi_client { … }; static struct device *qcuefi_dev(struct qcuefi_client *qcuefi) { … } static efi_status_t qsee_uefi_status_to_efi(u32 status) { … } static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, const efi_guid_t *guid, u32 *attributes, unsigned long *data_size, void *data) { … } static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, const efi_guid_t *guid, u32 attributes, unsigned long data_size, const void *data) { … } static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, unsigned long *name_size, efi_char16_t *name, efi_guid_t *guid) { … } static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr, u64 *storage_space, u64 *remaining_space, u64 *max_variable_size) { … } /* -- Global efivar interface. ---------------------------------------------- */ static struct qcuefi_client *__qcuefi; static DEFINE_MUTEX(__qcuefi_lock); static int qcuefi_set_reference(struct qcuefi_client *qcuefi) { … } static struct qcuefi_client *qcuefi_acquire(void) { … } static void qcuefi_release(void) { … } static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr, unsigned long *data_size, void *data) { … } static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor, u32 attr, unsigned long data_size, void *data) { … } static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name, efi_guid_t *vendor) { … } static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space, u64 *max_variable_size) { … } static const struct efivar_operations qcom_efivar_ops = …; /* -- Driver setup. --------------------------------------------------------- */ static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev, const struct auxiliary_device_id *aux_dev_id) { … } static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev) { … } static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = …; MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table); static struct auxiliary_driver qcom_uefisecapp_driver = …; module_auxiliary_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;