Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * AMD Secure Processor driver
4 *
5 * Copyright (C) 2017-2018 Advanced Micro Devices, Inc.
6 *
7 * Author: Tom Lendacky <thomas.lendacky@amd.com>
8 * Author: Gary R Hook <gary.hook@amd.com>
9 * Author: Brijesh Singh <brijesh.singh@amd.com>
10 */
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/kthread.h>
15#include <linux/sched.h>
16#include <linux/interrupt.h>
17#include <linux/spinlock.h>
18#include <linux/spinlock_types.h>
19#include <linux/types.h>
20#include <linux/ccp.h>
21
22#include "sev-dev.h"
23#include "ccp-dev.h"
24#include "sp-dev.h"
25
26MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
27MODULE_AUTHOR("Gary R Hook <gary.hook@amd.com>");
28MODULE_LICENSE("GPL");
29MODULE_VERSION("1.1.0");
30MODULE_DESCRIPTION("AMD Secure Processor driver");
31
32/* List of SPs, SP count, read-write access lock, and access functions
33 *
34 * Lock structure: get sp_unit_lock for reading whenever we need to
35 * examine the SP list.
36 */
37static DEFINE_RWLOCK(sp_unit_lock);
38static LIST_HEAD(sp_units);
39
40/* Ever-increasing value to produce unique unit numbers */
41static atomic_t sp_ordinal;
42
43static void sp_add_device(struct sp_device *sp)
44{
45 unsigned long flags;
46
47 write_lock_irqsave(&sp_unit_lock, flags);
48
49 list_add_tail(&sp->entry, &sp_units);
50
51 write_unlock_irqrestore(&sp_unit_lock, flags);
52}
53
54static void sp_del_device(struct sp_device *sp)
55{
56 unsigned long flags;
57
58 write_lock_irqsave(&sp_unit_lock, flags);
59
60 list_del(&sp->entry);
61
62 write_unlock_irqrestore(&sp_unit_lock, flags);
63}
64
65static irqreturn_t sp_irq_handler(int irq, void *data)
66{
67 struct sp_device *sp = data;
68
69 if (sp->ccp_irq_handler)
70 sp->ccp_irq_handler(irq, sp->ccp_irq_data);
71
72 if (sp->psp_irq_handler)
73 sp->psp_irq_handler(irq, sp->psp_irq_data);
74
75 return IRQ_HANDLED;
76}
77
78int sp_request_ccp_irq(struct sp_device *sp, irq_handler_t handler,
79 const char *name, void *data)
80{
81 int ret;
82
83 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) {
84 /* Need a common routine to manage all interrupts */
85 sp->ccp_irq_data = data;
86 sp->ccp_irq_handler = handler;
87
88 if (!sp->irq_registered) {
89 ret = request_irq(sp->ccp_irq, sp_irq_handler, 0,
90 sp->name, sp);
91 if (ret)
92 return ret;
93
94 sp->irq_registered = true;
95 }
96 } else {
97 /* Each sub-device can manage it's own interrupt */
98 ret = request_irq(sp->ccp_irq, handler, 0, name, data);
99 if (ret)
100 return ret;
101 }
102
103 return 0;
104}
105
106int sp_request_psp_irq(struct sp_device *sp, irq_handler_t handler,
107 const char *name, void *data)
108{
109 int ret;
110
111 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) {
112 /* Need a common routine to manage all interrupts */
113 sp->psp_irq_data = data;
114 sp->psp_irq_handler = handler;
115
116 if (!sp->irq_registered) {
117 ret = request_irq(sp->psp_irq, sp_irq_handler, 0,
118 sp->name, sp);
119 if (ret)
120 return ret;
121
122 sp->irq_registered = true;
123 }
124 } else {
125 /* Each sub-device can manage it's own interrupt */
126 ret = request_irq(sp->psp_irq, handler, 0, name, data);
127 if (ret)
128 return ret;
129 }
130
131 return 0;
132}
133
134void sp_free_ccp_irq(struct sp_device *sp, void *data)
135{
136 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) {
137 /* Using common routine to manage all interrupts */
138 if (!sp->psp_irq_handler) {
139 /* Nothing else using it, so free it */
140 free_irq(sp->ccp_irq, sp);
141
142 sp->irq_registered = false;
143 }
144
145 sp->ccp_irq_handler = NULL;
146 sp->ccp_irq_data = NULL;
147 } else {
148 /* Each sub-device can manage it's own interrupt */
149 free_irq(sp->ccp_irq, data);
150 }
151}
152
153void sp_free_psp_irq(struct sp_device *sp, void *data)
154{
155 if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) {
156 /* Using common routine to manage all interrupts */
157 if (!sp->ccp_irq_handler) {
158 /* Nothing else using it, so free it */
159 free_irq(sp->psp_irq, sp);
160
161 sp->irq_registered = false;
162 }
163
164 sp->psp_irq_handler = NULL;
165 sp->psp_irq_data = NULL;
166 } else {
167 /* Each sub-device can manage it's own interrupt */
168 free_irq(sp->psp_irq, data);
169 }
170}
171
172/**
173 * sp_alloc_struct - allocate and initialize the sp_device struct
174 *
175 * @dev: device struct of the SP
176 */
177struct sp_device *sp_alloc_struct(struct device *dev)
178{
179 struct sp_device *sp;
180
181 sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
182 if (!sp)
183 return NULL;
184
185 sp->dev = dev;
186 sp->ord = atomic_inc_return(&sp_ordinal);
187 snprintf(sp->name, SP_MAX_NAME_LEN, "sp-%u", sp->ord);
188
189 return sp;
190}
191
192int sp_init(struct sp_device *sp)
193{
194 sp_add_device(sp);
195
196 if (sp->dev_vdata->ccp_vdata)
197 ccp_dev_init(sp);
198
199 if (sp->dev_vdata->psp_vdata)
200 psp_dev_init(sp);
201 return 0;
202}
203
204void sp_destroy(struct sp_device *sp)
205{
206 if (sp->dev_vdata->ccp_vdata)
207 ccp_dev_destroy(sp);
208
209 if (sp->dev_vdata->psp_vdata)
210 psp_dev_destroy(sp);
211
212 sp_del_device(sp);
213}
214
215int sp_suspend(struct sp_device *sp)
216{
217 if (sp->dev_vdata->ccp_vdata) {
218 ccp_dev_suspend(sp);
219 }
220
221 return 0;
222}
223
224int sp_resume(struct sp_device *sp)
225{
226 if (sp->dev_vdata->ccp_vdata) {
227 ccp_dev_resume(sp);
228 }
229
230 return 0;
231}
232
233struct sp_device *sp_get_psp_master_device(void)
234{
235 struct sp_device *i, *ret = NULL;
236 unsigned long flags;
237
238 write_lock_irqsave(&sp_unit_lock, flags);
239 if (list_empty(&sp_units))
240 goto unlock;
241
242 list_for_each_entry(i, &sp_units, entry) {
243 if (i->psp_data && i->get_psp_master_device) {
244 ret = i->get_psp_master_device();
245 break;
246 }
247 }
248
249unlock:
250 write_unlock_irqrestore(&sp_unit_lock, flags);
251 return ret;
252}
253
254static int __init sp_mod_init(void)
255{
256#ifdef CONFIG_X86
257 static bool initialized;
258 int ret;
259
260 if (initialized)
261 return 0;
262
263 ret = sp_pci_init();
264 if (ret)
265 return ret;
266
267#ifdef CONFIG_CRYPTO_DEV_SP_PSP
268 psp_pci_init();
269#endif
270
271 initialized = true;
272
273 return 0;
274#endif
275
276#ifdef CONFIG_ARM64
277 int ret;
278
279 ret = sp_platform_init();
280 if (ret)
281 return ret;
282
283 return 0;
284#endif
285
286 return -ENODEV;
287}
288
289#if IS_BUILTIN(CONFIG_KVM_AMD) && IS_ENABLED(CONFIG_KVM_AMD_SEV)
290int __init sev_module_init(void)
291{
292 return sp_mod_init();
293}
294#endif
295
296static void __exit sp_mod_exit(void)
297{
298#ifdef CONFIG_X86
299
300#ifdef CONFIG_CRYPTO_DEV_SP_PSP
301 psp_pci_exit();
302#endif
303
304 sp_pci_exit();
305#endif
306
307#ifdef CONFIG_ARM64
308 sp_platform_exit();
309#endif
310}
311
312module_init(sp_mod_init);
313module_exit(sp_mod_exit);