| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866 |
- // 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 <luzmaximilian@gmail.com>
- */
- #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 1024
- #define QSEE_CMD_UEFI(x) (0x8000 | (x))
- #define QSEE_CMD_UEFI_GET_VARIABLE QSEE_CMD_UEFI(0)
- #define QSEE_CMD_UEFI_SET_VARIABLE QSEE_CMD_UEFI(1)
- #define QSEE_CMD_UEFI_GET_NEXT_VARIABLE QSEE_CMD_UEFI(2)
- #define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO QSEE_CMD_UEFI(3)
- /**
- * 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 {
- u32 command_id;
- u32 length;
- u32 name_offset;
- u32 name_size;
- u32 guid_offset;
- u32 guid_size;
- u32 data_size;
- } __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 {
- u32 command_id;
- u32 length;
- u32 status;
- u32 attributes;
- u32 data_offset;
- u32 data_size;
- } __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 {
- u32 command_id;
- u32 length;
- u32 name_offset;
- u32 name_size;
- u32 guid_offset;
- u32 guid_size;
- u32 attributes;
- u32 data_offset;
- u32 data_size;
- } __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 {
- u32 command_id;
- u32 length;
- u32 status;
- u32 _unknown1;
- u32 _unknown2;
- } __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 {
- u32 command_id;
- u32 length;
- u32 guid_offset;
- u32 guid_size;
- u32 name_offset;
- u32 name_size;
- } __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 {
- u32 command_id;
- u32 length;
- u32 status;
- u32 guid_offset;
- u32 guid_size;
- u32 name_offset;
- u32 name_size;
- } __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 {
- u32 command_id;
- u32 length;
- u32 attributes;
- } __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 {
- u32 command_id;
- u32 length;
- u32 status;
- u32 _pad;
- u64 storage_space;
- u64 remaining_space;
- u64 max_variable_size;
- } __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...) \
- ({ \
- size_t __len = 0; \
- fields \
- __len; \
- })
- #define __field_impl(size, align, offset) \
- ({ \
- size_t *__offset = (offset); \
- size_t __aligned; \
- \
- __aligned = ALIGN(__len, align); \
- __len = __aligned + (size); \
- \
- if (__offset) \
- *__offset = __aligned; \
- });
- #define __array_offs(type, count, offset) \
- __field_impl(sizeof(type) * (count), __alignof__(type), offset)
- #define __array_offs_aligned(type, count, align, offset) \
- __field_impl(sizeof(type) * (count), align, offset)
- #define __reqdata_offs(size, offset) \
- __array_offs_aligned(u8, size, 8, offset)
- #define __array(type, count) __array_offs(type, count, NULL)
- #define __field_offs(type, offset) __array_offs(type, 1, offset)
- #define __field(type) __array_offs(type, 1, NULL)
- /* -- UEFI app interface. --------------------------------------------------- */
- struct qcuefi_client {
- struct qseecom_client *client;
- struct efivars efivars;
- struct qcom_tzmem_pool *mempool;
- };
- static struct device *qcuefi_dev(struct qcuefi_client *qcuefi)
- {
- return &qcuefi->client->aux_dev.dev;
- }
- static efi_status_t qsee_uefi_status_to_efi(u32 status)
- {
- u64 category = status & 0xf0000000;
- u64 code = status & 0x0fffffff;
- return category << (BITS_PER_LONG - 32) | code;
- }
- 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)
- {
- struct qsee_req_uefi_get_variable *req_data;
- struct qsee_rsp_uefi_get_variable *rsp_data;
- void *cmd_buf __free(qcom_tzmem) = NULL;
- unsigned long buffer_size = *data_size;
- unsigned long name_length;
- efi_status_t efi_status;
- size_t cmd_buf_size;
- size_t guid_offs;
- size_t name_offs;
- size_t req_size;
- size_t rsp_size;
- size_t req_offs;
- size_t rsp_offs;
- ssize_t status;
- if (!name || !guid)
- return EFI_INVALID_PARAMETER;
- name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;
- if (name_length > QSEE_MAX_NAME_LEN)
- return EFI_INVALID_PARAMETER;
- if (buffer_size && !data)
- return EFI_INVALID_PARAMETER;
- req_size = qcuefi_buf_align_fields(
- __field(*req_data)
- __array_offs(*name, name_length, &name_offs)
- __field_offs(*guid, &guid_offs)
- );
- rsp_size = qcuefi_buf_align_fields(
- __field(*rsp_data)
- __array(u8, buffer_size)
- );
- cmd_buf_size = qcuefi_buf_align_fields(
- __reqdata_offs(req_size, &req_offs)
- __reqdata_offs(rsp_size, &rsp_offs)
- );
- cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
- if (!cmd_buf)
- return EFI_OUT_OF_RESOURCES;
- req_data = cmd_buf + req_offs;
- rsp_data = cmd_buf + rsp_offs;
- req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE;
- req_data->data_size = buffer_size;
- req_data->name_offset = name_offs;
- req_data->name_size = name_length * sizeof(*name);
- req_data->guid_offset = guid_offs;
- req_data->guid_size = sizeof(*guid);
- req_data->length = req_size;
- status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
- if (status < 0)
- return EFI_INVALID_PARAMETER;
- memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
- status = qcom_qseecom_app_send(qcuefi->client,
- cmd_buf + req_offs, req_size,
- cmd_buf + rsp_offs, rsp_size);
- if (status)
- return EFI_DEVICE_ERROR;
- if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE)
- return EFI_DEVICE_ERROR;
- if (rsp_data->length < sizeof(*rsp_data))
- return EFI_DEVICE_ERROR;
- if (rsp_data->status) {
- dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
- __func__, rsp_data->status);
- efi_status = qsee_uefi_status_to_efi(rsp_data->status);
- /* Update size and attributes in case buffer is too small. */
- if (efi_status == EFI_BUFFER_TOO_SMALL) {
- *data_size = rsp_data->data_size;
- if (attributes)
- *attributes = rsp_data->attributes;
- }
- return qsee_uefi_status_to_efi(rsp_data->status);
- }
- if (rsp_data->length > rsp_size)
- return EFI_DEVICE_ERROR;
- if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length)
- return EFI_DEVICE_ERROR;
- /*
- * Note: We need to set attributes and data size even if the buffer is
- * too small and we won't copy any data. This is described in spec, so
- * that callers can either allocate a buffer properly (with two calls
- * to this function) or just read back attributes withouth having to
- * deal with that.
- *
- * Specifically:
- * - If we have a buffer size of zero and no buffer, just return the
- * attributes, required size, and indicate success.
- * - If the buffer size is nonzero but too small, indicate that as an
- * error.
- * - Otherwise, we are good to copy the data.
- *
- * Note that we have already ensured above that the buffer pointer is
- * non-NULL if its size is nonzero.
- */
- *data_size = rsp_data->data_size;
- if (attributes)
- *attributes = rsp_data->attributes;
- if (buffer_size == 0 && !data)
- return EFI_SUCCESS;
- if (buffer_size < rsp_data->data_size)
- return EFI_BUFFER_TOO_SMALL;
- memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size);
- return EFI_SUCCESS;
- }
- 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)
- {
- struct qsee_req_uefi_set_variable *req_data;
- struct qsee_rsp_uefi_set_variable *rsp_data;
- void *cmd_buf __free(qcom_tzmem) = NULL;
- unsigned long name_length;
- size_t cmd_buf_size;
- size_t name_offs;
- size_t guid_offs;
- size_t data_offs;
- size_t req_size;
- size_t req_offs;
- size_t rsp_offs;
- ssize_t status;
- if (!name || !guid)
- return EFI_INVALID_PARAMETER;
- name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;
- if (name_length > QSEE_MAX_NAME_LEN)
- return EFI_INVALID_PARAMETER;
- /*
- * Make sure we have some data if data_size is nonzero. Note that using
- * a size of zero is a valid use-case described in spec and deletes the
- * variable.
- */
- if (data_size && !data)
- return EFI_INVALID_PARAMETER;
- req_size = qcuefi_buf_align_fields(
- __field(*req_data)
- __array_offs(*name, name_length, &name_offs)
- __field_offs(*guid, &guid_offs)
- __array_offs(u8, data_size, &data_offs)
- );
- cmd_buf_size = qcuefi_buf_align_fields(
- __reqdata_offs(req_size, &req_offs)
- __reqdata_offs(sizeof(*rsp_data), &rsp_offs)
- );
- cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
- if (!cmd_buf)
- return EFI_OUT_OF_RESOURCES;
- req_data = cmd_buf + req_offs;
- rsp_data = cmd_buf + rsp_offs;
- req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE;
- req_data->attributes = attributes;
- req_data->name_offset = name_offs;
- req_data->name_size = name_length * sizeof(*name);
- req_data->guid_offset = guid_offs;
- req_data->guid_size = sizeof(*guid);
- req_data->data_offset = data_offs;
- req_data->data_size = data_size;
- req_data->length = req_size;
- status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
- if (status < 0)
- return EFI_INVALID_PARAMETER;
- memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
- if (data_size)
- memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size);
- status = qcom_qseecom_app_send(qcuefi->client,
- cmd_buf + req_offs, req_size,
- cmd_buf + rsp_offs, sizeof(*rsp_data));
- if (status)
- return EFI_DEVICE_ERROR;
- if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE)
- return EFI_DEVICE_ERROR;
- if (rsp_data->length != sizeof(*rsp_data))
- return EFI_DEVICE_ERROR;
- if (rsp_data->status) {
- dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
- __func__, rsp_data->status);
- return qsee_uefi_status_to_efi(rsp_data->status);
- }
- return EFI_SUCCESS;
- }
- 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)
- {
- struct qsee_req_uefi_get_next_variable *req_data;
- struct qsee_rsp_uefi_get_next_variable *rsp_data;
- void *cmd_buf __free(qcom_tzmem) = NULL;
- efi_status_t efi_status;
- size_t cmd_buf_size;
- size_t guid_offs;
- size_t name_offs;
- size_t req_size;
- size_t rsp_size;
- size_t req_offs;
- size_t rsp_offs;
- ssize_t status;
- if (!name_size || !name || !guid)
- return EFI_INVALID_PARAMETER;
- if (*name_size == 0)
- return EFI_INVALID_PARAMETER;
- req_size = qcuefi_buf_align_fields(
- __field(*req_data)
- __field_offs(*guid, &guid_offs)
- __array_offs(*name, *name_size / sizeof(*name), &name_offs)
- );
- rsp_size = qcuefi_buf_align_fields(
- __field(*rsp_data)
- __field(*guid)
- __array(*name, *name_size / sizeof(*name))
- );
- cmd_buf_size = qcuefi_buf_align_fields(
- __reqdata_offs(req_size, &req_offs)
- __reqdata_offs(rsp_size, &rsp_offs)
- );
- cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
- if (!cmd_buf)
- return EFI_OUT_OF_RESOURCES;
- req_data = cmd_buf + req_offs;
- rsp_data = cmd_buf + rsp_offs;
- req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE;
- req_data->guid_offset = guid_offs;
- req_data->guid_size = sizeof(*guid);
- req_data->name_offset = name_offs;
- req_data->name_size = *name_size;
- req_data->length = req_size;
- memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
- status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name,
- *name_size / sizeof(*name));
- if (status < 0)
- return EFI_INVALID_PARAMETER;
- status = qcom_qseecom_app_send(qcuefi->client,
- cmd_buf + req_offs, req_size,
- cmd_buf + rsp_offs, rsp_size);
- if (status)
- return EFI_DEVICE_ERROR;
- if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE)
- return EFI_DEVICE_ERROR;
- if (rsp_data->length < sizeof(*rsp_data))
- return EFI_DEVICE_ERROR;
- if (rsp_data->status) {
- dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
- __func__, rsp_data->status);
- efi_status = qsee_uefi_status_to_efi(rsp_data->status);
- /*
- * If the buffer to hold the name is too small, update the
- * name_size with the required size, so that callers can
- * reallocate it accordingly.
- */
- if (efi_status == EFI_BUFFER_TOO_SMALL)
- *name_size = rsp_data->name_size;
- return efi_status;
- }
- if (rsp_data->length > rsp_size)
- return EFI_DEVICE_ERROR;
- if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length)
- return EFI_DEVICE_ERROR;
- if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length)
- return EFI_DEVICE_ERROR;
- if (rsp_data->name_size > *name_size) {
- *name_size = rsp_data->name_size;
- return EFI_BUFFER_TOO_SMALL;
- }
- if (rsp_data->guid_size != sizeof(*guid))
- return EFI_DEVICE_ERROR;
- memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size);
- status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset,
- rsp_data->name_size / sizeof(*name));
- *name_size = rsp_data->name_size;
- if (status < 0)
- /*
- * Return EFI_DEVICE_ERROR here because the buffer size should
- * have already been validated above, causing this function to
- * bail with EFI_BUFFER_TOO_SMALL.
- */
- return EFI_DEVICE_ERROR;
- return EFI_SUCCESS;
- }
- 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)
- {
- struct qsee_req_uefi_query_variable_info *req_data;
- struct qsee_rsp_uefi_query_variable_info *rsp_data;
- void *cmd_buf __free(qcom_tzmem) = NULL;
- size_t cmd_buf_size;
- size_t req_offs;
- size_t rsp_offs;
- int status;
- cmd_buf_size = qcuefi_buf_align_fields(
- __reqdata_offs(sizeof(*req_data), &req_offs)
- __reqdata_offs(sizeof(*rsp_data), &rsp_offs)
- );
- cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
- if (!cmd_buf)
- return EFI_OUT_OF_RESOURCES;
- req_data = cmd_buf + req_offs;
- rsp_data = cmd_buf + rsp_offs;
- req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO;
- req_data->attributes = attr;
- req_data->length = sizeof(*req_data);
- status = qcom_qseecom_app_send(qcuefi->client,
- cmd_buf + req_offs, sizeof(*req_data),
- cmd_buf + rsp_offs, sizeof(*rsp_data));
- if (status)
- return EFI_DEVICE_ERROR;
- if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO)
- return EFI_DEVICE_ERROR;
- if (rsp_data->length != sizeof(*rsp_data))
- return EFI_DEVICE_ERROR;
- if (rsp_data->status) {
- dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
- __func__, rsp_data->status);
- return qsee_uefi_status_to_efi(rsp_data->status);
- }
- if (storage_space)
- *storage_space = rsp_data->storage_space;
- if (remaining_space)
- *remaining_space = rsp_data->remaining_space;
- if (max_variable_size)
- *max_variable_size = rsp_data->max_variable_size;
- return EFI_SUCCESS;
- }
- /* -- Global efivar interface. ---------------------------------------------- */
- static struct qcuefi_client *__qcuefi;
- static DEFINE_MUTEX(__qcuefi_lock);
- static int qcuefi_set_reference(struct qcuefi_client *qcuefi)
- {
- mutex_lock(&__qcuefi_lock);
- if (qcuefi && __qcuefi) {
- mutex_unlock(&__qcuefi_lock);
- return -EEXIST;
- }
- __qcuefi = qcuefi;
- mutex_unlock(&__qcuefi_lock);
- return 0;
- }
- static struct qcuefi_client *qcuefi_acquire(void)
- {
- mutex_lock(&__qcuefi_lock);
- if (!__qcuefi) {
- mutex_unlock(&__qcuefi_lock);
- return NULL;
- }
- return __qcuefi;
- }
- static void qcuefi_release(void)
- {
- mutex_unlock(&__qcuefi_lock);
- }
- static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr,
- unsigned long *data_size, void *data)
- {
- struct qcuefi_client *qcuefi;
- efi_status_t status;
- qcuefi = qcuefi_acquire();
- if (!qcuefi)
- return EFI_NOT_READY;
- status = qsee_uefi_get_variable(qcuefi, name, vendor, attr, data_size, data);
- qcuefi_release();
- return status;
- }
- static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor,
- u32 attr, unsigned long data_size, void *data)
- {
- struct qcuefi_client *qcuefi;
- efi_status_t status;
- qcuefi = qcuefi_acquire();
- if (!qcuefi)
- return EFI_NOT_READY;
- status = qsee_uefi_set_variable(qcuefi, name, vendor, attr, data_size, data);
- qcuefi_release();
- return status;
- }
- static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name,
- efi_guid_t *vendor)
- {
- struct qcuefi_client *qcuefi;
- efi_status_t status;
- qcuefi = qcuefi_acquire();
- if (!qcuefi)
- return EFI_NOT_READY;
- status = qsee_uefi_get_next_variable(qcuefi, name_size, name, vendor);
- qcuefi_release();
- return status;
- }
- static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space,
- u64 *max_variable_size)
- {
- struct qcuefi_client *qcuefi;
- efi_status_t status;
- qcuefi = qcuefi_acquire();
- if (!qcuefi)
- return EFI_NOT_READY;
- status = qsee_uefi_query_variable_info(qcuefi, attr, storage_space, remaining_space,
- max_variable_size);
- qcuefi_release();
- return status;
- }
- static const struct efivar_operations qcom_efivar_ops = {
- .get_variable = qcuefi_get_variable,
- .set_variable = qcuefi_set_variable,
- .get_next_variable = qcuefi_get_next_variable,
- .query_variable_info = qcuefi_query_variable_info,
- };
- /* -- Driver setup. --------------------------------------------------------- */
- static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev,
- const struct auxiliary_device_id *aux_dev_id)
- {
- struct qcom_tzmem_pool_config pool_config;
- struct qcuefi_client *qcuefi;
- int status;
- qcuefi = devm_kzalloc(&aux_dev->dev, sizeof(*qcuefi), GFP_KERNEL);
- if (!qcuefi)
- return -ENOMEM;
- qcuefi->client = container_of(aux_dev, struct qseecom_client, aux_dev);
- memset(&pool_config, 0, sizeof(pool_config));
- pool_config.initial_size = SZ_4K;
- pool_config.policy = QCOM_TZMEM_POLICY_MULTIPLIER;
- pool_config.increment = 2;
- pool_config.max_size = SZ_256K;
- qcuefi->mempool = devm_qcom_tzmem_pool_new(&aux_dev->dev, &pool_config);
- if (IS_ERR(qcuefi->mempool))
- return PTR_ERR(qcuefi->mempool);
- auxiliary_set_drvdata(aux_dev, qcuefi);
- status = qcuefi_set_reference(qcuefi);
- if (status)
- return status;
- status = efivars_register(&qcuefi->efivars, &qcom_efivar_ops);
- if (status)
- qcuefi_set_reference(NULL);
- return status;
- }
- static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev)
- {
- struct qcuefi_client *qcuefi = auxiliary_get_drvdata(aux_dev);
- efivars_unregister(&qcuefi->efivars);
- qcuefi_set_reference(NULL);
- }
- static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = {
- { .name = "qcom_qseecom.uefisecapp" },
- {}
- };
- MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table);
- static struct auxiliary_driver qcom_uefisecapp_driver = {
- .probe = qcom_uefisecapp_probe,
- .remove = qcom_uefisecapp_remove,
- .id_table = qcom_uefisecapp_id_table,
- .driver = {
- .name = "qcom_qseecom_uefisecapp",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
- };
- module_auxiliary_driver(qcom_uefisecapp_driver);
- MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
- MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App");
- MODULE_LICENSE("GPL");
|