xref: /openbmc/linux/drivers/crypto/ccp/psp-dev.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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