18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AMD Platform Security Processor (PSP) interface
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016,2019 Advanced Micro Devices, Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Brijesh Singh <brijesh.singh@amd.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/irqreturn.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "sp-dev.h"
148c2ecf20Sopenharmony_ci#include "psp-dev.h"
158c2ecf20Sopenharmony_ci#include "sev-dev.h"
168c2ecf20Sopenharmony_ci#include "tee-dev.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct psp_device *psp_master;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct psp_device *psp_alloc_struct(struct sp_device *sp)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct device *dev = sp->dev;
238c2ecf20Sopenharmony_ci	struct psp_device *psp;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	psp = devm_kzalloc(dev, sizeof(*psp), GFP_KERNEL);
268c2ecf20Sopenharmony_ci	if (!psp)
278c2ecf20Sopenharmony_ci		return NULL;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	psp->dev = dev;
308c2ecf20Sopenharmony_ci	psp->sp = sp;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	snprintf(psp->name, sizeof(psp->name), "psp-%u", sp->ord);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return psp;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic irqreturn_t psp_irq_handler(int irq, void *data)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct psp_device *psp = data;
408c2ecf20Sopenharmony_ci	unsigned int status;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* Read the interrupt status: */
438c2ecf20Sopenharmony_ci	status = ioread32(psp->io_regs + psp->vdata->intsts_reg);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* Clear the interrupt status by writing the same value we read. */
468c2ecf20Sopenharmony_ci	iowrite32(status, psp->io_regs + psp->vdata->intsts_reg);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/* invoke subdevice interrupt handlers */
498c2ecf20Sopenharmony_ci	if (status) {
508c2ecf20Sopenharmony_ci		if (psp->sev_irq_handler)
518c2ecf20Sopenharmony_ci			psp->sev_irq_handler(irq, psp->sev_irq_data, status);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci		if (psp->tee_irq_handler)
548c2ecf20Sopenharmony_ci			psp->tee_irq_handler(irq, psp->tee_irq_data, status);
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic unsigned int psp_get_capability(struct psp_device *psp)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	unsigned int val = ioread32(psp->io_regs + psp->vdata->feature_reg);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/*
658c2ecf20Sopenharmony_ci	 * Check for a access to the registers.  If this read returns
668c2ecf20Sopenharmony_ci	 * 0xffffffff, it's likely that the system is running a broken
678c2ecf20Sopenharmony_ci	 * BIOS which disallows access to the device. Stop here and
688c2ecf20Sopenharmony_ci	 * fail the PSP initialization (but not the load, as the CCP
698c2ecf20Sopenharmony_ci	 * could get properly initialized).
708c2ecf20Sopenharmony_ci	 */
718c2ecf20Sopenharmony_ci	if (val == 0xffffffff) {
728c2ecf20Sopenharmony_ci		dev_notice(psp->dev, "psp: unable to access the device: you might be running a broken BIOS.\n");
738c2ecf20Sopenharmony_ci		return 0;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return val;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int psp_check_sev_support(struct psp_device *psp,
808c2ecf20Sopenharmony_ci				 unsigned int capability)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	/* Check if device supports SEV feature */
838c2ecf20Sopenharmony_ci	if (!(capability & 1)) {
848c2ecf20Sopenharmony_ci		dev_dbg(psp->dev, "psp does not support SEV\n");
858c2ecf20Sopenharmony_ci		return -ENODEV;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int psp_check_tee_support(struct psp_device *psp,
928c2ecf20Sopenharmony_ci				 unsigned int capability)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	/* Check if device supports TEE feature */
958c2ecf20Sopenharmony_ci	if (!(capability & 2)) {
968c2ecf20Sopenharmony_ci		dev_dbg(psp->dev, "psp does not support TEE\n");
978c2ecf20Sopenharmony_ci		return -ENODEV;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int psp_check_support(struct psp_device *psp,
1048c2ecf20Sopenharmony_ci			     unsigned int capability)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int sev_support = psp_check_sev_support(psp, capability);
1078c2ecf20Sopenharmony_ci	int tee_support = psp_check_tee_support(psp, capability);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Return error if device neither supports SEV nor TEE */
1108c2ecf20Sopenharmony_ci	if (sev_support && tee_support)
1118c2ecf20Sopenharmony_ci		return -ENODEV;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int psp_init(struct psp_device *psp, unsigned int capability)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int ret;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (!psp_check_sev_support(psp, capability)) {
1218c2ecf20Sopenharmony_ci		ret = sev_dev_init(psp);
1228c2ecf20Sopenharmony_ci		if (ret)
1238c2ecf20Sopenharmony_ci			return ret;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (!psp_check_tee_support(psp, capability)) {
1278c2ecf20Sopenharmony_ci		ret = tee_dev_init(psp);
1288c2ecf20Sopenharmony_ci		if (ret)
1298c2ecf20Sopenharmony_ci			return ret;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciint psp_dev_init(struct sp_device *sp)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct device *dev = sp->dev;
1388c2ecf20Sopenharmony_ci	struct psp_device *psp;
1398c2ecf20Sopenharmony_ci	unsigned int capability;
1408c2ecf20Sopenharmony_ci	int ret;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	ret = -ENOMEM;
1438c2ecf20Sopenharmony_ci	psp = psp_alloc_struct(sp);
1448c2ecf20Sopenharmony_ci	if (!psp)
1458c2ecf20Sopenharmony_ci		goto e_err;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	sp->psp_data = psp;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	psp->vdata = (struct psp_vdata *)sp->dev_vdata->psp_vdata;
1508c2ecf20Sopenharmony_ci	if (!psp->vdata) {
1518c2ecf20Sopenharmony_ci		ret = -ENODEV;
1528c2ecf20Sopenharmony_ci		dev_err(dev, "missing driver data\n");
1538c2ecf20Sopenharmony_ci		goto e_err;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	psp->io_regs = sp->io_map;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	capability = psp_get_capability(psp);
1598c2ecf20Sopenharmony_ci	if (!capability)
1608c2ecf20Sopenharmony_ci		goto e_disable;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	ret = psp_check_support(psp, capability);
1638c2ecf20Sopenharmony_ci	if (ret)
1648c2ecf20Sopenharmony_ci		goto e_disable;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Disable and clear interrupts until ready */
1678c2ecf20Sopenharmony_ci	iowrite32(0, psp->io_regs + psp->vdata->inten_reg);
1688c2ecf20Sopenharmony_ci	iowrite32(-1, psp->io_regs + psp->vdata->intsts_reg);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Request an irq */
1718c2ecf20Sopenharmony_ci	ret = sp_request_psp_irq(psp->sp, psp_irq_handler, psp->name, psp);
1728c2ecf20Sopenharmony_ci	if (ret) {
1738c2ecf20Sopenharmony_ci		dev_err(dev, "psp: unable to allocate an IRQ\n");
1748c2ecf20Sopenharmony_ci		goto e_err;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	ret = psp_init(psp, capability);
1788c2ecf20Sopenharmony_ci	if (ret)
1798c2ecf20Sopenharmony_ci		goto e_irq;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (sp->set_psp_master_device)
1828c2ecf20Sopenharmony_ci		sp->set_psp_master_device(sp);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Enable interrupt */
1858c2ecf20Sopenharmony_ci	iowrite32(-1, psp->io_regs + psp->vdata->inten_reg);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	dev_notice(dev, "psp enabled\n");
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cie_irq:
1928c2ecf20Sopenharmony_ci	sp_free_psp_irq(psp->sp, psp);
1938c2ecf20Sopenharmony_cie_err:
1948c2ecf20Sopenharmony_ci	sp->psp_data = NULL;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	dev_notice(dev, "psp initialization failed\n");
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cie_disable:
2018c2ecf20Sopenharmony_ci	sp->psp_data = NULL;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return ret;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_civoid psp_dev_destroy(struct sp_device *sp)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct psp_device *psp = sp->psp_data;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (!psp)
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	sev_dev_destroy(psp);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	tee_dev_destroy(psp);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	sp_free_psp_irq(sp, psp);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (sp->clear_psp_master_device)
2208c2ecf20Sopenharmony_ci		sp->clear_psp_master_device(sp);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_civoid psp_set_sev_irq_handler(struct psp_device *psp, psp_irq_handler_t handler,
2248c2ecf20Sopenharmony_ci			     void *data)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	psp->sev_irq_data = data;
2278c2ecf20Sopenharmony_ci	psp->sev_irq_handler = handler;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_civoid psp_clear_sev_irq_handler(struct psp_device *psp)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	psp_set_sev_irq_handler(psp, NULL, NULL);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_civoid psp_set_tee_irq_handler(struct psp_device *psp, psp_irq_handler_t handler,
2368c2ecf20Sopenharmony_ci			     void *data)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	psp->tee_irq_data = data;
2398c2ecf20Sopenharmony_ci	psp->tee_irq_handler = handler;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_civoid psp_clear_tee_irq_handler(struct psp_device *psp)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	psp_set_tee_irq_handler(psp, NULL, NULL);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistruct psp_device *psp_get_master_device(void)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct sp_device *sp = sp_get_psp_master_device();
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return sp ? sp->psp_data : NULL;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_civoid psp_pci_init(void)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	psp_master = psp_get_master_device();
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (!psp_master)
2598c2ecf20Sopenharmony_ci		return;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	sev_pci_init();
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_civoid psp_pci_exit(void)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	if (!psp_master)
2678c2ecf20Sopenharmony_ci		return;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	sev_pci_exit();
2708c2ecf20Sopenharmony_ci}
271