Path: blob/master/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
26428 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Client driver for Qualcomm UEFI Secure Application (qcom.tz.uefisecapp).3* Provides access to UEFI variables on platforms where they are secured by the4* aforementioned Secure Execution Environment (SEE) application.5*6* Copyright (C) 2023 Maximilian Luz <[email protected]>7*/89#include <linux/efi.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/mutex.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/sizes.h>16#include <linux/slab.h>17#include <linux/types.h>18#include <linux/ucs2_string.h>1920#include <linux/firmware/qcom/qcom_qseecom.h>21#include <linux/firmware/qcom/qcom_scm.h>22#include <linux/firmware/qcom/qcom_tzmem.h>2324/* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */2526/* Maximum length of name string with null-terminator */27#define QSEE_MAX_NAME_LEN 10242829#define QSEE_CMD_UEFI(x) (0x8000 | (x))30#define QSEE_CMD_UEFI_GET_VARIABLE QSEE_CMD_UEFI(0)31#define QSEE_CMD_UEFI_SET_VARIABLE QSEE_CMD_UEFI(1)32#define QSEE_CMD_UEFI_GET_NEXT_VARIABLE QSEE_CMD_UEFI(2)33#define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO QSEE_CMD_UEFI(3)3435/**36* struct qsee_req_uefi_get_variable - Request for GetVariable command.37* @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_GET_VARIABLE.38* @length: Length of the request in bytes, including this struct and any39* parameters (name, GUID) stored after it as well as any padding40* thereof for alignment.41* @name_offset: Offset from the start of this struct to where the variable42* name is stored (as utf-16 string), in bytes.43* @name_size: Size of the name parameter in bytes, including null-terminator.44* @guid_offset: Offset from the start of this struct to where the GUID45* parameter is stored, in bytes.46* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).47* @data_size: Size of the output buffer, in bytes.48*/49struct qsee_req_uefi_get_variable {50u32 command_id;51u32 length;52u32 name_offset;53u32 name_size;54u32 guid_offset;55u32 guid_size;56u32 data_size;57} __packed;5859/**60* struct qsee_rsp_uefi_get_variable - Response for GetVariable command.61* @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_GET_VARIABLE.62* @length: Length of the response in bytes, including this struct and the63* returned data.64* @status: Status of this command.65* @attributes: EFI variable attributes.66* @data_offset: Offset from the start of this struct to where the data is67* stored, in bytes.68* @data_size: Size of the returned data, in bytes. In case status indicates69* that the buffer is too small, this will be the size required70* to store the EFI variable data.71*/72struct qsee_rsp_uefi_get_variable {73u32 command_id;74u32 length;75u32 status;76u32 attributes;77u32 data_offset;78u32 data_size;79} __packed;8081/**82* struct qsee_req_uefi_set_variable - Request for the SetVariable command.83* @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_SET_VARIABLE.84* @length: Length of the request in bytes, including this struct and any85* parameters (name, GUID, data) stored after it as well as any86* padding thereof required for alignment.87* @name_offset: Offset from the start of this struct to where the variable88* name is stored (as utf-16 string), in bytes.89* @name_size: Size of the name parameter in bytes, including null-terminator.90* @guid_offset: Offset from the start of this struct to where the GUID91* parameter is stored, in bytes.92* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).93* @attributes: The EFI variable attributes to set for this variable.94* @data_offset: Offset from the start of this struct to where the EFI variable95* data is stored, in bytes.96* @data_size: Size of EFI variable data, in bytes.97*98*/99struct qsee_req_uefi_set_variable {100u32 command_id;101u32 length;102u32 name_offset;103u32 name_size;104u32 guid_offset;105u32 guid_size;106u32 attributes;107u32 data_offset;108u32 data_size;109} __packed;110111/**112* struct qsee_rsp_uefi_set_variable - Response for the SetVariable command.113* @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_SET_VARIABLE.114* @length: The length of this response, i.e. the size of this struct in115* bytes.116* @status: Status of this command.117* @_unknown1: Unknown response field.118* @_unknown2: Unknown response field.119*/120struct qsee_rsp_uefi_set_variable {121u32 command_id;122u32 length;123u32 status;124u32 _unknown1;125u32 _unknown2;126} __packed;127128/**129* struct qsee_req_uefi_get_next_variable - Request for the130* GetNextVariableName command.131* @command_id: The ID of the command. Must be132* %QSEE_CMD_UEFI_GET_NEXT_VARIABLE.133* @length: Length of the request in bytes, including this struct and any134* parameters (name, GUID) stored after it as well as any padding135* thereof for alignment.136* @guid_offset: Offset from the start of this struct to where the GUID137* parameter is stored, in bytes.138* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).139* @name_offset: Offset from the start of this struct to where the variable140* name is stored (as utf-16 string), in bytes.141* @name_size: Size of the name parameter in bytes, including null-terminator.142*/143struct qsee_req_uefi_get_next_variable {144u32 command_id;145u32 length;146u32 guid_offset;147u32 guid_size;148u32 name_offset;149u32 name_size;150} __packed;151152/**153* struct qsee_rsp_uefi_get_next_variable - Response for the154* GetNextVariableName command.155* @command_id: The ID of the command. Should be156* %QSEE_CMD_UEFI_GET_NEXT_VARIABLE.157* @length: Length of the response in bytes, including this struct and any158* parameters (name, GUID) stored after it as well as any padding159* thereof for alignment.160* @status: Status of this command.161* @guid_offset: Offset from the start of this struct to where the GUID162* parameter is stored, in bytes.163* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).164* @name_offset: Offset from the start of this struct to where the variable165* name is stored (as utf-16 string), in bytes.166* @name_size: Size of the name parameter in bytes, including null-terminator.167*/168struct qsee_rsp_uefi_get_next_variable {169u32 command_id;170u32 length;171u32 status;172u32 guid_offset;173u32 guid_size;174u32 name_offset;175u32 name_size;176} __packed;177178/**179* struct qsee_req_uefi_query_variable_info - Response for the180* GetNextVariableName command.181* @command_id: The ID of the command. Must be182* %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO.183* @length: The length of this request, i.e. the size of this struct in184* bytes.185* @attributes: The storage attributes to query the info for.186*/187struct qsee_req_uefi_query_variable_info {188u32 command_id;189u32 length;190u32 attributes;191} __packed;192193/**194* struct qsee_rsp_uefi_query_variable_info - Response for the195* GetNextVariableName command.196* @command_id: The ID of the command. Must be197* %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO.198* @length: The length of this response, i.e. the size of this199* struct in bytes.200* @status: Status of this command.201* @_pad: Padding.202* @storage_space: Full storage space size, in bytes.203* @remaining_space: Free storage space available, in bytes.204* @max_variable_size: Maximum variable data size, in bytes.205*/206struct qsee_rsp_uefi_query_variable_info {207u32 command_id;208u32 length;209u32 status;210u32 _pad;211u64 storage_space;212u64 remaining_space;213u64 max_variable_size;214} __packed;215216/* -- Alignment helpers ----------------------------------------------------- */217218/*219* Helper macro to ensure proper alignment of types (fields and arrays) when220* stored in some (contiguous) buffer.221*222* Note: The driver from which this one has been reverse-engineered expects an223* alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t,224* however, has an alignment of 4 byte (32 bits). So far, this seems to work225* fine here. See also the comment on the typedef of efi_guid_t.226*227* Note: It looks like uefisecapp is quite picky about how the memory passed to228* it is structured and aligned. In particular the request/response setup used229* for QSEE_CMD_UEFI_GET_VARIABLE. While qcom_qseecom_app_send(), in theory,230* accepts separate buffers/addresses for the request and response parts, in231* practice, however, it seems to expect them to be both part of a larger232* contiguous block. We initially allocated separate buffers for the request233* and response but this caused the QSEE_CMD_UEFI_GET_VARIABLE command to234* either not write any response to the response buffer or outright crash the235* device. Therefore, we now allocate a single contiguous block of DMA memory236* for both and properly align the data using the macros below. In particular,237* request and response structs are aligned at 8 byte (via __reqdata_offs()),238* following the driver that this has been reverse-engineered from.239*/240#define qcuefi_buf_align_fields(fields...) \241({ \242size_t __len = 0; \243fields \244__len; \245})246247#define __field_impl(size, align, offset) \248({ \249size_t *__offset = (offset); \250size_t __aligned; \251\252__aligned = ALIGN(__len, align); \253__len = __aligned + (size); \254\255if (__offset) \256*__offset = __aligned; \257});258259#define __array_offs(type, count, offset) \260__field_impl(sizeof(type) * (count), __alignof__(type), offset)261262#define __array_offs_aligned(type, count, align, offset) \263__field_impl(sizeof(type) * (count), align, offset)264265#define __reqdata_offs(size, offset) \266__array_offs_aligned(u8, size, 8, offset)267268#define __array(type, count) __array_offs(type, count, NULL)269#define __field_offs(type, offset) __array_offs(type, 1, offset)270#define __field(type) __array_offs(type, 1, NULL)271272/* -- UEFI app interface. --------------------------------------------------- */273274struct qcuefi_client {275struct qseecom_client *client;276struct efivars efivars;277struct qcom_tzmem_pool *mempool;278};279280static struct device *qcuefi_dev(struct qcuefi_client *qcuefi)281{282return &qcuefi->client->aux_dev.dev;283}284285static efi_status_t qsee_uefi_status_to_efi(u32 status)286{287u64 category = status & 0xf0000000;288u64 code = status & 0x0fffffff;289290return category << (BITS_PER_LONG - 32) | code;291}292293static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name,294const efi_guid_t *guid, u32 *attributes,295unsigned long *data_size, void *data)296{297struct qsee_req_uefi_get_variable *req_data;298struct qsee_rsp_uefi_get_variable *rsp_data;299void *cmd_buf __free(qcom_tzmem) = NULL;300unsigned long buffer_size = *data_size;301unsigned long name_length;302efi_status_t efi_status;303size_t cmd_buf_size;304size_t guid_offs;305size_t name_offs;306size_t req_size;307size_t rsp_size;308size_t req_offs;309size_t rsp_offs;310ssize_t status;311312if (!name || !guid)313return EFI_INVALID_PARAMETER;314315name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;316if (name_length > QSEE_MAX_NAME_LEN)317return EFI_INVALID_PARAMETER;318319if (buffer_size && !data)320return EFI_INVALID_PARAMETER;321322req_size = qcuefi_buf_align_fields(323__field(*req_data)324__array_offs(*name, name_length, &name_offs)325__field_offs(*guid, &guid_offs)326);327328rsp_size = qcuefi_buf_align_fields(329__field(*rsp_data)330__array(u8, buffer_size)331);332333cmd_buf_size = qcuefi_buf_align_fields(334__reqdata_offs(req_size, &req_offs)335__reqdata_offs(rsp_size, &rsp_offs)336);337338cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);339if (!cmd_buf)340return EFI_OUT_OF_RESOURCES;341342req_data = cmd_buf + req_offs;343rsp_data = cmd_buf + rsp_offs;344345req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE;346req_data->data_size = buffer_size;347req_data->name_offset = name_offs;348req_data->name_size = name_length * sizeof(*name);349req_data->guid_offset = guid_offs;350req_data->guid_size = sizeof(*guid);351req_data->length = req_size;352353status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);354if (status < 0)355return EFI_INVALID_PARAMETER;356357memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);358359status = qcom_qseecom_app_send(qcuefi->client,360cmd_buf + req_offs, req_size,361cmd_buf + rsp_offs, rsp_size);362if (status)363return EFI_DEVICE_ERROR;364365if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE)366return EFI_DEVICE_ERROR;367368if (rsp_data->length < sizeof(*rsp_data))369return EFI_DEVICE_ERROR;370371if (rsp_data->status) {372dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",373__func__, rsp_data->status);374efi_status = qsee_uefi_status_to_efi(rsp_data->status);375376/* Update size and attributes in case buffer is too small. */377if (efi_status == EFI_BUFFER_TOO_SMALL) {378*data_size = rsp_data->data_size;379if (attributes)380*attributes = rsp_data->attributes;381}382383return qsee_uefi_status_to_efi(rsp_data->status);384}385386if (rsp_data->length > rsp_size)387return EFI_DEVICE_ERROR;388389if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length)390return EFI_DEVICE_ERROR;391392/*393* Note: We need to set attributes and data size even if the buffer is394* too small and we won't copy any data. This is described in spec, so395* that callers can either allocate a buffer properly (with two calls396* to this function) or just read back attributes withouth having to397* deal with that.398*399* Specifically:400* - If we have a buffer size of zero and no buffer, just return the401* attributes, required size, and indicate success.402* - If the buffer size is nonzero but too small, indicate that as an403* error.404* - Otherwise, we are good to copy the data.405*406* Note that we have already ensured above that the buffer pointer is407* non-NULL if its size is nonzero.408*/409*data_size = rsp_data->data_size;410if (attributes)411*attributes = rsp_data->attributes;412413if (buffer_size == 0 && !data)414return EFI_SUCCESS;415416if (buffer_size < rsp_data->data_size)417return EFI_BUFFER_TOO_SMALL;418419memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size);420421return EFI_SUCCESS;422}423424static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name,425const efi_guid_t *guid, u32 attributes,426unsigned long data_size, const void *data)427{428struct qsee_req_uefi_set_variable *req_data;429struct qsee_rsp_uefi_set_variable *rsp_data;430void *cmd_buf __free(qcom_tzmem) = NULL;431unsigned long name_length;432size_t cmd_buf_size;433size_t name_offs;434size_t guid_offs;435size_t data_offs;436size_t req_size;437size_t req_offs;438size_t rsp_offs;439ssize_t status;440441if (!name || !guid)442return EFI_INVALID_PARAMETER;443444name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;445if (name_length > QSEE_MAX_NAME_LEN)446return EFI_INVALID_PARAMETER;447448/*449* Make sure we have some data if data_size is nonzero. Note that using450* a size of zero is a valid use-case described in spec and deletes the451* variable.452*/453if (data_size && !data)454return EFI_INVALID_PARAMETER;455456req_size = qcuefi_buf_align_fields(457__field(*req_data)458__array_offs(*name, name_length, &name_offs)459__field_offs(*guid, &guid_offs)460__array_offs(u8, data_size, &data_offs)461);462463cmd_buf_size = qcuefi_buf_align_fields(464__reqdata_offs(req_size, &req_offs)465__reqdata_offs(sizeof(*rsp_data), &rsp_offs)466);467468cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);469if (!cmd_buf)470return EFI_OUT_OF_RESOURCES;471472req_data = cmd_buf + req_offs;473rsp_data = cmd_buf + rsp_offs;474475req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE;476req_data->attributes = attributes;477req_data->name_offset = name_offs;478req_data->name_size = name_length * sizeof(*name);479req_data->guid_offset = guid_offs;480req_data->guid_size = sizeof(*guid);481req_data->data_offset = data_offs;482req_data->data_size = data_size;483req_data->length = req_size;484485status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);486if (status < 0)487return EFI_INVALID_PARAMETER;488489memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);490491if (data_size)492memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size);493494status = qcom_qseecom_app_send(qcuefi->client,495cmd_buf + req_offs, req_size,496cmd_buf + rsp_offs, sizeof(*rsp_data));497if (status)498return EFI_DEVICE_ERROR;499500if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE)501return EFI_DEVICE_ERROR;502503if (rsp_data->length != sizeof(*rsp_data))504return EFI_DEVICE_ERROR;505506if (rsp_data->status) {507dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",508__func__, rsp_data->status);509return qsee_uefi_status_to_efi(rsp_data->status);510}511512return EFI_SUCCESS;513}514515static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,516unsigned long *name_size, efi_char16_t *name,517efi_guid_t *guid)518{519struct qsee_req_uefi_get_next_variable *req_data;520struct qsee_rsp_uefi_get_next_variable *rsp_data;521void *cmd_buf __free(qcom_tzmem) = NULL;522efi_status_t efi_status;523size_t cmd_buf_size;524size_t guid_offs;525size_t name_offs;526size_t req_size;527size_t rsp_size;528size_t req_offs;529size_t rsp_offs;530ssize_t status;531532if (!name_size || !name || !guid)533return EFI_INVALID_PARAMETER;534535if (*name_size == 0)536return EFI_INVALID_PARAMETER;537538req_size = qcuefi_buf_align_fields(539__field(*req_data)540__field_offs(*guid, &guid_offs)541__array_offs(*name, *name_size / sizeof(*name), &name_offs)542);543544rsp_size = qcuefi_buf_align_fields(545__field(*rsp_data)546__field(*guid)547__array(*name, *name_size / sizeof(*name))548);549550cmd_buf_size = qcuefi_buf_align_fields(551__reqdata_offs(req_size, &req_offs)552__reqdata_offs(rsp_size, &rsp_offs)553);554555cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);556if (!cmd_buf)557return EFI_OUT_OF_RESOURCES;558559req_data = cmd_buf + req_offs;560rsp_data = cmd_buf + rsp_offs;561562req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE;563req_data->guid_offset = guid_offs;564req_data->guid_size = sizeof(*guid);565req_data->name_offset = name_offs;566req_data->name_size = *name_size;567req_data->length = req_size;568569memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);570status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name,571*name_size / sizeof(*name));572if (status < 0)573return EFI_INVALID_PARAMETER;574575status = qcom_qseecom_app_send(qcuefi->client,576cmd_buf + req_offs, req_size,577cmd_buf + rsp_offs, rsp_size);578if (status)579return EFI_DEVICE_ERROR;580581if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE)582return EFI_DEVICE_ERROR;583584if (rsp_data->length < sizeof(*rsp_data))585return EFI_DEVICE_ERROR;586587if (rsp_data->status) {588dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",589__func__, rsp_data->status);590efi_status = qsee_uefi_status_to_efi(rsp_data->status);591592/*593* If the buffer to hold the name is too small, update the594* name_size with the required size, so that callers can595* reallocate it accordingly.596*/597if (efi_status == EFI_BUFFER_TOO_SMALL)598*name_size = rsp_data->name_size;599600return efi_status;601}602603if (rsp_data->length > rsp_size)604return EFI_DEVICE_ERROR;605606if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length)607return EFI_DEVICE_ERROR;608609if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length)610return EFI_DEVICE_ERROR;611612if (rsp_data->name_size > *name_size) {613*name_size = rsp_data->name_size;614return EFI_BUFFER_TOO_SMALL;615}616617if (rsp_data->guid_size != sizeof(*guid))618return EFI_DEVICE_ERROR;619620memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size);621status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset,622rsp_data->name_size / sizeof(*name));623*name_size = rsp_data->name_size;624625if (status < 0)626/*627* Return EFI_DEVICE_ERROR here because the buffer size should628* have already been validated above, causing this function to629* bail with EFI_BUFFER_TOO_SMALL.630*/631return EFI_DEVICE_ERROR;632633return EFI_SUCCESS;634}635636static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr,637u64 *storage_space, u64 *remaining_space,638u64 *max_variable_size)639{640struct qsee_req_uefi_query_variable_info *req_data;641struct qsee_rsp_uefi_query_variable_info *rsp_data;642void *cmd_buf __free(qcom_tzmem) = NULL;643size_t cmd_buf_size;644size_t req_offs;645size_t rsp_offs;646int status;647648cmd_buf_size = qcuefi_buf_align_fields(649__reqdata_offs(sizeof(*req_data), &req_offs)650__reqdata_offs(sizeof(*rsp_data), &rsp_offs)651);652653cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);654if (!cmd_buf)655return EFI_OUT_OF_RESOURCES;656657req_data = cmd_buf + req_offs;658rsp_data = cmd_buf + rsp_offs;659660req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO;661req_data->attributes = attr;662req_data->length = sizeof(*req_data);663664status = qcom_qseecom_app_send(qcuefi->client,665cmd_buf + req_offs, sizeof(*req_data),666cmd_buf + rsp_offs, sizeof(*rsp_data));667if (status)668return EFI_DEVICE_ERROR;669670if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO)671return EFI_DEVICE_ERROR;672673if (rsp_data->length != sizeof(*rsp_data))674return EFI_DEVICE_ERROR;675676if (rsp_data->status) {677dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",678__func__, rsp_data->status);679return qsee_uefi_status_to_efi(rsp_data->status);680}681682if (storage_space)683*storage_space = rsp_data->storage_space;684685if (remaining_space)686*remaining_space = rsp_data->remaining_space;687688if (max_variable_size)689*max_variable_size = rsp_data->max_variable_size;690691return EFI_SUCCESS;692}693694/* -- Global efivar interface. ---------------------------------------------- */695696static struct qcuefi_client *__qcuefi;697static DEFINE_MUTEX(__qcuefi_lock);698699static int qcuefi_set_reference(struct qcuefi_client *qcuefi)700{701mutex_lock(&__qcuefi_lock);702703if (qcuefi && __qcuefi) {704mutex_unlock(&__qcuefi_lock);705return -EEXIST;706}707708__qcuefi = qcuefi;709710mutex_unlock(&__qcuefi_lock);711return 0;712}713714static struct qcuefi_client *qcuefi_acquire(void)715{716mutex_lock(&__qcuefi_lock);717if (!__qcuefi) {718mutex_unlock(&__qcuefi_lock);719return NULL;720}721return __qcuefi;722}723724static void qcuefi_release(void)725{726mutex_unlock(&__qcuefi_lock);727}728729static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr,730unsigned long *data_size, void *data)731{732struct qcuefi_client *qcuefi;733efi_status_t status;734735qcuefi = qcuefi_acquire();736if (!qcuefi)737return EFI_NOT_READY;738739status = qsee_uefi_get_variable(qcuefi, name, vendor, attr, data_size, data);740741qcuefi_release();742return status;743}744745static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor,746u32 attr, unsigned long data_size, void *data)747{748struct qcuefi_client *qcuefi;749efi_status_t status;750751qcuefi = qcuefi_acquire();752if (!qcuefi)753return EFI_NOT_READY;754755status = qsee_uefi_set_variable(qcuefi, name, vendor, attr, data_size, data);756757qcuefi_release();758return status;759}760761static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name,762efi_guid_t *vendor)763{764struct qcuefi_client *qcuefi;765efi_status_t status;766767qcuefi = qcuefi_acquire();768if (!qcuefi)769return EFI_NOT_READY;770771status = qsee_uefi_get_next_variable(qcuefi, name_size, name, vendor);772773qcuefi_release();774return status;775}776777static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space,778u64 *max_variable_size)779{780struct qcuefi_client *qcuefi;781efi_status_t status;782783qcuefi = qcuefi_acquire();784if (!qcuefi)785return EFI_NOT_READY;786787status = qsee_uefi_query_variable_info(qcuefi, attr, storage_space, remaining_space,788max_variable_size);789790qcuefi_release();791return status;792}793794static const struct efivar_operations qcom_efivar_ops = {795.get_variable = qcuefi_get_variable,796.set_variable = qcuefi_set_variable,797.get_next_variable = qcuefi_get_next_variable,798.query_variable_info = qcuefi_query_variable_info,799};800801/* -- Driver setup. --------------------------------------------------------- */802803static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev,804const struct auxiliary_device_id *aux_dev_id)805{806struct qcom_tzmem_pool_config pool_config;807struct qcuefi_client *qcuefi;808int status;809810qcuefi = devm_kzalloc(&aux_dev->dev, sizeof(*qcuefi), GFP_KERNEL);811if (!qcuefi)812return -ENOMEM;813814qcuefi->client = container_of(aux_dev, struct qseecom_client, aux_dev);815816memset(&pool_config, 0, sizeof(pool_config));817pool_config.initial_size = SZ_4K;818pool_config.policy = QCOM_TZMEM_POLICY_MULTIPLIER;819pool_config.increment = 2;820pool_config.max_size = SZ_256K;821822qcuefi->mempool = devm_qcom_tzmem_pool_new(&aux_dev->dev, &pool_config);823if (IS_ERR(qcuefi->mempool))824return PTR_ERR(qcuefi->mempool);825826auxiliary_set_drvdata(aux_dev, qcuefi);827status = qcuefi_set_reference(qcuefi);828if (status)829return status;830831status = efivars_register(&qcuefi->efivars, &qcom_efivar_ops);832if (status)833qcuefi_set_reference(NULL);834835return status;836}837838static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev)839{840struct qcuefi_client *qcuefi = auxiliary_get_drvdata(aux_dev);841842efivars_unregister(&qcuefi->efivars);843qcuefi_set_reference(NULL);844}845846static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = {847{ .name = "qcom_qseecom.uefisecapp" },848{}849};850MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table);851852static struct auxiliary_driver qcom_uefisecapp_driver = {853.probe = qcom_uefisecapp_probe,854.remove = qcom_uefisecapp_remove,855.id_table = qcom_uefisecapp_id_table,856.driver = {857.name = "qcom_qseecom_uefisecapp",858.probe_type = PROBE_PREFER_ASYNCHRONOUS,859},860};861module_auxiliary_driver(qcom_uefisecapp_driver);862863MODULE_AUTHOR("Maximilian Luz <[email protected]>");864MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App");865MODULE_LICENSE("GPL");866867868