Skip to content

Commit ed09f81

Browse files
qzedandersson
authored andcommitted
firmware: qcom: uefisecapp: Fix memory related IO errors and crashes
It turns out that while the QSEECOM APP_SEND command has specific fields for request and response buffers, uefisecapp expects them both to be in a single memory region. Failure to adhere to this has (so far) resulted in either no response being written to the response buffer (causing an EIO to be emitted down the line), the SCM call to fail with EINVAL (i.e., directly from TZ/firmware), or the device to be hard-reset. While this issue can be triggered deterministically, in the current form it seems to happen rather sporadically (which is why it has gone unnoticed during earlier testing). This is likely due to the two kzalloc() calls (for request and response) being directly after each other. Which means that those likely return consecutive regions most of the time, especially when not much else is going on in the system. Fix this by allocating a single memory region for both request and response buffers, properly aligning both structs inside it. This unfortunately also means that the qcom_scm_qseecom_app_send() interface needs to be restructured, as it should no longer map the DMA regions separately. Therefore, move the responsibility of DMA allocation (or mapping) to the caller. Fixes: 759e7a2 ("firmware: Add support for Qualcomm UEFI Secure Application") Cc: [email protected] # 6.7 Tested-by: Johan Hovold <[email protected]> Reviewed-by: Johan Hovold <[email protected]> Signed-off-by: Maximilian Luz <[email protected]> Tested-by: Konrad Dybcio <[email protected]> # X13s Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent 4cece76 commit ed09f81

File tree

4 files changed

+153
-86
lines changed

4 files changed

+153
-86
lines changed

drivers/firmware/qcom/qcom_qseecom_uefisecapp.c

