1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Qualcomm Secure Execution Environment (SEE) interface (QSEECOM). |
4 | * Responsible for setting up and managing QSEECOM client devices. |
5 | * |
6 | * Copyright (C) 2023 Maximilian Luz <luzmaximilian@gmail.com> |
7 | */ |
8 | #include <linux/auxiliary_bus.h> |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/types.h> |
13 | |
14 | #include <linux/firmware/qcom/qcom_qseecom.h> |
15 | #include <linux/firmware/qcom/qcom_scm.h> |
16 | |
17 | struct qseecom_app_desc { |
18 | const char *app_name; |
19 | const char *dev_name; |
20 | }; |
21 | |
22 | static void qseecom_client_release(struct device *dev) |
23 | { |
24 | struct qseecom_client *client; |
25 | |
26 | client = container_of(dev, struct qseecom_client, aux_dev.dev); |
27 | kfree(objp: client); |
28 | } |
29 | |
30 | static void qseecom_client_remove(void *data) |
31 | { |
32 | struct qseecom_client *client = data; |
33 | |
34 | auxiliary_device_delete(auxdev: &client->aux_dev); |
35 | auxiliary_device_uninit(auxdev: &client->aux_dev); |
36 | } |
37 | |
38 | static int qseecom_client_register(struct platform_device *qseecom_dev, |
39 | const struct qseecom_app_desc *desc) |
40 | { |
41 | struct qseecom_client *client; |
42 | u32 app_id; |
43 | int ret; |
44 | |
45 | /* Try to find the app ID, skip device if not found */ |
46 | ret = qcom_scm_qseecom_app_get_id(app_name: desc->app_name, app_id: &app_id); |
47 | if (ret) |
48 | return ret == -ENOENT ? 0 : ret; |
49 | |
50 | dev_info(&qseecom_dev->dev, "setting up client for %s\n" , desc->app_name); |
51 | |
52 | /* Allocate and set-up the client device */ |
53 | client = kzalloc(size: sizeof(*client), GFP_KERNEL); |
54 | if (!client) |
55 | return -ENOMEM; |
56 | |
57 | client->aux_dev.name = desc->dev_name; |
58 | client->aux_dev.dev.parent = &qseecom_dev->dev; |
59 | client->aux_dev.dev.release = qseecom_client_release; |
60 | client->app_id = app_id; |
61 | |
62 | ret = auxiliary_device_init(auxdev: &client->aux_dev); |
63 | if (ret) { |
64 | kfree(objp: client); |
65 | return ret; |
66 | } |
67 | |
68 | ret = auxiliary_device_add(&client->aux_dev); |
69 | if (ret) { |
70 | auxiliary_device_uninit(auxdev: &client->aux_dev); |
71 | return ret; |
72 | } |
73 | |
74 | ret = devm_add_action_or_reset(&qseecom_dev->dev, qseecom_client_remove, client); |
75 | if (ret) |
76 | return ret; |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | /* |
82 | * List of supported applications. One client device will be created per entry, |
83 | * assuming the app has already been loaded (usually by firmware bootloaders) |
84 | * and its ID can be queried successfully. |
85 | */ |
86 | static const struct qseecom_app_desc qcom_qseecom_apps[] = { |
87 | { "qcom.tz.uefisecapp" , "uefisecapp" }, |
88 | }; |
89 | |
90 | static int qcom_qseecom_probe(struct platform_device *qseecom_dev) |
91 | { |
92 | int ret; |
93 | int i; |
94 | |
95 | /* Set up client devices for each base application */ |
96 | for (i = 0; i < ARRAY_SIZE(qcom_qseecom_apps); i++) { |
97 | ret = qseecom_client_register(qseecom_dev, desc: &qcom_qseecom_apps[i]); |
98 | if (ret) |
99 | return ret; |
100 | } |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static struct platform_driver qcom_qseecom_driver = { |
106 | .driver = { |
107 | .name = "qcom_qseecom" , |
108 | }, |
109 | .probe = qcom_qseecom_probe, |
110 | }; |
111 | |
112 | static int __init qcom_qseecom_init(void) |
113 | { |
114 | return platform_driver_register(&qcom_qseecom_driver); |
115 | } |
116 | subsys_initcall(qcom_qseecom_init); |
117 | |
118 | MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>" ); |
119 | MODULE_DESCRIPTION("Driver for the Qualcomm SEE (QSEECOM) interface" ); |
120 | MODULE_LICENSE("GPL" ); |
121 | |