• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ccp-dev.h"
23 #include "sp-dev.h"
24 
25 MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
26 MODULE_AUTHOR("Gary R Hook <gary.hook@amd.com>");
27 MODULE_LICENSE("GPL");
28 MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
29 MODULE_VERSION("1.1.0");
30 MODULE_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  */
37 static DEFINE_RWLOCK(sp_unit_lock);
38 static LIST_HEAD(sp_units);
39 
40 /* Ever-increasing value to produce unique unit numbers */
41 static atomic_t sp_ordinal;
42 
sp_add_device(struct sp_device * sp)43 static 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 
sp_del_device(struct sp_device * sp)54 static 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 
sp_irq_handler(int irq,void * data)65 static 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 
sp_request_ccp_irq(struct sp_device * sp,irq_handler_t handler,const char * name,void * data)78 int 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 
sp_request_psp_irq(struct sp_device * sp,irq_handler_t handler,const char * name,void * data)106 int 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 
sp_free_ccp_irq(struct sp_device * sp,void * data)134 void 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 
sp_free_psp_irq(struct sp_device * sp,void * data)153 void 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  */
sp_alloc_struct(struct device * dev)177 struct 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 
sp_init(struct sp_device * sp)192 int 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 
sp_destroy(struct sp_device * sp)204 void 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 
sp_suspend(struct sp_device * sp)215 int 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 
sp_resume(struct sp_device * sp)224 int 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 
sp_get_psp_master_device(void)233 struct 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 
249 unlock:
250 	write_unlock_irqrestore(&sp_unit_lock, flags);
251 	return ret;
252 }
253 
sp_mod_init(void)254 static int __init sp_mod_init(void)
255 {
256 #ifdef CONFIG_X86
257 	int ret;
258 
259 	ret = sp_pci_init();
260 	if (ret)
261 		return ret;
262 
263 #ifdef CONFIG_CRYPTO_DEV_SP_PSP
264 	psp_pci_init();
265 #endif
266 
267 	return 0;
268 #endif
269 
270 #ifdef CONFIG_ARM64
271 	int ret;
272 
273 	ret = sp_platform_init();
274 	if (ret)
275 		return ret;
276 
277 	return 0;
278 #endif
279 
280 	return -ENODEV;
281 }
282 
sp_mod_exit(void)283 static void __exit sp_mod_exit(void)
284 {
285 #ifdef CONFIG_X86
286 
287 #ifdef CONFIG_CRYPTO_DEV_SP_PSP
288 	psp_pci_exit();
289 #endif
290 
291 	sp_pci_exit();
292 #endif
293 
294 #ifdef CONFIG_ARM64
295 	sp_platform_exit();
296 #endif
297 }
298 
299 module_init(sp_mod_init);
300 module_exit(sp_mod_exit);
301