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 
qseecom_client_release(struct device * dev)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(client);
28 }
29 
qseecom_client_remove(void * data)30 static void qseecom_client_remove(void *data)
31 {
32 	struct qseecom_client *client = data;
33 
34 	auxiliary_device_delete(&client->aux_dev);
35 	auxiliary_device_uninit(&client->aux_dev);
36 }
37 
qseecom_client_register(struct platform_device * qseecom_dev,const struct qseecom_app_desc * desc)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(desc->app_name, &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(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(&client->aux_dev);
63 	if (ret) {
64 		kfree(client);
65 		return ret;
66 	}
67 
68 	ret = auxiliary_device_add(&client->aux_dev);
69 	if (ret) {
70 		auxiliary_device_uninit(&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 
qcom_qseecom_probe(struct platform_device * qseecom_dev)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, &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 
qcom_qseecom_init(void)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