cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sp-dev.c (5912B)


      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
     25MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
     26MODULE_AUTHOR("Gary R Hook <gary.hook@amd.com>");
     27MODULE_LICENSE("GPL");
     28MODULE_VERSION("1.1.0");
     29MODULE_DESCRIPTION("AMD Secure Processor driver");
     30
     31/* List of SPs, SP count, read-write access lock, and access functions
     32 *
     33 * Lock structure: get sp_unit_lock for reading whenever we need to
     34 * examine the SP list.
     35 */
     36static DEFINE_RWLOCK(sp_unit_lock);
     37static LIST_HEAD(sp_units);
     38
     39/* Ever-increasing value to produce unique unit numbers */
     40static atomic_t sp_ordinal;
     41
     42static void sp_add_device(struct sp_device *sp)
     43{
     44	unsigned long flags;
     45
     46	write_lock_irqsave(&sp_unit_lock, flags);
     47
     48	list_add_tail(&sp->entry, &sp_units);
     49
     50	write_unlock_irqrestore(&sp_unit_lock, flags);
     51}
     52
     53static void sp_del_device(struct sp_device *sp)
     54{
     55	unsigned long flags;
     56
     57	write_lock_irqsave(&sp_unit_lock, flags);
     58
     59	list_del(&sp->entry);
     60
     61	write_unlock_irqrestore(&sp_unit_lock, flags);
     62}
     63
     64static irqreturn_t sp_irq_handler(int irq, void *data)
     65{
     66	struct sp_device *sp = data;
     67
     68	if (sp->ccp_irq_handler)
     69		sp->ccp_irq_handler(irq, sp->ccp_irq_data);
     70
     71	if (sp->psp_irq_handler)
     72		sp->psp_irq_handler(irq, sp->psp_irq_data);
     73
     74	return IRQ_HANDLED;
     75}
     76
     77int sp_request_ccp_irq(struct sp_device *sp, irq_handler_t handler,
     78		       const char *name, void *data)
     79{
     80	int ret;
     81
     82	if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) {
     83		/* Need a common routine to manage all interrupts */
     84		sp->ccp_irq_data = data;
     85		sp->ccp_irq_handler = handler;
     86
     87		if (!sp->irq_registered) {
     88			ret = request_irq(sp->ccp_irq, sp_irq_handler, 0,
     89					  sp->name, sp);
     90			if (ret)
     91				return ret;
     92
     93			sp->irq_registered = true;
     94		}
     95	} else {
     96		/* Each sub-device can manage it's own interrupt */
     97		ret = request_irq(sp->ccp_irq, handler, 0, name, data);
     98		if (ret)
     99			return ret;
    100	}
    101
    102	return 0;
    103}
    104
    105int sp_request_psp_irq(struct sp_device *sp, irq_handler_t handler,
    106		       const char *name, void *data)
    107{
    108	int ret;
    109
    110	if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) {
    111		/* Need a common routine to manage all interrupts */
    112		sp->psp_irq_data = data;
    113		sp->psp_irq_handler = handler;
    114
    115		if (!sp->irq_registered) {
    116			ret = request_irq(sp->psp_irq, sp_irq_handler, 0,
    117					  sp->name, sp);
    118			if (ret)
    119				return ret;
    120
    121			sp->irq_registered = true;
    122		}
    123	} else {
    124		/* Each sub-device can manage it's own interrupt */
    125		ret = request_irq(sp->psp_irq, handler, 0, name, data);
    126		if (ret)
    127			return ret;
    128	}
    129
    130	return 0;
    131}
    132
    133void sp_free_ccp_irq(struct sp_device *sp, void *data)
    134{
    135	if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) {
    136		/* Using common routine to manage all interrupts */
    137		if (!sp->psp_irq_handler) {
    138			/* Nothing else using it, so free it */
    139			free_irq(sp->ccp_irq, sp);
    140
    141			sp->irq_registered = false;
    142		}
    143
    144		sp->ccp_irq_handler = NULL;
    145		sp->ccp_irq_data = NULL;
    146	} else {
    147		/* Each sub-device can manage it's own interrupt */
    148		free_irq(sp->ccp_irq, data);
    149	}
    150}
    151
    152void sp_free_psp_irq(struct sp_device *sp, void *data)
    153{
    154	if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) {
    155		/* Using common routine to manage all interrupts */
    156		if (!sp->ccp_irq_handler) {
    157			/* Nothing else using it, so free it */
    158			free_irq(sp->psp_irq, sp);
    159
    160			sp->irq_registered = false;
    161		}
    162
    163		sp->psp_irq_handler = NULL;
    164		sp->psp_irq_data = NULL;
    165	} else {
    166		/* Each sub-device can manage it's own interrupt */
    167		free_irq(sp->psp_irq, data);
    168	}
    169}
    170
    171/**
    172 * sp_alloc_struct - allocate and initialize the sp_device struct
    173 *
    174 * @dev: device struct of the SP
    175 */
    176struct sp_device *sp_alloc_struct(struct device *dev)
    177{
    178	struct sp_device *sp;
    179
    180	sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
    181	if (!sp)
    182		return NULL;
    183
    184	sp->dev = dev;
    185	sp->ord = atomic_inc_return(&sp_ordinal);
    186	snprintf(sp->name, SP_MAX_NAME_LEN, "sp-%u", sp->ord);
    187
    188	return sp;
    189}
    190
    191int sp_init(struct sp_device *sp)
    192{
    193	sp_add_device(sp);
    194
    195	if (sp->dev_vdata->ccp_vdata)
    196		ccp_dev_init(sp);
    197
    198	if (sp->dev_vdata->psp_vdata)
    199		psp_dev_init(sp);
    200	return 0;
    201}
    202
    203void sp_destroy(struct sp_device *sp)
    204{
    205	if (sp->dev_vdata->ccp_vdata)
    206		ccp_dev_destroy(sp);
    207
    208	if (sp->dev_vdata->psp_vdata)
    209		psp_dev_destroy(sp);
    210
    211	sp_del_device(sp);
    212}
    213
    214int sp_suspend(struct sp_device *sp)
    215{
    216	if (sp->dev_vdata->ccp_vdata) {
    217		ccp_dev_suspend(sp);
    218	}
    219
    220	return 0;
    221}
    222
    223int sp_resume(struct sp_device *sp)
    224{
    225	if (sp->dev_vdata->ccp_vdata) {
    226		ccp_dev_resume(sp);
    227	}
    228
    229	return 0;
    230}
    231
    232struct sp_device *sp_get_psp_master_device(void)
    233{
    234	struct sp_device *i, *ret = NULL;
    235	unsigned long flags;
    236
    237	write_lock_irqsave(&sp_unit_lock, flags);
    238	if (list_empty(&sp_units))
    239		goto unlock;
    240
    241	list_for_each_entry(i, &sp_units, entry) {
    242		if (i->psp_data && i->get_psp_master_device) {
    243			ret = i->get_psp_master_device();
    244			break;
    245		}
    246	}
    247
    248unlock:
    249	write_unlock_irqrestore(&sp_unit_lock, flags);
    250	return ret;
    251}
    252
    253static int __init sp_mod_init(void)
    254{
    255#ifdef CONFIG_X86
    256	int ret;
    257
    258	ret = sp_pci_init();
    259	if (ret)
    260		return ret;
    261
    262#ifdef CONFIG_CRYPTO_DEV_SP_PSP
    263	psp_pci_init();
    264#endif
    265
    266	return 0;
    267#endif
    268
    269#ifdef CONFIG_ARM64
    270	int ret;
    271
    272	ret = sp_platform_init();
    273	if (ret)
    274		return ret;
    275
    276	return 0;
    277#endif
    278
    279	return -ENODEV;
    280}
    281
    282static void __exit sp_mod_exit(void)
    283{
    284#ifdef CONFIG_X86
    285
    286#ifdef CONFIG_CRYPTO_DEV_SP_PSP
    287	psp_pci_exit();
    288#endif
    289
    290	sp_pci_exit();
    291#endif
    292
    293#ifdef CONFIG_ARM64
    294	sp_platform_exit();
    295#endif
    296}
    297
    298module_init(sp_mod_init);
    299module_exit(sp_mod_exit);