1b93566f1SRijo Thomas // SPDX-License-Identifier: GPL-2.0-only
2b93566f1SRijo Thomas /*
3b93566f1SRijo Thomas * AMD Platform Security Processor (PSP) interface
4b93566f1SRijo Thomas *
5b93566f1SRijo Thomas * Copyright (C) 2016,2019 Advanced Micro Devices, Inc.
6b93566f1SRijo Thomas *
7b93566f1SRijo Thomas * Author: Brijesh Singh <brijesh.singh@amd.com>
8b93566f1SRijo Thomas */
9b93566f1SRijo Thomas
10b93566f1SRijo Thomas #include <linux/kernel.h>
11b93566f1SRijo Thomas #include <linux/irqreturn.h>
12b93566f1SRijo Thomas
13b93566f1SRijo Thomas #include "sp-dev.h"
14b93566f1SRijo Thomas #include "psp-dev.h"
15b93566f1SRijo Thomas #include "sev-dev.h"
1633960accSRijo Thomas #include "tee-dev.h"
177ccc4f4eSMario Limonciello #include "platform-access.h"
18*c04cf9e1SMario Limonciello #include "dbc.h"
19b93566f1SRijo Thomas
20b93566f1SRijo Thomas struct psp_device *psp_master;
21b93566f1SRijo Thomas
psp_alloc_struct(struct sp_device * sp)22b93566f1SRijo Thomas static struct psp_device *psp_alloc_struct(struct sp_device *sp)
23b93566f1SRijo Thomas {
24b93566f1SRijo Thomas struct device *dev = sp->dev;
25b93566f1SRijo Thomas struct psp_device *psp;
26b93566f1SRijo Thomas
27b93566f1SRijo Thomas psp = devm_kzalloc(dev, sizeof(*psp), GFP_KERNEL);
28b93566f1SRijo Thomas if (!psp)
29b93566f1SRijo Thomas return NULL;
30b93566f1SRijo Thomas
31b93566f1SRijo Thomas psp->dev = dev;
32b93566f1SRijo Thomas psp->sp = sp;
33b93566f1SRijo Thomas
34b93566f1SRijo Thomas snprintf(psp->name, sizeof(psp->name), "psp-%u", sp->ord);
35b93566f1SRijo Thomas
36b93566f1SRijo Thomas return psp;
37b93566f1SRijo Thomas }
38b93566f1SRijo Thomas
psp_irq_handler(int irq,void * data)39b93566f1SRijo Thomas static irqreturn_t psp_irq_handler(int irq, void *data)
40b93566f1SRijo Thomas {
41b93566f1SRijo Thomas struct psp_device *psp = data;
42b93566f1SRijo Thomas unsigned int status;
43b93566f1SRijo Thomas
44b93566f1SRijo Thomas /* Read the interrupt status: */
45b93566f1SRijo Thomas status = ioread32(psp->io_regs + psp->vdata->intsts_reg);
46b93566f1SRijo Thomas
4745121ad4SJeremi Piotrowski /* Clear the interrupt status by writing the same value we read. */
4845121ad4SJeremi Piotrowski iowrite32(status, psp->io_regs + psp->vdata->intsts_reg);
4945121ad4SJeremi Piotrowski
50b93566f1SRijo Thomas /* invoke subdevice interrupt handlers */
51b93566f1SRijo Thomas if (status) {
52b93566f1SRijo Thomas if (psp->sev_irq_handler)
53b93566f1SRijo Thomas psp->sev_irq_handler(irq, psp->sev_irq_data, status);
54b93566f1SRijo Thomas }
55b93566f1SRijo Thomas
56b93566f1SRijo Thomas return IRQ_HANDLED;
57b93566f1SRijo Thomas }
58b93566f1SRijo Thomas
psp_get_capability(struct psp_device * psp)59f100ab62SRijo Thomas static unsigned int psp_get_capability(struct psp_device *psp)
60b93566f1SRijo Thomas {
61b93566f1SRijo Thomas unsigned int val = ioread32(psp->io_regs + psp->vdata->feature_reg);
62b93566f1SRijo Thomas
63b93566f1SRijo Thomas /*
64b93566f1SRijo Thomas * Check for a access to the registers. If this read returns
65b93566f1SRijo Thomas * 0xffffffff, it's likely that the system is running a broken
66b93566f1SRijo Thomas * BIOS which disallows access to the device. Stop here and
67b93566f1SRijo Thomas * fail the PSP initialization (but not the load, as the CCP
68b93566f1SRijo Thomas * could get properly initialized).
69b93566f1SRijo Thomas */
70b93566f1SRijo Thomas if (val == 0xffffffff) {
71b93566f1SRijo Thomas dev_notice(psp->dev, "psp: unable to access the device: you might be running a broken BIOS.\n");
72cac32cd4SMario Limonciello return -ENODEV;
73cac32cd4SMario Limonciello }
74cac32cd4SMario Limonciello psp->capability = val;
75cac32cd4SMario Limonciello
764e2c8794SMario Limonciello /* Detect if TSME and SME are both enabled */
774e2c8794SMario Limonciello if (psp->capability & PSP_CAPABILITY_PSP_SECURITY_REPORTING &&
784e2c8794SMario Limonciello psp->capability & (PSP_SECURITY_TSME_STATUS << PSP_CAPABILITY_PSP_SECURITY_OFFSET) &&
794e2c8794SMario Limonciello cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT))
804e2c8794SMario Limonciello dev_notice(psp->dev, "psp: Both TSME and SME are active, SME is unnecessary when TSME is active.\n");
814e2c8794SMario Limonciello
82f100ab62SRijo Thomas return 0;
83b93566f1SRijo Thomas }
84b93566f1SRijo Thomas
psp_check_sev_support(struct psp_device * psp)85cac32cd4SMario Limonciello static int psp_check_sev_support(struct psp_device *psp)
86f100ab62SRijo Thomas {
87f100ab62SRijo Thomas /* Check if device supports SEV feature */
88cac32cd4SMario Limonciello if (!(psp->capability & PSP_CAPABILITY_SEV)) {
89b93566f1SRijo Thomas dev_dbg(psp->dev, "psp does not support SEV\n");
90b93566f1SRijo Thomas return -ENODEV;
91b93566f1SRijo Thomas }
92b93566f1SRijo Thomas
93b93566f1SRijo Thomas return 0;
94b93566f1SRijo Thomas }
95b93566f1SRijo Thomas
psp_check_tee_support(struct psp_device * psp)96cac32cd4SMario Limonciello static int psp_check_tee_support(struct psp_device *psp)
97f100ab62SRijo Thomas {
98f100ab62SRijo Thomas /* Check if device supports TEE feature */
99cac32cd4SMario Limonciello if (!(psp->capability & PSP_CAPABILITY_TEE)) {
100f100ab62SRijo Thomas dev_dbg(psp->dev, "psp does not support TEE\n");
101f100ab62SRijo Thomas return -ENODEV;
102f100ab62SRijo Thomas }
103f100ab62SRijo Thomas
104f100ab62SRijo Thomas return 0;
105f100ab62SRijo Thomas }
106f100ab62SRijo Thomas
psp_init_platform_access(struct psp_device * psp)1077ccc4f4eSMario Limonciello static void psp_init_platform_access(struct psp_device *psp)
1087ccc4f4eSMario Limonciello {
1097ccc4f4eSMario Limonciello int ret;
1107ccc4f4eSMario Limonciello
1117ccc4f4eSMario Limonciello ret = platform_access_dev_init(psp);
1127ccc4f4eSMario Limonciello if (ret) {
1137ccc4f4eSMario Limonciello dev_warn(psp->dev, "platform access init failed: %d\n", ret);
1147ccc4f4eSMario Limonciello return;
1157ccc4f4eSMario Limonciello }
116*c04cf9e1SMario Limonciello
117*c04cf9e1SMario Limonciello /* dbc must come after platform access as it tests the feature */
118*c04cf9e1SMario Limonciello ret = dbc_dev_init(psp);
119*c04cf9e1SMario Limonciello if (ret)
120*c04cf9e1SMario Limonciello dev_warn(psp->dev, "failed to init dynamic boost control: %d\n",
121*c04cf9e1SMario Limonciello ret);
1227ccc4f4eSMario Limonciello }
1237ccc4f4eSMario Limonciello
psp_init(struct psp_device * psp)124cac32cd4SMario Limonciello static int psp_init(struct psp_device *psp)
12533960accSRijo Thomas {
12633960accSRijo Thomas int ret;
12733960accSRijo Thomas
128cac32cd4SMario Limonciello if (!psp_check_sev_support(psp)) {
12933960accSRijo Thomas ret = sev_dev_init(psp);
13033960accSRijo Thomas if (ret)
13133960accSRijo Thomas return ret;
13233960accSRijo Thomas }
13333960accSRijo Thomas
134cac32cd4SMario Limonciello if (!psp_check_tee_support(psp)) {
13533960accSRijo Thomas ret = tee_dev_init(psp);
13633960accSRijo Thomas if (ret)
13733960accSRijo Thomas return ret;
13833960accSRijo Thomas }
13933960accSRijo Thomas
1407ccc4f4eSMario Limonciello if (psp->vdata->platform_access)
1417ccc4f4eSMario Limonciello psp_init_platform_access(psp);
1427ccc4f4eSMario Limonciello
14333960accSRijo Thomas return 0;
14433960accSRijo Thomas }
14533960accSRijo Thomas
psp_dev_init(struct sp_device * sp)146b93566f1SRijo Thomas int psp_dev_init(struct sp_device *sp)
147b93566f1SRijo Thomas {
148b93566f1SRijo Thomas struct device *dev = sp->dev;
149b93566f1SRijo Thomas struct psp_device *psp;
150b93566f1SRijo Thomas int ret;
151b93566f1SRijo Thomas
152b93566f1SRijo Thomas ret = -ENOMEM;
153b93566f1SRijo Thomas psp = psp_alloc_struct(sp);
154b93566f1SRijo Thomas if (!psp)
155b93566f1SRijo Thomas goto e_err;
156b93566f1SRijo Thomas
157b93566f1SRijo Thomas sp->psp_data = psp;
158b93566f1SRijo Thomas
159b93566f1SRijo Thomas psp->vdata = (struct psp_vdata *)sp->dev_vdata->psp_vdata;
160b93566f1SRijo Thomas if (!psp->vdata) {
161b93566f1SRijo Thomas ret = -ENODEV;
162b93566f1SRijo Thomas dev_err(dev, "missing driver data\n");
163b93566f1SRijo Thomas goto e_err;
164b93566f1SRijo Thomas }
165b93566f1SRijo Thomas
166b93566f1SRijo Thomas psp->io_regs = sp->io_map;
167b93566f1SRijo Thomas
168cac32cd4SMario Limonciello ret = psp_get_capability(psp);
169cac32cd4SMario Limonciello if (ret)
170f100ab62SRijo Thomas goto e_disable;
171f100ab62SRijo Thomas
172b93566f1SRijo Thomas /* Disable and clear interrupts until ready */
173b93566f1SRijo Thomas iowrite32(0, psp->io_regs + psp->vdata->inten_reg);
174b93566f1SRijo Thomas iowrite32(-1, psp->io_regs + psp->vdata->intsts_reg);
175b93566f1SRijo Thomas
176b93566f1SRijo Thomas /* Request an irq */
177b93566f1SRijo Thomas ret = sp_request_psp_irq(psp->sp, psp_irq_handler, psp->name, psp);
178b93566f1SRijo Thomas if (ret) {
179b93566f1SRijo Thomas dev_err(dev, "psp: unable to allocate an IRQ\n");
180b93566f1SRijo Thomas goto e_err;
181b93566f1SRijo Thomas }
182b93566f1SRijo Thomas
183b8440d55SMario Limonciello /* master device must be set for platform access */
184b8440d55SMario Limonciello if (psp->sp->set_psp_master_device)
185b8440d55SMario Limonciello psp->sp->set_psp_master_device(psp->sp);
186b8440d55SMario Limonciello
187cac32cd4SMario Limonciello ret = psp_init(psp);
188b93566f1SRijo Thomas if (ret)
189b93566f1SRijo Thomas goto e_irq;
190b93566f1SRijo Thomas
191b93566f1SRijo Thomas /* Enable interrupt */
192b93566f1SRijo Thomas iowrite32(-1, psp->io_regs + psp->vdata->inten_reg);
193b93566f1SRijo Thomas
194b93566f1SRijo Thomas dev_notice(dev, "psp enabled\n");
195b93566f1SRijo Thomas
196b93566f1SRijo Thomas return 0;
197b93566f1SRijo Thomas
198b93566f1SRijo Thomas e_irq:
199b8440d55SMario Limonciello if (sp->clear_psp_master_device)
200b8440d55SMario Limonciello sp->clear_psp_master_device(sp);
201b8440d55SMario Limonciello
202b93566f1SRijo Thomas sp_free_psp_irq(psp->sp, psp);
203b93566f1SRijo Thomas e_err:
204b93566f1SRijo Thomas sp->psp_data = NULL;
205b93566f1SRijo Thomas
206b93566f1SRijo Thomas dev_notice(dev, "psp initialization failed\n");
207b93566f1SRijo Thomas
208b93566f1SRijo Thomas return ret;
209b93566f1SRijo Thomas
210b93566f1SRijo Thomas e_disable:
211b93566f1SRijo Thomas sp->psp_data = NULL;
212b93566f1SRijo Thomas
213b93566f1SRijo Thomas return ret;
214b93566f1SRijo Thomas }
215b93566f1SRijo Thomas
psp_dev_destroy(struct sp_device * sp)216b93566f1SRijo Thomas void psp_dev_destroy(struct sp_device *sp)
217b93566f1SRijo Thomas {
218b93566f1SRijo Thomas struct psp_device *psp = sp->psp_data;
219b93566f1SRijo Thomas
220b93566f1SRijo Thomas if (!psp)
221b93566f1SRijo Thomas return;
222b93566f1SRijo Thomas
223b93566f1SRijo Thomas sev_dev_destroy(psp);
224b93566f1SRijo Thomas
22533960accSRijo Thomas tee_dev_destroy(psp);
22633960accSRijo Thomas
227*c04cf9e1SMario Limonciello dbc_dev_destroy(psp);
228*c04cf9e1SMario Limonciello
2297ccc4f4eSMario Limonciello platform_access_dev_destroy(psp);
2307ccc4f4eSMario Limonciello
231b93566f1SRijo Thomas sp_free_psp_irq(sp, psp);
23215f7a4c6SJohn Allen
23315f7a4c6SJohn Allen if (sp->clear_psp_master_device)
23415f7a4c6SJohn Allen sp->clear_psp_master_device(sp);
235b93566f1SRijo Thomas }
236b93566f1SRijo Thomas
psp_set_sev_irq_handler(struct psp_device * psp,psp_irq_handler_t handler,void * data)237b93566f1SRijo Thomas void psp_set_sev_irq_handler(struct psp_device *psp, psp_irq_handler_t handler,
238b93566f1SRijo Thomas void *data)
239b93566f1SRijo Thomas {
240b93566f1SRijo Thomas psp->sev_irq_data = data;
241b93566f1SRijo Thomas psp->sev_irq_handler = handler;
242b93566f1SRijo Thomas }
243b93566f1SRijo Thomas
psp_clear_sev_irq_handler(struct psp_device * psp)244b93566f1SRijo Thomas void psp_clear_sev_irq_handler(struct psp_device *psp)
245b93566f1SRijo Thomas {
246b93566f1SRijo Thomas psp_set_sev_irq_handler(psp, NULL, NULL);
247b93566f1SRijo Thomas }
248b93566f1SRijo Thomas
psp_get_master_device(void)249b93566f1SRijo Thomas struct psp_device *psp_get_master_device(void)
250b93566f1SRijo Thomas {
251b93566f1SRijo Thomas struct sp_device *sp = sp_get_psp_master_device();
252b93566f1SRijo Thomas
253b93566f1SRijo Thomas return sp ? sp->psp_data : NULL;
254b93566f1SRijo Thomas }
255b93566f1SRijo Thomas
psp_pci_init(void)256b93566f1SRijo Thomas void psp_pci_init(void)
257b93566f1SRijo Thomas {
258b93566f1SRijo Thomas psp_master = psp_get_master_device();
259b93566f1SRijo Thomas
260b93566f1SRijo Thomas if (!psp_master)
261b93566f1SRijo Thomas return;
262b93566f1SRijo Thomas
263b93566f1SRijo Thomas sev_pci_init();
264b93566f1SRijo Thomas }
265b93566f1SRijo Thomas
psp_pci_exit(void)266b93566f1SRijo Thomas void psp_pci_exit(void)
267b93566f1SRijo Thomas {
268b93566f1SRijo Thomas if (!psp_master)
269b93566f1SRijo Thomas return;
270b93566f1SRijo Thomas
271b93566f1SRijo Thomas sev_pci_exit();
272b93566f1SRijo Thomas }
273