]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
63b94509 | 2 | /* |
d0ebbc0c | 3 | * AMD Secure Processor device driver |
63b94509 | 4 | * |
33960acc | 5 | * Copyright (C) 2013,2019 Advanced Micro Devices, Inc. |
63b94509 TL |
6 | * |
7 | * Author: Tom Lendacky <thomas.lendacky@amd.com> | |
fba8855c | 8 | * Author: Gary R Hook <gary.hook@amd.com> |
63b94509 TL |
9 | */ |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/kernel.h> | |
3d77565b | 13 | #include <linux/device.h> |
63b94509 TL |
14 | #include <linux/pci.h> |
15 | #include <linux/pci_ids.h> | |
3d77565b | 16 | #include <linux/dma-mapping.h> |
63b94509 TL |
17 | #include <linux/kthread.h> |
18 | #include <linux/sched.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/spinlock.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/ccp.h> | |
23 | ||
24 | #include "ccp-dev.h" | |
b93566f1 | 25 | #include "psp-dev.h" |
63b94509 | 26 | |
63b94509 TL |
27 | #define MSIX_VECTORS 2 |
28 | ||
d0ebbc0c | 29 | struct sp_pci { |
63b94509 | 30 | int msix_count; |
f4d18d65 | 31 | struct msix_entry msix_entry[MSIX_VECTORS]; |
63b94509 | 32 | }; |
2a6170df | 33 | static struct sp_device *sp_dev_master; |
63b94509 | 34 | |
d0ebbc0c | 35 | static int sp_get_msix_irqs(struct sp_device *sp) |
63b94509 | 36 | { |
d0ebbc0c | 37 | struct sp_pci *sp_pci = sp->dev_specific; |
f4d18d65 | 38 | struct device *dev = sp->dev; |
c6c59bf2 | 39 | struct pci_dev *pdev = to_pci_dev(dev); |
63b94509 TL |
40 | int v, ret; |
41 | ||
d0ebbc0c BS |
42 | for (v = 0; v < ARRAY_SIZE(sp_pci->msix_entry); v++) |
43 | sp_pci->msix_entry[v].entry = v; | |
63b94509 | 44 | |
d0ebbc0c | 45 | ret = pci_enable_msix_range(pdev, sp_pci->msix_entry, 1, v); |
5347ee8e | 46 | if (ret < 0) |
63b94509 TL |
47 | return ret; |
48 | ||
d0ebbc0c | 49 | sp_pci->msix_count = ret; |
f4d18d65 | 50 | sp->use_tasklet = true; |
63b94509 | 51 | |
d0ebbc0c BS |
52 | sp->psp_irq = sp_pci->msix_entry[0].vector; |
53 | sp->ccp_irq = (sp_pci->msix_count > 1) ? sp_pci->msix_entry[1].vector | |
54 | : sp_pci->msix_entry[0].vector; | |
63b94509 | 55 | return 0; |
63b94509 TL |
56 | } |
57 | ||
d0ebbc0c | 58 | static int sp_get_msi_irq(struct sp_device *sp) |
63b94509 | 59 | { |
f4d18d65 | 60 | struct device *dev = sp->dev; |
c6c59bf2 | 61 | struct pci_dev *pdev = to_pci_dev(dev); |
63b94509 TL |
62 | int ret; |
63 | ||
64 | ret = pci_enable_msi(pdev); | |
65 | if (ret) | |
66 | return ret; | |
67 | ||
f4d18d65 BS |
68 | sp->ccp_irq = pdev->irq; |
69 | sp->psp_irq = pdev->irq; | |
63b94509 TL |
70 | |
71 | return 0; | |
63b94509 TL |
72 | } |
73 | ||
d0ebbc0c | 74 | static int sp_get_irqs(struct sp_device *sp) |
63b94509 | 75 | { |
f4d18d65 | 76 | struct device *dev = sp->dev; |
63b94509 TL |
77 | int ret; |
78 | ||
d0ebbc0c | 79 | ret = sp_get_msix_irqs(sp); |
63b94509 TL |
80 | if (!ret) |
81 | return 0; | |
82 | ||
83 | /* Couldn't get MSI-X vectors, try MSI */ | |
84 | dev_notice(dev, "could not enable MSI-X (%d), trying MSI\n", ret); | |
d0ebbc0c | 85 | ret = sp_get_msi_irq(sp); |
63b94509 TL |
86 | if (!ret) |
87 | return 0; | |
88 | ||
89 | /* Couldn't get MSI interrupt */ | |
90 | dev_notice(dev, "could not enable MSI (%d)\n", ret); | |
91 | ||
92 | return ret; | |
93 | } | |
94 | ||
d0ebbc0c | 95 | static void sp_free_irqs(struct sp_device *sp) |
63b94509 | 96 | { |
d0ebbc0c | 97 | struct sp_pci *sp_pci = sp->dev_specific; |
f4d18d65 | 98 | struct device *dev = sp->dev; |
c6c59bf2 | 99 | struct pci_dev *pdev = to_pci_dev(dev); |
63b94509 | 100 | |
d0ebbc0c | 101 | if (sp_pci->msix_count) |
63b94509 | 102 | pci_disable_msix(pdev); |
f4d18d65 | 103 | else if (sp->psp_irq) |
63b94509 | 104 | pci_disable_msi(pdev); |
f4d18d65 BS |
105 | |
106 | sp->ccp_irq = 0; | |
107 | sp->psp_irq = 0; | |
63b94509 TL |
108 | } |
109 | ||
2a6170df BS |
110 | static bool sp_pci_is_master(struct sp_device *sp) |
111 | { | |
112 | struct device *dev_cur, *dev_new; | |
113 | struct pci_dev *pdev_cur, *pdev_new; | |
114 | ||
115 | dev_new = sp->dev; | |
116 | dev_cur = sp_dev_master->dev; | |
117 | ||
118 | pdev_new = to_pci_dev(dev_new); | |
119 | pdev_cur = to_pci_dev(dev_cur); | |
120 | ||
121 | if (pdev_new->bus->number < pdev_cur->bus->number) | |
122 | return true; | |
123 | ||
124 | if (PCI_SLOT(pdev_new->devfn) < PCI_SLOT(pdev_cur->devfn)) | |
125 | return true; | |
126 | ||
127 | if (PCI_FUNC(pdev_new->devfn) < PCI_FUNC(pdev_cur->devfn)) | |
128 | return true; | |
129 | ||
130 | return false; | |
131 | } | |
132 | ||
133 | static void psp_set_master(struct sp_device *sp) | |
134 | { | |
135 | if (!sp_dev_master) { | |
136 | sp_dev_master = sp; | |
137 | return; | |
138 | } | |
139 | ||
140 | if (sp_pci_is_master(sp)) | |
141 | sp_dev_master = sp; | |
142 | } | |
143 | ||
144 | static struct sp_device *psp_get_master(void) | |
145 | { | |
146 | return sp_dev_master; | |
147 | } | |
148 | ||
15f7a4c6 JA |
149 | static void psp_clear_master(struct sp_device *sp) |
150 | { | |
151 | if (sp == sp_dev_master) { | |
152 | sp_dev_master = NULL; | |
153 | dev_dbg(sp->dev, "Cleared sp_dev_master\n"); | |
154 | } | |
155 | } | |
156 | ||
d0ebbc0c | 157 | static int sp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
63b94509 | 158 | { |
720419f0 | 159 | struct sp_device *sp; |
d0ebbc0c | 160 | struct sp_pci *sp_pci; |
63b94509 | 161 | struct device *dev = &pdev->dev; |
970e8303 BS |
162 | void __iomem * const *iomap_table; |
163 | int bar_mask; | |
63b94509 TL |
164 | int ret; |
165 | ||
166 | ret = -ENOMEM; | |
720419f0 BS |
167 | sp = sp_alloc_struct(dev); |
168 | if (!sp) | |
63b94509 TL |
169 | goto e_err; |
170 | ||
d0ebbc0c BS |
171 | sp_pci = devm_kzalloc(dev, sizeof(*sp_pci), GFP_KERNEL); |
172 | if (!sp_pci) | |
be03a3a0 TL |
173 | goto e_err; |
174 | ||
d0ebbc0c | 175 | sp->dev_specific = sp_pci; |
720419f0 BS |
176 | sp->dev_vdata = (struct sp_dev_vdata *)id->driver_data; |
177 | if (!sp->dev_vdata) { | |
c7019c4d GH |
178 | ret = -ENODEV; |
179 | dev_err(dev, "missing driver data\n"); | |
180 | goto e_err; | |
181 | } | |
63b94509 | 182 | |
970e8303 | 183 | ret = pcim_enable_device(pdev); |
63b94509 | 184 | if (ret) { |
970e8303 | 185 | dev_err(dev, "pcim_enable_device failed (%d)\n", ret); |
be03a3a0 | 186 | goto e_err; |
63b94509 TL |
187 | } |
188 | ||
970e8303 BS |
189 | bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); |
190 | ret = pcim_iomap_regions(pdev, bar_mask, "ccp"); | |
63b94509 | 191 | if (ret) { |
970e8303 BS |
192 | dev_err(dev, "pcim_iomap_regions failed (%d)\n", ret); |
193 | goto e_err; | |
63b94509 TL |
194 | } |
195 | ||
970e8303 BS |
196 | iomap_table = pcim_iomap_table(pdev); |
197 | if (!iomap_table) { | |
198 | dev_err(dev, "pcim_iomap_table failed\n"); | |
199 | ret = -ENOMEM; | |
200 | goto e_err; | |
201 | } | |
63b94509 | 202 | |
720419f0 BS |
203 | sp->io_map = iomap_table[sp->dev_vdata->bar]; |
204 | if (!sp->io_map) { | |
970e8303 BS |
205 | dev_err(dev, "ioremap failed\n"); |
206 | ret = -ENOMEM; | |
207 | goto e_err; | |
63b94509 | 208 | } |
970e8303 | 209 | |
d0ebbc0c | 210 | ret = sp_get_irqs(sp); |
f4d18d65 BS |
211 | if (ret) |
212 | goto e_err; | |
213 | ||
970e8303 | 214 | pci_set_master(pdev); |
2a6170df BS |
215 | sp->set_psp_master_device = psp_set_master; |
216 | sp->get_psp_master_device = psp_get_master; | |
15f7a4c6 | 217 | sp->clear_psp_master_device = psp_clear_master; |
63b94509 | 218 | |
3d77565b TL |
219 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); |
220 | if (ret) { | |
221 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); | |
63b94509 | 222 | if (ret) { |
3d77565b | 223 | dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", |
63b94509 | 224 | ret); |
a6f8e68e | 225 | goto free_irqs; |
63b94509 TL |
226 | } |
227 | } | |
228 | ||
720419f0 | 229 | dev_set_drvdata(dev, sp); |
63b94509 | 230 | |
720419f0 | 231 | ret = sp_init(sp); |
63b94509 | 232 | if (ret) |
a6f8e68e | 233 | goto free_irqs; |
63b94509 | 234 | |
63b94509 TL |
235 | return 0; |
236 | ||
a6f8e68e CJ |
237 | free_irqs: |
238 | sp_free_irqs(sp); | |
63b94509 TL |
239 | e_err: |
240 | dev_notice(dev, "initialization failed\n"); | |
241 | return ret; | |
242 | } | |
243 | ||
d0ebbc0c | 244 | static void sp_pci_remove(struct pci_dev *pdev) |
63b94509 TL |
245 | { |
246 | struct device *dev = &pdev->dev; | |
720419f0 | 247 | struct sp_device *sp = dev_get_drvdata(dev); |
63b94509 | 248 | |
720419f0 | 249 | if (!sp) |
db34cf91 TL |
250 | return; |
251 | ||
720419f0 | 252 | sp_destroy(sp); |
63b94509 | 253 | |
d0ebbc0c | 254 | sp_free_irqs(sp); |
63b94509 TL |
255 | } |
256 | ||
f892a21f | 257 | static int __maybe_unused sp_pci_suspend(struct device *dev) |
63b94509 | 258 | { |
720419f0 | 259 | struct sp_device *sp = dev_get_drvdata(dev); |
63b94509 | 260 | |
f892a21f | 261 | return sp_suspend(sp); |
63b94509 TL |
262 | } |
263 | ||
f892a21f | 264 | static int __maybe_unused sp_pci_resume(struct device *dev) |
63b94509 | 265 | { |
720419f0 | 266 | struct sp_device *sp = dev_get_drvdata(dev); |
63b94509 | 267 | |
720419f0 | 268 | return sp_resume(sp); |
63b94509 | 269 | } |
63b94509 | 270 | |
2a6170df | 271 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP |
6eb0cc72 | 272 | static const struct sev_vdata sevv1 = { |
ad01a984 TL |
273 | .cmdresp_reg = 0x10580, |
274 | .cmdbuff_addr_lo_reg = 0x105e0, | |
275 | .cmdbuff_addr_hi_reg = 0x105e4, | |
6eb0cc72 RT |
276 | }; |
277 | ||
278 | static const struct sev_vdata sevv2 = { | |
279 | .cmdresp_reg = 0x10980, | |
280 | .cmdbuff_addr_lo_reg = 0x109e0, | |
281 | .cmdbuff_addr_hi_reg = 0x109e4, | |
282 | }; | |
283 | ||
33960acc RT |
284 | static const struct tee_vdata teev1 = { |
285 | .cmdresp_reg = 0x10544, | |
286 | .cmdbuff_addr_lo_reg = 0x10548, | |
287 | .cmdbuff_addr_hi_reg = 0x1054c, | |
288 | .ring_wptr_reg = 0x10550, | |
289 | .ring_rptr_reg = 0x10554, | |
290 | }; | |
291 | ||
6eb0cc72 RT |
292 | static const struct psp_vdata pspv1 = { |
293 | .sev = &sevv1, | |
ad01a984 TL |
294 | .feature_reg = 0x105fc, |
295 | .inten_reg = 0x10610, | |
296 | .intsts_reg = 0x10614, | |
2a6170df | 297 | }; |
dcbc0c6e TL |
298 | |
299 | static const struct psp_vdata pspv2 = { | |
6eb0cc72 | 300 | .sev = &sevv2, |
dcbc0c6e TL |
301 | .feature_reg = 0x109fc, |
302 | .inten_reg = 0x10690, | |
303 | .intsts_reg = 0x10694, | |
304 | }; | |
33960acc RT |
305 | |
306 | static const struct psp_vdata pspv3 = { | |
307 | .tee = &teev1, | |
308 | .feature_reg = 0x109fc, | |
309 | .inten_reg = 0x10690, | |
310 | .intsts_reg = 0x10694, | |
311 | }; | |
2a6170df BS |
312 | #endif |
313 | ||
720419f0 | 314 | static const struct sp_dev_vdata dev_vdata[] = { |
dcbc0c6e | 315 | { /* 0 */ |
720419f0 BS |
316 | .bar = 2, |
317 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
318 | .ccp_vdata = &ccpv3, | |
319 | #endif | |
320 | }, | |
dcbc0c6e | 321 | { /* 1 */ |
720419f0 BS |
322 | .bar = 2, |
323 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
324 | .ccp_vdata = &ccpv5a, | |
2a6170df BS |
325 | #endif |
326 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP | |
dcbc0c6e | 327 | .psp_vdata = &pspv1, |
720419f0 BS |
328 | #endif |
329 | }, | |
dcbc0c6e | 330 | { /* 2 */ |
720419f0 BS |
331 | .bar = 2, |
332 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
333 | .ccp_vdata = &ccpv5b, | |
dcbc0c6e TL |
334 | #endif |
335 | }, | |
336 | { /* 3 */ | |
337 | .bar = 2, | |
338 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
339 | .ccp_vdata = &ccpv5a, | |
340 | #endif | |
341 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP | |
342 | .psp_vdata = &pspv2, | |
33960acc RT |
343 | #endif |
344 | }, | |
345 | { /* 4 */ | |
346 | .bar = 2, | |
347 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
348 | .ccp_vdata = &ccpv5a, | |
349 | #endif | |
350 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP | |
351 | .psp_vdata = &pspv3, | |
720419f0 BS |
352 | #endif |
353 | }, | |
354 | }; | |
d0ebbc0c | 355 | static const struct pci_device_id sp_pci_table[] = { |
720419f0 BS |
356 | { PCI_VDEVICE(AMD, 0x1537), (kernel_ulong_t)&dev_vdata[0] }, |
357 | { PCI_VDEVICE(AMD, 0x1456), (kernel_ulong_t)&dev_vdata[1] }, | |
358 | { PCI_VDEVICE(AMD, 0x1468), (kernel_ulong_t)&dev_vdata[2] }, | |
dcbc0c6e | 359 | { PCI_VDEVICE(AMD, 0x1486), (kernel_ulong_t)&dev_vdata[3] }, |
33960acc | 360 | { PCI_VDEVICE(AMD, 0x15DF), (kernel_ulong_t)&dev_vdata[4] }, |
195ec383 | 361 | { PCI_VDEVICE(AMD, 0x1649), (kernel_ulong_t)&dev_vdata[4] }, |
63b94509 TL |
362 | /* Last entry must be zero */ |
363 | { 0, } | |
364 | }; | |
d0ebbc0c | 365 | MODULE_DEVICE_TABLE(pci, sp_pci_table); |
63b94509 | 366 | |
f892a21f VG |
367 | static SIMPLE_DEV_PM_OPS(sp_pci_pm_ops, sp_pci_suspend, sp_pci_resume); |
368 | ||
d0ebbc0c | 369 | static struct pci_driver sp_pci_driver = { |
166db195 | 370 | .name = "ccp", |
d0ebbc0c BS |
371 | .id_table = sp_pci_table, |
372 | .probe = sp_pci_probe, | |
373 | .remove = sp_pci_remove, | |
f892a21f | 374 | .driver.pm = &sp_pci_pm_ops, |
63b94509 TL |
375 | }; |
376 | ||
d0ebbc0c | 377 | int sp_pci_init(void) |
63b94509 | 378 | { |
d0ebbc0c | 379 | return pci_register_driver(&sp_pci_driver); |
63b94509 TL |
380 | } |
381 | ||
d0ebbc0c | 382 | void sp_pci_exit(void) |
63b94509 | 383 | { |
d0ebbc0c | 384 | pci_unregister_driver(&sp_pci_driver); |
63b94509 | 385 | } |