Lines changed: 91 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,19 @@ struct qsee_rsp_uefi_query_variable_info {
221221
* alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t,
222222
* however, has an alignment of 4 byte (32 bits). So far, this seems to work
223223
* fine here. See also the comment on the typedef of efi_guid_t.
224+
*
225+
* Note: It looks like uefisecapp is quite picky about how the memory passed to
226+
* it is structured and aligned. In particular the request/response setup used
227+
* for QSEE_CMD_UEFI_GET_VARIABLE. While qcom_qseecom_app_send(), in theory,
228+
* accepts separate buffers/addresses for the request and response parts, in
229+
* practice, however, it seems to expect them to be both part of a larger
230+
* contiguous block. We initially allocated separate buffers for the request
231+
* and response but this caused the QSEE_CMD_UEFI_GET_VARIABLE command to
232+
* either not write any response to the response buffer or outright crash the
233+
* device. Therefore, we now allocate a single contiguous block of DMA memory
234+
* for both and properly align the data using the macros below. In particular,
235+
* request and response structs are aligned at 8 byte (via __reqdata_offs()),
236+
* following the driver that this has been reverse-engineered from.
224237
*/
225238
#define qcuefi_buf_align_fields(fields...) \
226239
({ \
@@ -244,6 +257,12 @@ struct qsee_rsp_uefi_query_variable_info {
244257
#define __array_offs(type, count, offset) \
245258
__field_impl(sizeof(type) * (count), __alignof__(type), offset)
246259

260+
#define __array_offs_aligned(type, count, align, offset) \
261+
__field_impl(sizeof(type) * (count), align, offset)
262+
263+
#define __reqdata_offs(size, offset) \
264+
__array_offs_aligned(u8, size, 8, offset)
265+
247266
#define __array(type, count) __array_offs(type, count, NULL)
248267
#define __field_offs(type, offset) __array_offs(type, 1, offset)
249268
#define __field(type) __array_offs(type, 1, NULL)
@@ -277,10 +296,15 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
277296
unsigned long buffer_size = *data_size;
278297
efi_status_t efi_status = EFI_SUCCESS;
279298
unsigned long name_length;
299+
dma_addr_t cmd_buf_dma;
300+
size_t cmd_buf_size;
301+
void *cmd_buf;
280302
size_t guid_offs;
281303
size_t name_offs;
282304
size_t req_size;
283305
size_t rsp_size;
306+
size_t req_offs;
307+
size_t rsp_offs;
284308
ssize_t status;
285309

286310
if (!name || !guid)
@@ -304,17 +328,19 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
304328
__array(u8, buffer_size)
305329
);
306330

307-
req_data = kzalloc(req_size, GFP_KERNEL);
308-
if (!req_data) {
331+
cmd_buf_size = qcuefi_buf_align_fields(
332+
__reqdata_offs(req_size, &req_offs)
333+
__reqdata_offs(rsp_size, &rsp_offs)
334+
);
335+
336+
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL);
337+
if (!cmd_buf) {
309338
efi_status = EFI_OUT_OF_RESOURCES;
310339
goto out;
311340
}
312341

313-
rsp_data = kzalloc(rsp_size, GFP_KERNEL);
314-
if (!rsp_data) {
315-
efi_status = EFI_OUT_OF_RESOURCES;
316-
goto out_free_req;
317-
}
342+
req_data = cmd_buf + req_offs;
343+
rsp_data = cmd_buf + rsp_offs;
318344

319345
req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE;
320346
req_data->data_size = buffer_size;
@@ -332,7 +358,9 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
332358

333359
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
334360

335-
status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size);
361+
status = qcom_qseecom_app_send(qcuefi->client,
362+
cmd_buf_dma + req_offs, req_size,
363+
cmd_buf_dma + rsp_offs, rsp_size);
336364
if (status) {
337365
efi_status = EFI_DEVICE_ERROR;
338366
goto out_free;
@@ -407,9 +435,7 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
407435
memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size);
408436

409437
out_free:
410-
kfree(rsp_data);
411-
out_free_req:
412-
kfree(req_data);
438+
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
413439
out:
414440
return efi_status;
415441
}
@@ -422,10 +448,15 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
422448
struct qsee_rsp_uefi_set_variable *rsp_data;
423449
efi_status_t efi_status = EFI_SUCCESS;
424450
unsigned long name_length;
451+
dma_addr_t cmd_buf_dma;
452+
size_t cmd_buf_size;
453+
void *cmd_buf;
425454
size_t name_offs;
426455
size_t guid_offs;
427456
size_t data_offs;
428457
size_t req_size;
458+
size_t req_offs;
459+
size_t rsp_offs;
429460
ssize_t status;
430461

431462
if (!name || !guid)
@@ -450,17 +481,19 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
450481
__array_offs(u8, data_size, &data_offs)
451482
);
452483

453-
req_data = kzalloc(req_size, GFP_KERNEL);
454-
if (!req_data) {
484+
cmd_buf_size = qcuefi_buf_align_fields(
485+
__reqdata_offs(req_size, &req_offs)
486+
__reqdata_offs(sizeof(*rsp_data), &rsp_offs)
487+
);
488+
489+
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL);
490+
if (!cmd_buf) {
455491
efi_status = EFI_OUT_OF_RESOURCES;
456492
goto out;
457493
}
458494

459-
rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL);
460-
if (!rsp_data) {
461-
efi_status = EFI_OUT_OF_RESOURCES;
462-
goto out_free_req;
463-
}
495+
req_data = cmd_buf + req_offs;
496+
rsp_data = cmd_buf + rsp_offs;
464497

465498
req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE;
466499
req_data->attributes = attributes;
@@ -483,8 +516,9 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
483516
if (data_size)
484517
memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size);
485518

486-
status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data,
487-
sizeof(*rsp_data));
519+
status = qcom_qseecom_app_send(qcuefi->client,
520+
cmd_buf_dma + req_offs, req_size,
521+
cmd_buf_dma + rsp_offs, sizeof(*rsp_data));
488522
if (status) {
489523
efi_status = EFI_DEVICE_ERROR;
490524
goto out_free;
@@ -507,9 +541,7 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
507541
}
508542

