linux/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c

// 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();