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

pci_hotplug_core.c (14527B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * PCI HotPlug Controller Core
      4 *
      5 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
      6 * Copyright (C) 2001-2002 IBM Corp.
      7 *
      8 * All rights reserved.
      9 *
     10 * Send feedback to <kristen.c.accardi@intel.com>
     11 *
     12 * Authors:
     13 *   Greg Kroah-Hartman <greg@kroah.com>
     14 *   Scott Murray <scottm@somanetworks.com>
     15 */
     16
     17#include <linux/module.h>	/* try_module_get & module_put */
     18#include <linux/moduleparam.h>
     19#include <linux/kernel.h>
     20#include <linux/types.h>
     21#include <linux/list.h>
     22#include <linux/kobject.h>
     23#include <linux/sysfs.h>
     24#include <linux/pagemap.h>
     25#include <linux/init.h>
     26#include <linux/mount.h>
     27#include <linux/namei.h>
     28#include <linux/mutex.h>
     29#include <linux/pci.h>
     30#include <linux/pci_hotplug.h>
     31#include <linux/uaccess.h>
     32#include "../pci.h"
     33#include "cpci_hotplug.h"
     34
     35#define MY_NAME	"pci_hotplug"
     36
     37#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
     38#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
     39#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
     40#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
     41
     42/* local variables */
     43static bool debug;
     44
     45static LIST_HEAD(pci_hotplug_slot_list);
     46static DEFINE_MUTEX(pci_hp_mutex);
     47
     48/* Weee, fun with macros... */
     49#define GET_STATUS(name, type)	\
     50static int get_##name(struct hotplug_slot *slot, type *value)		\
     51{									\
     52	const struct hotplug_slot_ops *ops = slot->ops;			\
     53	int retval = 0;							\
     54	if (!try_module_get(slot->owner))				\
     55		return -ENODEV;						\
     56	if (ops->get_##name)						\
     57		retval = ops->get_##name(slot, value);			\
     58	module_put(slot->owner);					\
     59	return retval;							\
     60}
     61
     62GET_STATUS(power_status, u8)
     63GET_STATUS(attention_status, u8)
     64GET_STATUS(latch_status, u8)
     65GET_STATUS(adapter_status, u8)
     66
     67static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
     68{
     69	int retval;
     70	u8 value;
     71
     72	retval = get_power_status(pci_slot->hotplug, &value);
     73	if (retval)
     74		return retval;
     75
     76	return sysfs_emit(buf, "%d\n", value);
     77}
     78
     79static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
     80				size_t count)
     81{
     82	struct hotplug_slot *slot = pci_slot->hotplug;
     83	unsigned long lpower;
     84	u8 power;
     85	int retval = 0;
     86
     87	lpower = simple_strtoul(buf, NULL, 10);
     88	power = (u8)(lpower & 0xff);
     89	dbg("power = %d\n", power);
     90
     91	if (!try_module_get(slot->owner)) {
     92		retval = -ENODEV;
     93		goto exit;
     94	}
     95	switch (power) {
     96	case 0:
     97		if (slot->ops->disable_slot)
     98			retval = slot->ops->disable_slot(slot);
     99		break;
    100
    101	case 1:
    102		if (slot->ops->enable_slot)
    103			retval = slot->ops->enable_slot(slot);
    104		break;
    105
    106	default:
    107		err("Illegal value specified for power\n");
    108		retval = -EINVAL;
    109	}
    110	module_put(slot->owner);
    111
    112exit:
    113	if (retval)
    114		return retval;
    115	return count;
    116}
    117
    118static struct pci_slot_attribute hotplug_slot_attr_power = {
    119	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
    120	.show = power_read_file,
    121	.store = power_write_file
    122};
    123
    124static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
    125{
    126	int retval;
    127	u8 value;
    128
    129	retval = get_attention_status(pci_slot->hotplug, &value);
    130	if (retval)
    131		return retval;
    132
    133	return sysfs_emit(buf, "%d\n", value);
    134}
    135
    136static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
    137				    size_t count)
    138{
    139	struct hotplug_slot *slot = pci_slot->hotplug;
    140	const struct hotplug_slot_ops *ops = slot->ops;
    141	unsigned long lattention;
    142	u8 attention;
    143	int retval = 0;
    144
    145	lattention = simple_strtoul(buf, NULL, 10);
    146	attention = (u8)(lattention & 0xff);
    147	dbg(" - attention = %d\n", attention);
    148
    149	if (!try_module_get(slot->owner)) {
    150		retval = -ENODEV;
    151		goto exit;
    152	}
    153	if (ops->set_attention_status)
    154		retval = ops->set_attention_status(slot, attention);
    155	module_put(slot->owner);
    156
    157exit:
    158	if (retval)
    159		return retval;
    160	return count;
    161}
    162
    163static struct pci_slot_attribute hotplug_slot_attr_attention = {
    164	.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
    165	.show = attention_read_file,
    166	.store = attention_write_file
    167};
    168
    169static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
    170{
    171	int retval;
    172	u8 value;
    173
    174	retval = get_latch_status(pci_slot->hotplug, &value);
    175	if (retval)
    176		return retval;
    177
    178	return sysfs_emit(buf, "%d\n", value);
    179}
    180
    181static struct pci_slot_attribute hotplug_slot_attr_latch = {
    182	.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
    183	.show = latch_read_file,
    184};
    185
    186static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
    187{
    188	int retval;
    189	u8 value;
    190
    191	retval = get_adapter_status(pci_slot->hotplug, &value);
    192	if (retval)
    193		return retval;
    194
    195	return sysfs_emit(buf, "%d\n", value);
    196}
    197
    198static struct pci_slot_attribute hotplug_slot_attr_presence = {
    199	.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
    200	.show = presence_read_file,
    201};
    202
    203static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
    204			       size_t count)
    205{
    206	struct hotplug_slot *slot = pci_slot->hotplug;
    207	unsigned long ltest;
    208	u32 test;
    209	int retval = 0;
    210
    211	ltest = simple_strtoul(buf, NULL, 10);
    212	test = (u32)(ltest & 0xffffffff);
    213	dbg("test = %d\n", test);
    214
    215	if (!try_module_get(slot->owner)) {
    216		retval = -ENODEV;
    217		goto exit;
    218	}
    219	if (slot->ops->hardware_test)
    220		retval = slot->ops->hardware_test(slot, test);
    221	module_put(slot->owner);
    222
    223exit:
    224	if (retval)
    225		return retval;
    226	return count;
    227}
    228
    229static struct pci_slot_attribute hotplug_slot_attr_test = {
    230	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
    231	.store = test_write_file
    232};
    233
    234static bool has_power_file(struct pci_slot *pci_slot)
    235{
    236	struct hotplug_slot *slot = pci_slot->hotplug;
    237
    238	if ((!slot) || (!slot->ops))
    239		return false;
    240	if ((slot->ops->enable_slot) ||
    241	    (slot->ops->disable_slot) ||
    242	    (slot->ops->get_power_status))
    243		return true;
    244	return false;
    245}
    246
    247static bool has_attention_file(struct pci_slot *pci_slot)
    248{
    249	struct hotplug_slot *slot = pci_slot->hotplug;
    250
    251	if ((!slot) || (!slot->ops))
    252		return false;
    253	if ((slot->ops->set_attention_status) ||
    254	    (slot->ops->get_attention_status))
    255		return true;
    256	return false;
    257}
    258
    259static bool has_latch_file(struct pci_slot *pci_slot)
    260{
    261	struct hotplug_slot *slot = pci_slot->hotplug;
    262
    263	if ((!slot) || (!slot->ops))
    264		return false;
    265	if (slot->ops->get_latch_status)
    266		return true;
    267	return false;
    268}
    269
    270static bool has_adapter_file(struct pci_slot *pci_slot)
    271{
    272	struct hotplug_slot *slot = pci_slot->hotplug;
    273
    274	if ((!slot) || (!slot->ops))
    275		return false;
    276	if (slot->ops->get_adapter_status)
    277		return true;
    278	return false;
    279}
    280
    281static bool has_test_file(struct pci_slot *pci_slot)
    282{
    283	struct hotplug_slot *slot = pci_slot->hotplug;
    284
    285	if ((!slot) || (!slot->ops))
    286		return false;
    287	if (slot->ops->hardware_test)
    288		return true;
    289	return false;
    290}
    291
    292static int fs_add_slot(struct pci_slot *pci_slot)
    293{
    294	int retval = 0;
    295
    296	/* Create symbolic link to the hotplug driver module */
    297	pci_hp_create_module_link(pci_slot);
    298
    299	if (has_power_file(pci_slot)) {
    300		retval = sysfs_create_file(&pci_slot->kobj,
    301					   &hotplug_slot_attr_power.attr);
    302		if (retval)
    303			goto exit_power;
    304	}
    305
    306	if (has_attention_file(pci_slot)) {
    307		retval = sysfs_create_file(&pci_slot->kobj,
    308					   &hotplug_slot_attr_attention.attr);
    309		if (retval)
    310			goto exit_attention;
    311	}
    312
    313	if (has_latch_file(pci_slot)) {
    314		retval = sysfs_create_file(&pci_slot->kobj,
    315					   &hotplug_slot_attr_latch.attr);
    316		if (retval)
    317			goto exit_latch;
    318	}
    319
    320	if (has_adapter_file(pci_slot)) {
    321		retval = sysfs_create_file(&pci_slot->kobj,
    322					   &hotplug_slot_attr_presence.attr);
    323		if (retval)
    324			goto exit_adapter;
    325	}
    326
    327	if (has_test_file(pci_slot)) {
    328		retval = sysfs_create_file(&pci_slot->kobj,
    329					   &hotplug_slot_attr_test.attr);
    330		if (retval)
    331			goto exit_test;
    332	}
    333
    334	goto exit;
    335
    336exit_test:
    337	if (has_adapter_file(pci_slot))
    338		sysfs_remove_file(&pci_slot->kobj,
    339				  &hotplug_slot_attr_presence.attr);
    340exit_adapter:
    341	if (has_latch_file(pci_slot))
    342		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
    343exit_latch:
    344	if (has_attention_file(pci_slot))
    345		sysfs_remove_file(&pci_slot->kobj,
    346				  &hotplug_slot_attr_attention.attr);
    347exit_attention:
    348	if (has_power_file(pci_slot))
    349		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
    350exit_power:
    351	pci_hp_remove_module_link(pci_slot);
    352exit:
    353	return retval;
    354}
    355
    356static void fs_remove_slot(struct pci_slot *pci_slot)
    357{
    358	if (has_power_file(pci_slot))
    359		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
    360
    361	if (has_attention_file(pci_slot))
    362		sysfs_remove_file(&pci_slot->kobj,
    363				  &hotplug_slot_attr_attention.attr);
    364
    365	if (has_latch_file(pci_slot))
    366		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
    367
    368	if (has_adapter_file(pci_slot))
    369		sysfs_remove_file(&pci_slot->kobj,
    370				  &hotplug_slot_attr_presence.attr);
    371
    372	if (has_test_file(pci_slot))
    373		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);
    374
    375	pci_hp_remove_module_link(pci_slot);
    376}
    377
    378static struct hotplug_slot *get_slot_from_name(const char *name)
    379{
    380	struct hotplug_slot *slot;
    381
    382	list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) {
    383		if (strcmp(hotplug_slot_name(slot), name) == 0)
    384			return slot;
    385	}
    386	return NULL;
    387}
    388
    389/**
    390 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
    391 * @bus: bus this slot is on
    392 * @slot: pointer to the &struct hotplug_slot to register
    393 * @devnr: device number
    394 * @name: name registered with kobject core
    395 * @owner: caller module owner
    396 * @mod_name: caller module name
    397 *
    398 * Prepares a hotplug slot for in-kernel use and immediately publishes it to
    399 * user space in one go.  Drivers may alternatively carry out the two steps
    400 * separately by invoking pci_hp_initialize() and pci_hp_add().
    401 *
    402 * Returns 0 if successful, anything else for an error.
    403 */
    404int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
    405		      int devnr, const char *name,
    406		      struct module *owner, const char *mod_name)
    407{
    408	int result;
    409
    410	result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);
    411	if (result)
    412		return result;
    413
    414	result = pci_hp_add(slot);
    415	if (result)
    416		pci_hp_destroy(slot);
    417
    418	return result;
    419}
    420EXPORT_SYMBOL_GPL(__pci_hp_register);
    421
    422/**
    423 * __pci_hp_initialize - prepare hotplug slot for in-kernel use
    424 * @slot: pointer to the &struct hotplug_slot to initialize
    425 * @bus: bus this slot is on
    426 * @devnr: slot number
    427 * @name: name registered with kobject core
    428 * @owner: caller module owner
    429 * @mod_name: caller module name
    430 *
    431 * Allocate and fill in a PCI slot for use by a hotplug driver.  Once this has
    432 * been called, the driver may invoke hotplug_slot_name() to get the slot's
    433 * unique name.  The driver must be prepared to handle a ->reset_slot callback
    434 * from this point on.
    435 *
    436 * Returns 0 on success or a negative int on error.
    437 */
    438int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
    439			int devnr, const char *name, struct module *owner,
    440			const char *mod_name)
    441{
    442	struct pci_slot *pci_slot;
    443
    444	if (slot == NULL)
    445		return -ENODEV;
    446	if (slot->ops == NULL)
    447		return -EINVAL;
    448
    449	slot->owner = owner;
    450	slot->mod_name = mod_name;
    451
    452	/*
    453	 * No problems if we call this interface from both ACPI_PCI_SLOT
    454	 * driver and call it here again. If we've already created the
    455	 * pci_slot, the interface will simply bump the refcount.
    456	 */
    457	pci_slot = pci_create_slot(bus, devnr, name, slot);
    458	if (IS_ERR(pci_slot))
    459		return PTR_ERR(pci_slot);
    460
    461	slot->pci_slot = pci_slot;
    462	pci_slot->hotplug = slot;
    463	return 0;
    464}
    465EXPORT_SYMBOL_GPL(__pci_hp_initialize);
    466
    467/**
    468 * pci_hp_add - publish hotplug slot to user space
    469 * @slot: pointer to the &struct hotplug_slot to publish
    470 *
    471 * Make a hotplug slot's sysfs interface available and inform user space of its
    472 * addition by sending a uevent.  The hotplug driver must be prepared to handle
    473 * all &struct hotplug_slot_ops callbacks from this point on.
    474 *
    475 * Returns 0 on success or a negative int on error.
    476 */
    477int pci_hp_add(struct hotplug_slot *slot)
    478{
    479	struct pci_slot *pci_slot = slot->pci_slot;
    480	int result;
    481
    482	result = fs_add_slot(pci_slot);
    483	if (result)
    484		return result;
    485
    486	kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
    487	mutex_lock(&pci_hp_mutex);
    488	list_add(&slot->slot_list, &pci_hotplug_slot_list);
    489	mutex_unlock(&pci_hp_mutex);
    490	dbg("Added slot %s to the list\n", hotplug_slot_name(slot));
    491	return 0;
    492}
    493EXPORT_SYMBOL_GPL(pci_hp_add);
    494
    495/**
    496 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
    497 * @slot: pointer to the &struct hotplug_slot to deregister
    498 *
    499 * The @slot must have been registered with the pci hotplug subsystem
    500 * previously with a call to pci_hp_register().
    501 *
    502 * Returns 0 if successful, anything else for an error.
    503 */
    504void pci_hp_deregister(struct hotplug_slot *slot)
    505{
    506	pci_hp_del(slot);
    507	pci_hp_destroy(slot);
    508}
    509EXPORT_SYMBOL_GPL(pci_hp_deregister);
    510
    511/**
    512 * pci_hp_del - unpublish hotplug slot from user space
    513 * @slot: pointer to the &struct hotplug_slot to unpublish
    514 *
    515 * Remove a hotplug slot's sysfs interface.
    516 *
    517 * Returns 0 on success or a negative int on error.
    518 */
    519void pci_hp_del(struct hotplug_slot *slot)
    520{
    521	struct hotplug_slot *temp;
    522
    523	if (WARN_ON(!slot))
    524		return;
    525
    526	mutex_lock(&pci_hp_mutex);
    527	temp = get_slot_from_name(hotplug_slot_name(slot));
    528	if (WARN_ON(temp != slot)) {
    529		mutex_unlock(&pci_hp_mutex);
    530		return;
    531	}
    532
    533	list_del(&slot->slot_list);
    534	mutex_unlock(&pci_hp_mutex);
    535	dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));
    536	fs_remove_slot(slot->pci_slot);
    537}
    538EXPORT_SYMBOL_GPL(pci_hp_del);
    539
    540/**
    541 * pci_hp_destroy - remove hotplug slot from in-kernel use
    542 * @slot: pointer to the &struct hotplug_slot to destroy
    543 *
    544 * Destroy a PCI slot used by a hotplug driver.  Once this has been called,
    545 * the driver may no longer invoke hotplug_slot_name() to get the slot's
    546 * unique name.  The driver no longer needs to handle a ->reset_slot callback
    547 * from this point on.
    548 *
    549 * Returns 0 on success or a negative int on error.
    550 */
    551void pci_hp_destroy(struct hotplug_slot *slot)
    552{
    553	struct pci_slot *pci_slot = slot->pci_slot;
    554
    555	slot->pci_slot = NULL;
    556	pci_slot->hotplug = NULL;
    557	pci_destroy_slot(pci_slot);
    558}
    559EXPORT_SYMBOL_GPL(pci_hp_destroy);
    560
    561static int __init pci_hotplug_init(void)
    562{
    563	int result;
    564
    565	result = cpci_hotplug_init(debug);
    566	if (result) {
    567		err("cpci_hotplug_init with error %d\n", result);
    568		return result;
    569	}
    570
    571	return result;
    572}
    573device_initcall(pci_hotplug_init);
    574
    575/*
    576 * not really modular, but the easiest way to keep compat with existing
    577 * bootargs behaviour is to continue using module_param here.
    578 */
    579module_param(debug, bool, 0644);
    580MODULE_PARM_DESC(debug, "Debugging mode enabled or not");