867 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Client driver for Qualcomm UEFI Secure Application (qcom.tz.uefisecapp).4 * Provides access to UEFI variables on platforms where they are secured by the5 * aforementioned Secure Execution Environment (SEE) application.6 *7 * Copyright (C) 2023 Maximilian Luz <luzmaximilian@gmail.com>8 */9 10#include <linux/efi.h>11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/mutex.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/sizes.h>17#include <linux/slab.h>18#include <linux/types.h>19#include <linux/ucs2_string.h>20 21#include <linux/firmware/qcom/qcom_qseecom.h>22#include <linux/firmware/qcom/qcom_scm.h>23#include <linux/firmware/qcom/qcom_tzmem.h>24 25/* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */26 27/* Maximum length of name string with null-terminator */28#define QSEE_MAX_NAME_LEN 102429 30#define QSEE_CMD_UEFI(x) (0x8000 | (x))31#define QSEE_CMD_UEFI_GET_VARIABLE QSEE_CMD_UEFI(0)32#define QSEE_CMD_UEFI_SET_VARIABLE QSEE_CMD_UEFI(1)33#define QSEE_CMD_UEFI_GET_NEXT_VARIABLE QSEE_CMD_UEFI(2)34#define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO QSEE_CMD_UEFI(3)35 36/**37 * struct qsee_req_uefi_get_variable - Request for GetVariable command.38 * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_GET_VARIABLE.39 * @length: Length of the request in bytes, including this struct and any40 * parameters (name, GUID) stored after it as well as any padding41 * thereof for alignment.42 * @name_offset: Offset from the start of this struct to where the variable43 * name is stored (as utf-16 string), in bytes.44 * @name_size: Size of the name parameter in bytes, including null-terminator.45 * @guid_offset: Offset from the start of this struct to where the GUID46 * parameter is stored, in bytes.47 * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).48 * @data_size: Size of the output buffer, in bytes.49 */50struct qsee_req_uefi_get_variable {51 u32 command_id;52 u32 length;53 u32 name_offset;54 u32 name_size;55 u32 guid_offset;56 u32 guid_size;57 u32 data_size;58} __packed;59 60/**61 * struct qsee_rsp_uefi_get_variable - Response for GetVariable command.62 * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_GET_VARIABLE.63 * @length: Length of the response in bytes, including this struct and the64 * returned data.65 * @status: Status of this command.66 * @attributes: EFI variable attributes.67 * @data_offset: Offset from the start of this struct to where the data is68 * stored, in bytes.69 * @data_size: Size of the returned data, in bytes. In case status indicates70 * that the buffer is too small, this will be the size required71 * to store the EFI variable data.72 */73struct qsee_rsp_uefi_get_variable {74 u32 command_id;75 u32 length;76 u32 status;77 u32 attributes;78 u32 data_offset;79 u32 data_size;80} __packed;81 82/**83 * struct qsee_req_uefi_set_variable - Request for the SetVariable command.84 * @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_SET_VARIABLE.85 * @length: Length of the request in bytes, including this struct and any86 * parameters (name, GUID, data) stored after it as well as any87 * padding thereof required for alignment.88 * @name_offset: Offset from the start of this struct to where the variable89 * name is stored (as utf-16 string), in bytes.90 * @name_size: Size of the name parameter in bytes, including null-terminator.91 * @guid_offset: Offset from the start of this struct to where the GUID92 * parameter is stored, in bytes.93 * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).94 * @attributes: The EFI variable attributes to set for this variable.95 * @data_offset: Offset from the start of this struct to where the EFI variable96 * data is stored, in bytes.97 * @data_size: Size of EFI variable data, in bytes.98 *99 */100struct qsee_req_uefi_set_variable {101 u32 command_id;102 u32 length;103 u32 name_offset;104 u32 name_size;105 u32 guid_offset;106 u32 guid_size;107 u32 attributes;108 u32 data_offset;109 u32 data_size;110} __packed;111 112/**113 * struct qsee_rsp_uefi_set_variable - Response for the SetVariable command.114 * @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_SET_VARIABLE.115 * @length: The length of this response, i.e. the size of this struct in116 * bytes.117 * @status: Status of this command.118 * @_unknown1: Unknown response field.119 * @_unknown2: Unknown response field.120 */121struct qsee_rsp_uefi_set_variable {122 u32 command_id;123 u32 length;124 u32 status;125 u32 _unknown1;126 u32 _unknown2;127} __packed;128 129/**130 * struct qsee_req_uefi_get_next_variable - Request for the131 * GetNextVariableName command.132 * @command_id: The ID of the command. Must be133 * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE.134 * @length: Length of the request in bytes, including this struct and any135 * parameters (name, GUID) stored after it as well as any padding136 * thereof for alignment.137 * @guid_offset: Offset from the start of this struct to where the GUID138 * parameter is stored, in bytes.139 * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).140 * @name_offset: Offset from the start of this struct to where the variable141 * name is stored (as utf-16 string), in bytes.142 * @name_size: Size of the name parameter in bytes, including null-terminator.143 */144struct qsee_req_uefi_get_next_variable {145 u32 command_id;146 u32 length;147 u32 guid_offset;148 u32 guid_size;149 u32 name_offset;150 u32 name_size;151} __packed;152 153/**154 * struct qsee_rsp_uefi_get_next_variable - Response for the155 * GetNextVariableName command.156 * @command_id: The ID of the command. Should be157 * %QSEE_CMD_UEFI_GET_NEXT_VARIABLE.158 * @length: Length of the response in bytes, including this struct and any159 * parameters (name, GUID) stored after it as well as any padding160 * thereof for alignment.161 * @status: Status of this command.162 * @guid_offset: Offset from the start of this struct to where the GUID163 * parameter is stored, in bytes.164 * @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).165 * @name_offset: Offset from the start of this struct to where the variable166 * name is stored (as utf-16 string), in bytes.167 * @name_size: Size of the name parameter in bytes, including null-terminator.168 */169struct qsee_rsp_uefi_get_next_variable {170 u32 command_id;171 u32 length;172 u32 status;173 u32 guid_offset;174 u32 guid_size;175 u32 name_offset;176 u32 name_size;177} __packed;178 179/**180 * struct qsee_req_uefi_query_variable_info - Response for the181 * GetNextVariableName command.182 * @command_id: The ID of the command. Must be183 * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO.184 * @length: The length of this request, i.e. the size of this struct in185 * bytes.186 * @attributes: The storage attributes to query the info for.187 */188struct qsee_req_uefi_query_variable_info {189 u32 command_id;190 u32 length;191 u32 attributes;192} __packed;193 194/**195 * struct qsee_rsp_uefi_query_variable_info - Response for the196 * GetNextVariableName command.197 * @command_id: The ID of the command. Must be198 * %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO.199 * @length: The length of this response, i.e. the size of this200 * struct in bytes.201 * @status: Status of this command.202 * @_pad: Padding.203 * @storage_space: Full storage space size, in bytes.204 * @remaining_space: Free storage space available, in bytes.205 * @max_variable_size: Maximum variable data size, in bytes.206 */207struct qsee_rsp_uefi_query_variable_info {208 u32 command_id;209 u32 length;210 u32 status;211 u32 _pad;212 u64 storage_space;213 u64 remaining_space;214 u64 max_variable_size;215} __packed;216 217/* -- Alignment helpers ----------------------------------------------------- */218 219/*220 * Helper macro to ensure proper alignment of types (fields and arrays) when221 * stored in some (contiguous) buffer.222 *223 * Note: The driver from which this one has been reverse-engineered expects an224 * alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t,225 * however, has an alignment of 4 byte (32 bits). So far, this seems to work226 * fine here. See also the comment on the typedef of efi_guid_t.227 *228 * Note: It looks like uefisecapp is quite picky about how the memory passed to229 * it is structured and aligned. In particular the request/response setup used230 * for QSEE_CMD_UEFI_GET_VARIABLE. While qcom_qseecom_app_send(), in theory,231 * accepts separate buffers/addresses for the request and response parts, in232 * practice, however, it seems to expect them to be both part of a larger233 * contiguous block. We initially allocated separate buffers for the request234 * and response but this caused the QSEE_CMD_UEFI_GET_VARIABLE command to235 * either not write any response to the response buffer or outright crash the236 * device. Therefore, we now allocate a single contiguous block of DMA memory237 * for both and properly align the data using the macros below. In particular,238 * request and response structs are aligned at 8 byte (via __reqdata_offs()),239 * following the driver that this has been reverse-engineered from.240 */241#define qcuefi_buf_align_fields(fields...) \242 ({ \243 size_t __len = 0; \244 fields \245 __len; \246 })247 248#define __field_impl(size, align, offset) \249 ({ \250 size_t *__offset = (offset); \251 size_t __aligned; \252 \253 __aligned = ALIGN(__len, align); \254 __len = __aligned + (size); \255 \256 if (__offset) \257 *__offset = __aligned; \258 });259 260#define __array_offs(type, count, offset) \261 __field_impl(sizeof(type) * (count), __alignof__(type), offset)262 263#define __array_offs_aligned(type, count, align, offset) \264 __field_impl(sizeof(type) * (count), align, offset)265 266#define __reqdata_offs(size, offset) \267 __array_offs_aligned(u8, size, 8, offset)268 269#define __array(type, count) __array_offs(type, count, NULL)270#define __field_offs(type, offset) __array_offs(type, 1, offset)271#define __field(type) __array_offs(type, 1, NULL)272 273/* -- UEFI app interface. --------------------------------------------------- */274 275struct qcuefi_client {276 struct qseecom_client *client;277 struct efivars efivars;278 struct qcom_tzmem_pool *mempool;279};280 281static struct device *qcuefi_dev(struct qcuefi_client *qcuefi)282{283 return &qcuefi->client->aux_dev.dev;284}285 286static efi_status_t qsee_uefi_status_to_efi(u32 status)287{288 u64 category = status & 0xf0000000;289 u64 code = status & 0x0fffffff;290 291 return category << (BITS_PER_LONG - 32) | code;292}293 294static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name,295 const efi_guid_t *guid, u32 *attributes,296 unsigned long *data_size, void *data)297{298 struct qsee_req_uefi_get_variable *req_data;299 struct qsee_rsp_uefi_get_variable *rsp_data;300 void *cmd_buf __free(qcom_tzmem) = NULL;301 unsigned long buffer_size = *data_size;302 unsigned long name_length;303 efi_status_t efi_status;304 size_t cmd_buf_size;305 size_t guid_offs;306 size_t name_offs;307 size_t req_size;308 size_t rsp_size;309 size_t req_offs;310 size_t rsp_offs;311 ssize_t status;312 313 if (!name || !guid)314 return EFI_INVALID_PARAMETER;315 316 name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;317 if (name_length > QSEE_MAX_NAME_LEN)318 return EFI_INVALID_PARAMETER;319 320 if (buffer_size && !data)321 return EFI_INVALID_PARAMETER;322 323 req_size = qcuefi_buf_align_fields(324 __field(*req_data)325 __array_offs(*name, name_length, &name_offs)326 __field_offs(*guid, &guid_offs)327 );328 329 rsp_size = qcuefi_buf_align_fields(330 __field(*rsp_data)331 __array(u8, buffer_size)332 );333 334 cmd_buf_size = qcuefi_buf_align_fields(335 __reqdata_offs(req_size, &req_offs)336 __reqdata_offs(rsp_size, &rsp_offs)337 );338 339 cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);340 if (!cmd_buf)341 return EFI_OUT_OF_RESOURCES;342 343 req_data = cmd_buf + req_offs;344 rsp_data = cmd_buf + rsp_offs;345 346 req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE;347 req_data->data_size = buffer_size;348 req_data->name_offset = name_offs;349 req_data->name_size = name_length * sizeof(*name);350 req_data->guid_offset = guid_offs;351 req_data->guid_size = sizeof(*guid);352 req_data->length = req_size;353 354 status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);355 if (status < 0)356 return EFI_INVALID_PARAMETER;357 358 memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);359 360 status = qcom_qseecom_app_send(qcuefi->client,361 cmd_buf + req_offs, req_size,362 cmd_buf + rsp_offs, rsp_size);363 if (status)364 return EFI_DEVICE_ERROR;365 366 if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE)367 return EFI_DEVICE_ERROR;368 369 if (rsp_data->length < sizeof(*rsp_data))370 return EFI_DEVICE_ERROR;371 372 if (rsp_data->status) {373 dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",374 __func__, rsp_data->status);375 efi_status = qsee_uefi_status_to_efi(rsp_data->status);376 377 /* Update size and attributes in case buffer is too small. */378 if (efi_status == EFI_BUFFER_TOO_SMALL) {379 *data_size = rsp_data->data_size;380 if (attributes)381 *attributes = rsp_data->attributes;382 }383 384 return qsee_uefi_status_to_efi(rsp_data->status);385 }386 387 if (rsp_data->length > rsp_size)388 return EFI_DEVICE_ERROR;389 390 if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length)391 return EFI_DEVICE_ERROR;392 393 /*394 * Note: We need to set attributes and data size even if the buffer is395 * too small and we won't copy any data. This is described in spec, so396 * that callers can either allocate a buffer properly (with two calls397 * to this function) or just read back attributes withouth having to398 * deal with that.399 *400 * Specifically:401 * - If we have a buffer size of zero and no buffer, just return the402 * attributes, required size, and indicate success.403 * - If the buffer size is nonzero but too small, indicate that as an404 * error.405 * - Otherwise, we are good to copy the data.406 *407 * Note that we have already ensured above that the buffer pointer is408 * non-NULL if its size is nonzero.409 */410 *data_size = rsp_data->data_size;411 if (attributes)412 *attributes = rsp_data->attributes;413 414 if (buffer_size == 0 && !data)415 return EFI_SUCCESS;416 417 if (buffer_size < rsp_data->data_size)418 return EFI_BUFFER_TOO_SMALL;419 420 memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size);421 422 return EFI_SUCCESS;423}424 425static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name,426 const efi_guid_t *guid, u32 attributes,427 unsigned long data_size, const void *data)428{429 struct qsee_req_uefi_set_variable *req_data;430 struct qsee_rsp_uefi_set_variable *rsp_data;431 void *cmd_buf __free(qcom_tzmem) = NULL;432 unsigned long name_length;433 size_t cmd_buf_size;434 size_t name_offs;435 size_t guid_offs;436 size_t data_offs;437 size_t req_size;438 size_t req_offs;439 size_t rsp_offs;440 ssize_t status;441 442 if (!name || !guid)443 return EFI_INVALID_PARAMETER;444 445 name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;446 if (name_length > QSEE_MAX_NAME_LEN)447 return EFI_INVALID_PARAMETER;448 449 /*450 * Make sure we have some data if data_size is nonzero. Note that using451 * a size of zero is a valid use-case described in spec and deletes the452 * variable.453 */454 if (data_size && !data)455 return EFI_INVALID_PARAMETER;456 457 req_size = qcuefi_buf_align_fields(458 __field(*req_data)459 __array_offs(*name, name_length, &name_offs)460 __field_offs(*guid, &guid_offs)461 __array_offs(u8, data_size, &data_offs)462 );463 464 cmd_buf_size = qcuefi_buf_align_fields(465 __reqdata_offs(req_size, &req_offs)466 __reqdata_offs(sizeof(*rsp_data), &rsp_offs)467 );468 469 cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);470 if (!cmd_buf)471 return EFI_OUT_OF_RESOURCES;472 473 req_data = cmd_buf + req_offs;474 rsp_data = cmd_buf + rsp_offs;475 476 req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE;477 req_data->attributes = attributes;478 req_data->name_offset = name_offs;479 req_data->name_size = name_length * sizeof(*name);480 req_data->guid_offset = guid_offs;481 req_data->guid_size = sizeof(*guid);482 req_data->data_offset = data_offs;483 req_data->data_size = data_size;484 req_data->length = req_size;485 486 status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);487 if (status < 0)488 return EFI_INVALID_PARAMETER;489 490 memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);491 492 if (data_size)493 memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size);494 495 status = qcom_qseecom_app_send(qcuefi->client,496 cmd_buf + req_offs, req_size,497 cmd_buf + rsp_offs, sizeof(*rsp_data));498 if (status)499 return EFI_DEVICE_ERROR;500 501 if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE)502 return EFI_DEVICE_ERROR;503 504 if (rsp_data->length != sizeof(*rsp_data))505 return EFI_DEVICE_ERROR;506 507 if (rsp_data->status) {508 dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",509 __func__, rsp_data->status);510 return qsee_uefi_status_to_efi(rsp_data->status);511 }512 513 return EFI_SUCCESS;514}515 516static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,517 unsigned long *name_size, efi_char16_t *name,518 efi_guid_t *guid)519{520 struct qsee_req_uefi_get_next_variable *req_data;521 struct qsee_rsp_uefi_get_next_variable *rsp_data;522 void *cmd_buf __free(qcom_tzmem) = NULL;523 efi_status_t efi_status;524 size_t cmd_buf_size;525 size_t guid_offs;526 size_t name_offs;527 size_t req_size;528 size_t rsp_size;529 size_t req_offs;530 size_t rsp_offs;531 ssize_t status;532 533 if (!name_size || !name || !guid)534 return EFI_INVALID_PARAMETER;535 536 if (*name_size == 0)537 return EFI_INVALID_PARAMETER;538 539 req_size = qcuefi_buf_align_fields(540 __field(*req_data)541 __field_offs(*guid, &guid_offs)542 __array_offs(*name, *name_size / sizeof(*name), &name_offs)543 );544 545 rsp_size = qcuefi_buf_align_fields(546 __field(*rsp_data)547 __field(*guid)548 __array(*name, *name_size / sizeof(*name))549 );550 551 cmd_buf_size = qcuefi_buf_align_fields(552 __reqdata_offs(req_size, &req_offs)553 __reqdata_offs(rsp_size, &rsp_offs)554 );555 556 cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);557 if (!cmd_buf)558 return EFI_OUT_OF_RESOURCES;559 560 req_data = cmd_buf + req_offs;561 rsp_data = cmd_buf + rsp_offs;562 563 req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE;564 req_data->guid_offset = guid_offs;565 req_data->guid_size = sizeof(*guid);566 req_data->name_offset = name_offs;567 req_data->name_size = *name_size;568 req_data->length = req_size;569 570 memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);571 status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name,572 *name_size / sizeof(*name));573 if (status < 0)574 return EFI_INVALID_PARAMETER;575 576 status = qcom_qseecom_app_send(qcuefi->client,577 cmd_buf + req_offs, req_size,578 cmd_buf + rsp_offs, rsp_size);579 if (status)580 return EFI_DEVICE_ERROR;581 582 if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE)583 return EFI_DEVICE_ERROR;584 585 if (rsp_data->length < sizeof(*rsp_data))586 return EFI_DEVICE_ERROR;587 588 if (rsp_data->status) {589 dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",590 __func__, rsp_data->status);591 efi_status = qsee_uefi_status_to_efi(rsp_data->status);592 593 /*594 * If the buffer to hold the name is too small, update the595 * name_size with the required size, so that callers can596 * reallocate it accordingly.597 */598 if (efi_status == EFI_BUFFER_TOO_SMALL)599 *name_size = rsp_data->name_size;600 601 return efi_status;602 }603 604 if (rsp_data->length > rsp_size)605 return EFI_DEVICE_ERROR;606 607 if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length)608 return EFI_DEVICE_ERROR;609 610 if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length)611 return EFI_DEVICE_ERROR;612 613 if (rsp_data->name_size > *name_size) {614 *name_size = rsp_data->name_size;615 return EFI_BUFFER_TOO_SMALL;616 }617 618 if (rsp_data->guid_size != sizeof(*guid))619 return EFI_DEVICE_ERROR;620 621 memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size);622 status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset,623 rsp_data->name_size / sizeof(*name));624 *name_size = rsp_data->name_size;625 626 if (status < 0)627 /*628 * Return EFI_DEVICE_ERROR here because the buffer size should629 * have already been validated above, causing this function to630 * bail with EFI_BUFFER_TOO_SMALL.631 */632 return EFI_DEVICE_ERROR;633 634 return EFI_SUCCESS;635}636 637static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr,638 u64 *storage_space, u64 *remaining_space,639 u64 *max_variable_size)640{641 struct qsee_req_uefi_query_variable_info *req_data;642 struct qsee_rsp_uefi_query_variable_info *rsp_data;643 void *cmd_buf __free(qcom_tzmem) = NULL;644 size_t cmd_buf_size;645 size_t req_offs;646 size_t rsp_offs;647 int status;648 649 cmd_buf_size = qcuefi_buf_align_fields(650 __reqdata_offs(sizeof(*req_data), &req_offs)651 __reqdata_offs(sizeof(*rsp_data), &rsp_offs)652 );653 654 cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);655 if (!cmd_buf)656 return EFI_OUT_OF_RESOURCES;657 658 req_data = cmd_buf + req_offs;659 rsp_data = cmd_buf + rsp_offs;660 661 req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO;662 req_data->attributes = attr;663 req_data->length = sizeof(*req_data);664 665 status = qcom_qseecom_app_send(qcuefi->client,666 cmd_buf + req_offs, sizeof(*req_data),667 cmd_buf + rsp_offs, sizeof(*rsp_data));668 if (status)669 return EFI_DEVICE_ERROR;670 671 if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO)672 return EFI_DEVICE_ERROR;673 674 if (rsp_data->length != sizeof(*rsp_data))675 return EFI_DEVICE_ERROR;676 677 if (rsp_data->status) {678 dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",679 __func__, rsp_data->status);680 return qsee_uefi_status_to_efi(rsp_data->status);681 }682 683 if (storage_space)684 *storage_space = rsp_data->storage_space;685 686 if (remaining_space)687 *remaining_space = rsp_data->remaining_space;688 689 if (max_variable_size)690 *max_variable_size = rsp_data->max_variable_size;691 692 return EFI_SUCCESS;693}694 695/* -- Global efivar interface. ---------------------------------------------- */696 697static struct qcuefi_client *__qcuefi;698static DEFINE_MUTEX(__qcuefi_lock);699 700static int qcuefi_set_reference(struct qcuefi_client *qcuefi)701{702 mutex_lock(&__qcuefi_lock);703 704 if (qcuefi && __qcuefi) {705 mutex_unlock(&__qcuefi_lock);706 return -EEXIST;707 }708 709 __qcuefi = qcuefi;710 711 mutex_unlock(&__qcuefi_lock);712 return 0;713}714 715static struct qcuefi_client *qcuefi_acquire(void)716{717 mutex_lock(&__qcuefi_lock);718 if (!__qcuefi) {719 mutex_unlock(&__qcuefi_lock);720 return NULL;721 }722 return __qcuefi;723}724 725static void qcuefi_release(void)726{727 mutex_unlock(&__qcuefi_lock);728}729 730static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr,731 unsigned long *data_size, void *data)732{733 struct qcuefi_client *qcuefi;734 efi_status_t status;735 736 qcuefi = qcuefi_acquire();737 if (!qcuefi)738 return EFI_NOT_READY;739 740 status = qsee_uefi_get_variable(qcuefi, name, vendor, attr, data_size, data);741 742 qcuefi_release();743 return status;744}745 746static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor,747 u32 attr, unsigned long data_size, void *data)748{749 struct qcuefi_client *qcuefi;750 efi_status_t status;751 752 qcuefi = qcuefi_acquire();753 if (!qcuefi)754 return EFI_NOT_READY;755 756 status = qsee_uefi_set_variable(qcuefi, name, vendor, attr, data_size, data);757 758 qcuefi_release();759 return status;760}761 762static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name,763 efi_guid_t *vendor)764{765 struct qcuefi_client *qcuefi;766 efi_status_t status;767 768 qcuefi = qcuefi_acquire();769 if (!qcuefi)770 return EFI_NOT_READY;771 772 status = qsee_uefi_get_next_variable(qcuefi, name_size, name, vendor);773 774 qcuefi_release();775 return status;776}777 778static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space,779 u64 *max_variable_size)780{781 struct qcuefi_client *qcuefi;782 efi_status_t status;783 784 qcuefi = qcuefi_acquire();785 if (!qcuefi)786 return EFI_NOT_READY;787 788 status = qsee_uefi_query_variable_info(qcuefi, attr, storage_space, remaining_space,789 max_variable_size);790 791 qcuefi_release();792 return status;793}794 795static const struct efivar_operations qcom_efivar_ops = {796 .get_variable = qcuefi_get_variable,797 .set_variable = qcuefi_set_variable,798 .get_next_variable = qcuefi_get_next_variable,799 .query_variable_info = qcuefi_query_variable_info,800};801 802/* -- Driver setup. --------------------------------------------------------- */803 804static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev,805 const struct auxiliary_device_id *aux_dev_id)806{807 struct qcom_tzmem_pool_config pool_config;808 struct qcuefi_client *qcuefi;809 int status;810 811 qcuefi = devm_kzalloc(&aux_dev->dev, sizeof(*qcuefi), GFP_KERNEL);812 if (!qcuefi)813 return -ENOMEM;814 815 qcuefi->client = container_of(aux_dev, struct qseecom_client, aux_dev);816 817 auxiliary_set_drvdata(aux_dev, qcuefi);818 status = qcuefi_set_reference(qcuefi);819 if (status)820 return status;821 822 status = efivars_register(&qcuefi->efivars, &qcom_efivar_ops);823 if (status)824 qcuefi_set_reference(NULL);825 826 memset(&pool_config, 0, sizeof(pool_config));827 pool_config.initial_size = SZ_4K;828 pool_config.policy = QCOM_TZMEM_POLICY_MULTIPLIER;829 pool_config.increment = 2;830 pool_config.max_size = SZ_256K;831 832 qcuefi->mempool = devm_qcom_tzmem_pool_new(&aux_dev->dev, &pool_config);833 if (IS_ERR(qcuefi->mempool))834 return PTR_ERR(qcuefi->mempool);835 836 return status;837}838 839static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev)840{841 struct qcuefi_client *qcuefi = auxiliary_get_drvdata(aux_dev);842 843 efivars_unregister(&qcuefi->efivars);844 qcuefi_set_reference(NULL);845}846 847static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = {848 { .name = "qcom_qseecom.uefisecapp" },849 {}850};851MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table);852 853static struct auxiliary_driver qcom_uefisecapp_driver = {854 .probe = qcom_uefisecapp_probe,855 .remove = qcom_uefisecapp_remove,856 .id_table = qcom_uefisecapp_id_table,857 .driver = {858 .name = "qcom_qseecom_uefisecapp",859 .probe_type = PROBE_PREFER_ASYNCHRONOUS,860 },861};862module_auxiliary_driver(qcom_uefisecapp_driver);863 864MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");865MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App");866MODULE_LICENSE("GPL");867