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

cpci_hotplug_core.c (14589B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * CompactPCI Hot Plug Driver
      4 *
      5 * Copyright (C) 2002,2005 SOMA Networks, Inc.
      6 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
      7 * Copyright (C) 2001 IBM Corp.
      8 *
      9 * All rights reserved.
     10 *
     11 * Send feedback to <scottm@somanetworks.com>
     12 */
     13
     14#include <linux/module.h>
     15#include <linux/kernel.h>
     16#include <linux/sched/signal.h>
     17#include <linux/slab.h>
     18#include <linux/pci.h>
     19#include <linux/pci_hotplug.h>
     20#include <linux/init.h>
     21#include <linux/interrupt.h>
     22#include <linux/atomic.h>
     23#include <linux/delay.h>
     24#include <linux/kthread.h>
     25#include "cpci_hotplug.h"
     26
     27#define DRIVER_AUTHOR	"Scott Murray <scottm@somanetworks.com>"
     28#define DRIVER_DESC	"CompactPCI Hot Plug Core"
     29
     30#define MY_NAME	"cpci_hotplug"
     31
     32#define dbg(format, arg...)					\
     33	do {							\
     34		if (cpci_debug)					\
     35			printk(KERN_DEBUG "%s: " format "\n",	\
     36				MY_NAME, ## arg);		\
     37	} while (0)
     38#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg)
     39#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg)
     40#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg)
     41
     42/* local variables */
     43static DECLARE_RWSEM(list_rwsem);
     44static LIST_HEAD(slot_list);
     45static int slots;
     46static atomic_t extracting;
     47int cpci_debug;
     48static struct cpci_hp_controller *controller;
     49static struct task_struct *cpci_thread;
     50static int thread_finished;
     51
     52static int enable_slot(struct hotplug_slot *slot);
     53static int disable_slot(struct hotplug_slot *slot);
     54static int set_attention_status(struct hotplug_slot *slot, u8 value);
     55static int get_power_status(struct hotplug_slot *slot, u8 *value);
     56static int get_attention_status(struct hotplug_slot *slot, u8 *value);
     57static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
     58static int get_latch_status(struct hotplug_slot *slot, u8 *value);
     59
     60static const struct hotplug_slot_ops cpci_hotplug_slot_ops = {
     61	.enable_slot = enable_slot,
     62	.disable_slot = disable_slot,
     63	.set_attention_status = set_attention_status,
     64	.get_power_status = get_power_status,
     65	.get_attention_status = get_attention_status,
     66	.get_adapter_status = get_adapter_status,
     67	.get_latch_status = get_latch_status,
     68};
     69
     70static int
     71enable_slot(struct hotplug_slot *hotplug_slot)
     72{
     73	struct slot *slot = to_slot(hotplug_slot);
     74	int retval = 0;
     75
     76	dbg("%s - physical_slot = %s", __func__, slot_name(slot));
     77
     78	if (controller->ops->set_power)
     79		retval = controller->ops->set_power(slot, 1);
     80	return retval;
     81}
     82
     83static int
     84disable_slot(struct hotplug_slot *hotplug_slot)
     85{
     86	struct slot *slot = to_slot(hotplug_slot);
     87	int retval = 0;
     88
     89	dbg("%s - physical_slot = %s", __func__, slot_name(slot));
     90
     91	down_write(&list_rwsem);
     92
     93	/* Unconfigure device */
     94	dbg("%s - unconfiguring slot %s", __func__, slot_name(slot));
     95	retval = cpci_unconfigure_slot(slot);
     96	if (retval) {
     97		err("%s - could not unconfigure slot %s",
     98		    __func__, slot_name(slot));
     99		goto disable_error;
    100	}
    101	dbg("%s - finished unconfiguring slot %s", __func__, slot_name(slot));
    102
    103	/* Clear EXT (by setting it) */
    104	if (cpci_clear_ext(slot)) {
    105		err("%s - could not clear EXT for slot %s",
    106		    __func__, slot_name(slot));
    107		retval = -ENODEV;
    108		goto disable_error;
    109	}
    110	cpci_led_on(slot);
    111
    112	if (controller->ops->set_power) {
    113		retval = controller->ops->set_power(slot, 0);
    114		if (retval)
    115			goto disable_error;
    116	}
    117
    118	slot->adapter_status = 0;
    119
    120	if (slot->extracting) {
    121		slot->extracting = 0;
    122		atomic_dec(&extracting);
    123	}
    124disable_error:
    125	up_write(&list_rwsem);
    126	return retval;
    127}
    128
    129static u8
    130cpci_get_power_status(struct slot *slot)
    131{
    132	u8 power = 1;
    133
    134	if (controller->ops->get_power)
    135		power = controller->ops->get_power(slot);
    136	return power;
    137}
    138
    139static int
    140get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
    141{
    142	struct slot *slot = to_slot(hotplug_slot);
    143
    144	*value = cpci_get_power_status(slot);
    145	return 0;
    146}
    147
    148static int
    149get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
    150{
    151	struct slot *slot = to_slot(hotplug_slot);
    152
    153	*value = cpci_get_attention_status(slot);
    154	return 0;
    155}
    156
    157static int
    158set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
    159{
    160	return cpci_set_attention_status(to_slot(hotplug_slot), status);
    161}
    162
    163static int
    164get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
    165{
    166	struct slot *slot = to_slot(hotplug_slot);
    167
    168	*value = slot->adapter_status;
    169	return 0;
    170}
    171
    172static int
    173get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
    174{
    175	struct slot *slot = to_slot(hotplug_slot);
    176
    177	*value = slot->latch_status;
    178	return 0;
    179}
    180
    181static void release_slot(struct slot *slot)
    182{
    183	pci_dev_put(slot->dev);
    184	kfree(slot);
    185}
    186
    187#define SLOT_NAME_SIZE	6
    188
    189int
    190cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
    191{
    192	struct slot *slot;
    193	char name[SLOT_NAME_SIZE];
    194	int status;
    195	int i;
    196
    197	if (!(controller && bus))
    198		return -ENODEV;
    199
    200	/*
    201	 * Create a structure for each slot, and register that slot
    202	 * with the pci_hotplug subsystem.
    203	 */
    204	for (i = first; i <= last; ++i) {
    205		slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
    206		if (!slot) {
    207			status = -ENOMEM;
    208			goto error;
    209		}
    210
    211		slot->bus = bus;
    212		slot->number = i;
    213		slot->devfn = PCI_DEVFN(i, 0);
    214
    215		snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
    216
    217		slot->hotplug_slot.ops = &cpci_hotplug_slot_ops;
    218
    219		dbg("registering slot %s", name);
    220		status = pci_hp_register(&slot->hotplug_slot, bus, i, name);
    221		if (status) {
    222			err("pci_hp_register failed with error %d", status);
    223			goto error_slot;
    224		}
    225		dbg("slot registered with name: %s", slot_name(slot));
    226
    227		/* Add slot to our internal list */
    228		down_write(&list_rwsem);
    229		list_add(&slot->slot_list, &slot_list);
    230		slots++;
    231		up_write(&list_rwsem);
    232	}
    233	return 0;
    234error_slot:
    235	kfree(slot);
    236error:
    237	return status;
    238}
    239EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
    240
    241int
    242cpci_hp_unregister_bus(struct pci_bus *bus)
    243{
    244	struct slot *slot;
    245	struct slot *tmp;
    246	int status = 0;
    247
    248	down_write(&list_rwsem);
    249	if (!slots) {
    250		up_write(&list_rwsem);
    251		return -1;
    252	}
    253	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
    254		if (slot->bus == bus) {
    255			list_del(&slot->slot_list);
    256			slots--;
    257
    258			dbg("deregistering slot %s", slot_name(slot));
    259			pci_hp_deregister(&slot->hotplug_slot);
    260			release_slot(slot);
    261		}
    262	}
    263	up_write(&list_rwsem);
    264	return status;
    265}
    266EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
    267
    268/* This is the interrupt mode interrupt handler */
    269static irqreturn_t
    270cpci_hp_intr(int irq, void *data)
    271{
    272	dbg("entered cpci_hp_intr");
    273
    274	/* Check to see if it was our interrupt */
    275	if ((controller->irq_flags & IRQF_SHARED) &&
    276	    !controller->ops->check_irq(controller->dev_id)) {
    277		dbg("exited cpci_hp_intr, not our interrupt");
    278		return IRQ_NONE;
    279	}
    280
    281	/* Disable ENUM interrupt */
    282	controller->ops->disable_irq();
    283
    284	/* Trigger processing by the event thread */
    285	wake_up_process(cpci_thread);
    286	return IRQ_HANDLED;
    287}
    288
    289/*
    290 * According to PICMG 2.1 R2.0, section 6.3.2, upon
    291 * initialization, the system driver shall clear the
    292 * INS bits of the cold-inserted devices.
    293 */
    294static int
    295init_slots(int clear_ins)
    296{
    297	struct slot *slot;
    298	struct pci_dev *dev;
    299
    300	dbg("%s - enter", __func__);
    301	down_read(&list_rwsem);
    302	if (!slots) {
    303		up_read(&list_rwsem);
    304		return -1;
    305	}
    306	list_for_each_entry(slot, &slot_list, slot_list) {
    307		dbg("%s - looking at slot %s", __func__, slot_name(slot));
    308		if (clear_ins && cpci_check_and_clear_ins(slot))
    309			dbg("%s - cleared INS for slot %s",
    310			    __func__, slot_name(slot));
    311		dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
    312		if (dev) {
    313			slot->adapter_status = 1;
    314			slot->latch_status = 1;
    315			slot->dev = dev;
    316		}
    317	}
    318	up_read(&list_rwsem);
    319	dbg("%s - exit", __func__);
    320	return 0;
    321}
    322
    323static int
    324check_slots(void)
    325{
    326	struct slot *slot;
    327	int extracted;
    328	int inserted;
    329	u16 hs_csr;
    330
    331	down_read(&list_rwsem);
    332	if (!slots) {
    333		up_read(&list_rwsem);
    334		err("no slots registered, shutting down");
    335		return -1;
    336	}
    337	extracted = inserted = 0;
    338	list_for_each_entry(slot, &slot_list, slot_list) {
    339		dbg("%s - looking at slot %s", __func__, slot_name(slot));
    340		if (cpci_check_and_clear_ins(slot)) {
    341			/*
    342			 * Some broken hardware (e.g. PLX 9054AB) asserts
    343			 * ENUM# twice...
    344			 */
    345			if (slot->dev) {
    346				warn("slot %s already inserted",
    347				     slot_name(slot));
    348				inserted++;
    349				continue;
    350			}
    351
    352			/* Process insertion */
    353			dbg("%s - slot %s inserted", __func__, slot_name(slot));
    354
    355			/* GSM, debug */
    356			hs_csr = cpci_get_hs_csr(slot);
    357			dbg("%s - slot %s HS_CSR (1) = %04x",
    358			    __func__, slot_name(slot), hs_csr);
    359
    360			/* Configure device */
    361			dbg("%s - configuring slot %s",
    362			    __func__, slot_name(slot));
    363			if (cpci_configure_slot(slot)) {
    364				err("%s - could not configure slot %s",
    365				    __func__, slot_name(slot));
    366				continue;
    367			}
    368			dbg("%s - finished configuring slot %s",
    369			    __func__, slot_name(slot));
    370
    371			/* GSM, debug */
    372			hs_csr = cpci_get_hs_csr(slot);
    373			dbg("%s - slot %s HS_CSR (2) = %04x",
    374			    __func__, slot_name(slot), hs_csr);
    375
    376			slot->latch_status = 1;
    377			slot->adapter_status = 1;
    378
    379			cpci_led_off(slot);
    380
    381			/* GSM, debug */
    382			hs_csr = cpci_get_hs_csr(slot);
    383			dbg("%s - slot %s HS_CSR (3) = %04x",
    384			    __func__, slot_name(slot), hs_csr);
    385
    386			inserted++;
    387		} else if (cpci_check_ext(slot)) {
    388			/* Process extraction request */
    389			dbg("%s - slot %s extracted",
    390			    __func__, slot_name(slot));
    391
    392			/* GSM, debug */
    393			hs_csr = cpci_get_hs_csr(slot);
    394			dbg("%s - slot %s HS_CSR = %04x",
    395			    __func__, slot_name(slot), hs_csr);
    396
    397			if (!slot->extracting) {
    398				slot->latch_status = 0;
    399				slot->extracting = 1;
    400				atomic_inc(&extracting);
    401			}
    402			extracted++;
    403		} else if (slot->extracting) {
    404			hs_csr = cpci_get_hs_csr(slot);
    405			if (hs_csr == 0xffff) {
    406				/*
    407				 * Hmmm, we're likely hosed at this point, should we
    408				 * bother trying to tell the driver or not?
    409				 */
    410				err("card in slot %s was improperly removed",
    411				    slot_name(slot));
    412				slot->adapter_status = 0;
    413				slot->extracting = 0;
    414				atomic_dec(&extracting);
    415			}
    416		}
    417	}
    418	up_read(&list_rwsem);
    419	dbg("inserted=%d, extracted=%d, extracting=%d",
    420	    inserted, extracted, atomic_read(&extracting));
    421	if (inserted || extracted)
    422		return extracted;
    423	else if (!atomic_read(&extracting)) {
    424		err("cannot find ENUM# source, shutting down");
    425		return -1;
    426	}
    427	return 0;
    428}
    429
    430/* This is the interrupt mode worker thread body */
    431static int
    432event_thread(void *data)
    433{
    434	int rc;
    435
    436	dbg("%s - event thread started", __func__);
    437	while (1) {
    438		dbg("event thread sleeping");
    439		set_current_state(TASK_INTERRUPTIBLE);
    440		schedule();
    441		if (kthread_should_stop())
    442			break;
    443		do {
    444			rc = check_slots();
    445			if (rc > 0) {
    446				/* Give userspace a chance to handle extraction */
    447				msleep(500);
    448			} else if (rc < 0) {
    449				dbg("%s - error checking slots", __func__);
    450				thread_finished = 1;
    451				goto out;
    452			}
    453		} while (atomic_read(&extracting) && !kthread_should_stop());
    454		if (kthread_should_stop())
    455			break;
    456
    457		/* Re-enable ENUM# interrupt */
    458		dbg("%s - re-enabling irq", __func__);
    459		controller->ops->enable_irq();
    460	}
    461 out:
    462	return 0;
    463}
    464
    465/* This is the polling mode worker thread body */
    466static int
    467poll_thread(void *data)
    468{
    469	int rc;
    470
    471	while (1) {
    472		if (kthread_should_stop() || signal_pending(current))
    473			break;
    474		if (controller->ops->query_enum()) {
    475			do {
    476				rc = check_slots();
    477				if (rc > 0) {
    478					/* Give userspace a chance to handle extraction */
    479					msleep(500);
    480				} else if (rc < 0) {
    481					dbg("%s - error checking slots", __func__);
    482					thread_finished = 1;
    483					goto out;
    484				}
    485			} while (atomic_read(&extracting) && !kthread_should_stop());
    486		}
    487		msleep(100);
    488	}
    489 out:
    490	return 0;
    491}
    492
    493static int
    494cpci_start_thread(void)
    495{
    496	if (controller->irq)
    497		cpci_thread = kthread_run(event_thread, NULL, "cpci_hp_eventd");
    498	else
    499		cpci_thread = kthread_run(poll_thread, NULL, "cpci_hp_polld");
    500	if (IS_ERR(cpci_thread)) {
    501		err("Can't start up our thread");
    502		return PTR_ERR(cpci_thread);
    503	}
    504	thread_finished = 0;
    505	return 0;
    506}
    507
    508static void
    509cpci_stop_thread(void)
    510{
    511	kthread_stop(cpci_thread);
    512	thread_finished = 1;
    513}
    514
    515int
    516cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
    517{
    518	int status = 0;
    519
    520	if (controller)
    521		return -1;
    522	if (!(new_controller && new_controller->ops))
    523		return -EINVAL;
    524	if (new_controller->irq) {
    525		if (!(new_controller->ops->enable_irq &&
    526		     new_controller->ops->disable_irq))
    527			status = -EINVAL;
    528		if (request_irq(new_controller->irq,
    529			       cpci_hp_intr,
    530			       new_controller->irq_flags,
    531			       MY_NAME,
    532			       new_controller->dev_id)) {
    533			err("Can't get irq %d for the hotplug cPCI controller",
    534			    new_controller->irq);
    535			status = -ENODEV;
    536		}
    537		dbg("%s - acquired controller irq %d",
    538		    __func__, new_controller->irq);
    539	}
    540	if (!status)
    541		controller = new_controller;
    542	return status;
    543}
    544EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
    545
    546static void
    547cleanup_slots(void)
    548{
    549	struct slot *slot;
    550	struct slot *tmp;
    551
    552	/*
    553	 * Unregister all of our slots with the pci_hotplug subsystem,
    554	 * and free up all memory that we had allocated.
    555	 */
    556	down_write(&list_rwsem);
    557	if (!slots)
    558		goto cleanup_null;
    559	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
    560		list_del(&slot->slot_list);
    561		pci_hp_deregister(&slot->hotplug_slot);
    562		release_slot(slot);
    563	}
    564cleanup_null:
    565	up_write(&list_rwsem);
    566}
    567
    568int
    569cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
    570{
    571	int status = 0;
    572
    573	if (controller) {
    574		if (!thread_finished)
    575			cpci_stop_thread();
    576		if (controller->irq)
    577			free_irq(controller->irq, controller->dev_id);
    578		controller = NULL;
    579		cleanup_slots();
    580	} else
    581		status = -ENODEV;
    582	return status;
    583}
    584EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
    585
    586int
    587cpci_hp_start(void)
    588{
    589	static int first = 1;
    590	int status;
    591
    592	dbg("%s - enter", __func__);
    593	if (!controller)
    594		return -ENODEV;
    595
    596	down_read(&list_rwsem);
    597	if (list_empty(&slot_list)) {
    598		up_read(&list_rwsem);
    599		return -ENODEV;
    600	}
    601	up_read(&list_rwsem);
    602
    603	status = init_slots(first);
    604	if (first)
    605		first = 0;
    606	if (status)
    607		return status;
    608
    609	status = cpci_start_thread();
    610	if (status)
    611		return status;
    612	dbg("%s - thread started", __func__);
    613
    614	if (controller->irq) {
    615		/* Start enum interrupt processing */
    616		dbg("%s - enabling irq", __func__);
    617		controller->ops->enable_irq();
    618	}
    619	dbg("%s - exit", __func__);
    620	return 0;
    621}
    622EXPORT_SYMBOL_GPL(cpci_hp_start);
    623
    624int
    625cpci_hp_stop(void)
    626{
    627	if (!controller)
    628		return -ENODEV;
    629	if (controller->irq) {
    630		/* Stop enum interrupt processing */
    631		dbg("%s - disabling irq", __func__);
    632		controller->ops->disable_irq();
    633	}
    634	cpci_stop_thread();
    635	return 0;
    636}
    637EXPORT_SYMBOL_GPL(cpci_hp_stop);
    638
    639int __init
    640cpci_hotplug_init(int debug)
    641{
    642	cpci_debug = debug;
    643	return 0;
    644}