509543
out_free:
510-
kfree(rsp_data);
511-
out_free_req:
512-
kfree(req_data);
544+
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
513545
out:
514546
return efi_status;
515547
}
@@ -521,10 +553,15 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
521553
struct qsee_req_uefi_get_next_variable *req_data;
522554
struct qsee_rsp_uefi_get_next_variable *rsp_data;
523555
efi_status_t efi_status = EFI_SUCCESS;
556+
dma_addr_t cmd_buf_dma;
557+
size_t cmd_buf_size;
558+
void *cmd_buf;
524559
size_t guid_offs;
525560
size_t name_offs;
526561
size_t req_size;
527562
size_t rsp_size;
563+
size_t req_offs;
564+
size_t rsp_offs;
528565
ssize_t status;
529566

530567
if (!name_size || !name || !guid)
@@ -545,17 +582,19 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
545582
__array(*name, *name_size / sizeof(*name))
546583
);
547584

548-
req_data = kzalloc(req_size, GFP_KERNEL);
549-
if (!req_data) {
585+
cmd_buf_size = qcuefi_buf_align_fields(
586+
__reqdata_offs(req_size, &req_offs)
587+
__reqdata_offs(rsp_size, &rsp_offs)
588+
);
589+
590+
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL);
591+
if (!cmd_buf) {
550592
efi_status = EFI_OUT_OF_RESOURCES;
551593
goto out;
552594
}
553595

554-
rsp_data = kzalloc(rsp_size, GFP_KERNEL);
555-
if (!rsp_data) {
556-
efi_status = EFI_OUT_OF_RESOURCES;
557-
goto out_free_req;
558-
}
596+
req_data = cmd_buf + req_offs;
597+
rsp_data = cmd_buf + rsp_offs;
559598

560599
req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE;
561600
req_data->guid_offset = guid_offs;
@@ -572,7 +611,9 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
572611
goto out_free;
573612
}
574613

575-
status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size);
614+
status = qcom_qseecom_app_send(qcuefi->client,
615+
cmd_buf_dma + req_offs, req_size,
616+
cmd_buf_dma + rsp_offs, rsp_size);
576617
if (status) {
577618
efi_status = EFI_DEVICE_ERROR;
578619
goto out_free;
@@ -645,9 +686,7 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
645686
}
646687

647688
out_free:
648-
kfree(rsp_data);
649-
out_free_req:
650-
kfree(req_data);
689+
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
651690
out:
652691
return efi_status;
653692
}
@@ -659,26 +698,34 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi,
659698
struct qsee_req_uefi_query_variable_info *req_data;
660699
struct qsee_rsp_uefi_query_variable_info *rsp_data;
661700
efi_status_t efi_status = EFI_SUCCESS;
701+
dma_addr_t cmd_buf_dma;
702+
size_t cmd_buf_size;
703+
void *cmd_buf;
704+
size_t req_offs;
705+
size_t rsp_offs;
662706
int status;
663707

664-
req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
665-
if (!req_data) {
708+
cmd_buf_size = qcuefi_buf_align_fields(
709+
__reqdata_offs(sizeof(*req_data), &req_offs)
710+
__reqdata_offs(sizeof(*rsp_data), &rsp_offs)
711+
);
712+
713+
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL);
714+
if (!cmd_buf) {
666715
efi_status = EFI_OUT_OF_RESOURCES;
667716
goto out;
668717
}
669718

670-
rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL);
671-
if (!rsp_data) {
672-
efi_status = EFI_OUT_OF_RESOURCES;
673-
goto out_free_req;
674-
}
719+
req_data = cmd_buf + req_offs;
720+
rsp_data = cmd_buf + rsp_offs;
675721

676722
req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO;
677723
req_data->attributes = attr;
678724
req_data->length = sizeof(*req_data);
679725

