1
2
3
4
5
6
7#include <asm/cacheflush.h>
8#include <linux/device.h>
9#include <linux/dma-mapping.h>
10#include <linux/firmware.h>
11#include <linux/firmware/xlnx-zynqmp.h>
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/of_device.h>
16
17#define ZYNQMP_AES_KEY_SIZE 64
18
19static u8 key[ZYNQMP_AES_KEY_SIZE] = {0};
20static dma_addr_t dma_addr;
21static u8 *keyptr;
22static size_t dma_size;
23static char *kbuf;
24
25static const struct zynqmp_eemi_ops *eemi_ops;
26
27static ssize_t secure_load_store(struct device *dev,
28 struct device_attribute *attr,
29 const char *buf, size_t count)
30{
31 const struct firmware *fw;
32 char image_name[NAME_MAX];
33 u64 dst, ret;
34 int len;
35
36 if (IS_ERR(eemi_ops) || !eemi_ops->secure_image)
37 return -EFAULT;
38
39 strncpy(image_name, buf, NAME_MAX);
40 len = strlen(image_name);
41 if (image_name[len - 1] == '\n')
42 image_name[len - 1] = 0;
43
44 ret = request_firmware(&fw, image_name, dev);
45 if (ret) {
46 dev_err(dev, "Error requesting firmware %s\n", image_name);
47 return ret;
48 }
49 dma_size = fw->size;
50
51 if (keyptr)
52 dma_size = fw->size + ZYNQMP_AES_KEY_SIZE;
53
54 kbuf = dma_alloc_coherent(dev, dma_size,
55 &dma_addr, GFP_KERNEL);
56 if (!kbuf)
57 return -ENOMEM;
58
59 memcpy(kbuf, fw->data, fw->size);
60
61 if (keyptr)
62 memcpy(kbuf + fw->size, key, ZYNQMP_AES_KEY_SIZE);
63
64
65 __flush_cache_user_range((unsigned long)kbuf,
66 (unsigned long)kbuf + dma_size);
67 release_firmware(fw);
68
69 if (keyptr)
70 ret = eemi_ops->secure_image(dma_addr, dma_addr + fw->size,
71 &dst);
72 else
73 ret = eemi_ops->secure_image(dma_addr, 0, &dst);
74
75 if (ret) {
76 dev_info(dev, "Failed to load secure image \r\n");
77 return ret;
78 }
79 dev_info(dev, "Verified image at 0x%llx\n", dst);
80
81 return count;
82}
83
84static ssize_t key_show(struct device *dev,
85 struct device_attribute *attr,
86 char *buf)
87{
88 return snprintf(buf, ZYNQMP_AES_KEY_SIZE + 1, "%s\n", key);
89}
90
91static ssize_t key_store(struct device *dev,
92 struct device_attribute *attr,
93 const char *buf, size_t count)
94{
95 memcpy(key, buf, count);
96 keyptr = &key[0];
97 return count;
98}
99
100static ssize_t secure_load_done_store(struct device *dev,
101 struct device_attribute *attr,
102 const char *buf, size_t count)
103{
104 int ret;
105 unsigned int value;
106
107 ret = kstrtouint(buf, 10, &value);
108 if (ret)
109 return ret;
110 if (value)
111 dma_free_coherent(dev, dma_size, kbuf, dma_addr);
112
113 return count;
114}
115
116static DEVICE_ATTR_RW(key);
117static DEVICE_ATTR_WO(secure_load);
118static DEVICE_ATTR_WO(secure_load_done);
119
120static struct attribute *securefw_attrs[] = {
121 &dev_attr_secure_load_done.attr,
122 &dev_attr_secure_load.attr,
123 &dev_attr_key.attr,
124 NULL,
125};
126
127ATTRIBUTE_GROUPS(securefw);
128
129static int securefw_probe(struct platform_device *pdev)
130{
131 int ret;
132 struct platform_device *securefw_pdev;
133
134 eemi_ops = zynqmp_pm_get_eemi_ops();
135 if (IS_ERR(eemi_ops))
136 return PTR_ERR(eemi_ops);
137
138 securefw_pdev = pdev;
139
140 securefw_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
141
142 ret = of_dma_configure(&securefw_pdev->dev, NULL, true);
143 if (ret < 0) {
144 dev_info(&securefw_pdev->dev, "Cannot setup DMA ops\r\n");
145 return ret;
146 }
147
148 ret = sysfs_create_groups(&securefw_pdev->dev.kobj, securefw_groups);
149 if (ret)
150 return ret;
151
152 dev_info(&securefw_pdev->dev, "securefw probed\r\n");
153 return ret;
154}
155
156static int securefw_remove(struct platform_device *pdev)
157{
158 sysfs_remove_groups(&pdev->dev.kobj, securefw_groups);
159 return 0;
160}
161
162static struct platform_driver securefw_driver = {
163 .driver = {
164 .name = "securefw",
165 },
166 .probe = securefw_probe,
167 .remove = securefw_remove,
168};
169
170static struct platform_device *securefw_dev_reg;
171
172static int __init zynqmp_secure_init(void)
173{
174 int ret;
175
176 ret = platform_driver_register(&securefw_driver);
177 if (ret)
178 return ret;
179
180 securefw_dev_reg = platform_device_register_simple("securefw", -1,
181 NULL, 0);
182 if (IS_ERR(securefw_dev_reg)) {
183 ret = PTR_ERR(securefw_dev_reg);
184 platform_driver_unregister(&securefw_driver);
185 return ret;
186 }
187 return 0;
188}
189
190static void __exit zynqmp_secure_exit(void)
191{
192 platform_device_unregister(securefw_dev_reg);
193 platform_driver_unregister(&securefw_driver);
194}
195
196module_init(zynqmp_secure_init);
197module_exit(zynqmp_secure_exit);
198