680-
status = qcom_qseecom_app_send(qcuefi->client, req_data, sizeof(*req_data), rsp_data,
681-
sizeof(*rsp_data));
726+
status = qcom_qseecom_app_send(qcuefi->client,
727+
cmd_buf_dma + req_offs, sizeof(*req_data),
728+
cmd_buf_dma + rsp_offs, sizeof(*rsp_data));
682729
if (status) {
683730
efi_status = EFI_DEVICE_ERROR;
684731
goto out_free;
@@ -711,9 +758,7 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi,
711758
*max_variable_size = rsp_data->max_variable_size;
712759

713760
out_free:
714-
kfree(rsp_data);
715-
out_free_req:
716-
kfree(req_data);
761+
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
717762
out:
718763
return efi_status;
719764
}

drivers/firmware/qcom/qcom_scm.c

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,9 +1576,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id);
15761576
/**
15771577
* qcom_scm_qseecom_app_send() - Send to and receive data from a given QSEE app.
15781578
* @app_id: The ID of the target app.
1579-
* @req: Request buffer sent to the app (must be DMA-mappable).
1579+
* @req: DMA address of the request buffer sent to the app.
15801580
* @req_size: Size of the request buffer.
1581-
* @rsp: Response buffer, written to by the app (must be DMA-mappable).
1581+
* @rsp: DMA address of the response buffer, written to by the app.
15821582
* @rsp_size: Size of the response buffer.
15831583
*
15841584
* Sends a request to the QSEE app associated with the given ID and read back
@@ -1589,52 +1589,27 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id);
15891589
*
15901590
* Return: Zero on success, nonzero on failure.
15911591
*/
1592-
int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size, void *rsp,
1593-
size_t rsp_size)
1592+
int qcom_scm_qseecom_app_send(u32 app_id, dma_addr_t req, size_t req_size,
1593+
dma_addr_t rsp, size_t rsp_size)
15941594
{
15951595
struct qcom_scm_qseecom_resp res = {};
15961596
struct qcom_scm_desc desc = {};
1597-
dma_addr_t req_phys;
1598-
dma_addr_t rsp_phys;
15991597
int status;
16001598

1601-
/* Map request buffer */
1602-
req_phys = dma_map_single(__scm->dev, req, req_size, DMA_TO_DEVICE);
1603-
status = dma_mapping_error(__scm->dev, req_phys);
1604-
if (status) {
1605-
dev_err(__scm->dev, "qseecom: failed to map request buffer\n");
1606-
return status;
1607-
}
1608-
1609-
/* Map response buffer */
1610-
rsp_phys = dma_map_single(__scm->dev, rsp, rsp_size, DMA_FROM_DEVICE);
1611-
status = dma_mapping_error(__scm->dev, rsp_phys);
1612-
if (status) {
1613-
dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE);
1614-
dev_err(__scm->dev, "qseecom: failed to map response buffer\n");
1615-
return status;
1616-
}
1617-
1618-
/* Set up SCM call data */
16191599
desc.owner = QSEECOM_TZ_OWNER_TZ_APPS;
16201600
desc.svc = QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER;
16211601
desc.cmd = QSEECOM_TZ_CMD_APP_SEND;
16221602
desc.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL,
16231603
QCOM_SCM_RW, QCOM_SCM_VAL,
16241604
QCOM_SCM_RW, QCOM_SCM_VAL);
16251605
desc.args[0] = app_id;
1626-
desc.args[1] = req_phys;
1606+
desc.args[1] = req;
16271607
desc.args[2] = req_size;
1628-
desc.args[3] = rsp_phys;
1608+
desc.args[3] = rsp;
16291609
desc.args[4] = rsp_size;
16301610

1631-
/* Perform call */
16321611
status = qcom_scm_qseecom_call(&desc, &res);
16331612

1634-
/* Unmap buffers */
1635-
dma_unmap_single(__scm->dev, rsp_phys, rsp_size, DMA_FROM_DEVICE);
1636-
dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE);
1637-
16381613
if (status)
16391614
return status;
16401615

0 commit comments

Comments
 (0)