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

shpchp_core.c (8701B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Standard Hot Plug Controller Driver
      4 *
      5 * Copyright (C) 1995,2001 Compaq Computer Corporation
      6 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
      7 * Copyright (C) 2001 IBM Corp.
      8 * Copyright (C) 2003-2004 Intel Corporation
      9 *
     10 * All rights reserved.
     11 *
     12 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
     13 *
     14 */
     15
     16#include <linux/module.h>
     17#include <linux/moduleparam.h>
     18#include <linux/kernel.h>
     19#include <linux/types.h>
     20#include <linux/slab.h>
     21#include <linux/pci.h>
     22#include "shpchp.h"
     23
     24/* Global variables */
     25bool shpchp_debug;
     26bool shpchp_poll_mode;
     27int shpchp_poll_time;
     28
     29#define DRIVER_VERSION	"0.4"
     30#define DRIVER_AUTHOR	"Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
     31#define DRIVER_DESC	"Standard Hot Plug PCI Controller Driver"
     32
     33MODULE_AUTHOR(DRIVER_AUTHOR);
     34MODULE_DESCRIPTION(DRIVER_DESC);
     35MODULE_LICENSE("GPL");
     36
     37module_param(shpchp_debug, bool, 0644);
     38module_param(shpchp_poll_mode, bool, 0644);
     39module_param(shpchp_poll_time, int, 0644);
     40MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
     41MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
     42MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
     43
     44#define SHPC_MODULE_NAME "shpchp"
     45
     46static int set_attention_status(struct hotplug_slot *slot, u8 value);
     47static int enable_slot(struct hotplug_slot *slot);
     48static int disable_slot(struct hotplug_slot *slot);
     49static int get_power_status(struct hotplug_slot *slot, u8 *value);
     50static int get_attention_status(struct hotplug_slot *slot, u8 *value);
     51static int get_latch_status(struct hotplug_slot *slot, u8 *value);
     52static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
     53
     54static const struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
     55	.set_attention_status =	set_attention_status,
     56	.enable_slot =		enable_slot,
     57	.disable_slot =		disable_slot,
     58	.get_power_status =	get_power_status,
     59	.get_attention_status =	get_attention_status,
     60	.get_latch_status =	get_latch_status,
     61	.get_adapter_status =	get_adapter_status,
     62};
     63
     64static int init_slots(struct controller *ctrl)
     65{
     66	struct slot *slot;
     67	struct hotplug_slot *hotplug_slot;
     68	char name[SLOT_NAME_SIZE];
     69	int retval;
     70	int i;
     71
     72	for (i = 0; i < ctrl->num_slots; i++) {
     73		slot = kzalloc(sizeof(*slot), GFP_KERNEL);
     74		if (!slot) {
     75			retval = -ENOMEM;
     76			goto error;
     77		}
     78
     79		hotplug_slot = &slot->hotplug_slot;
     80
     81		slot->hp_slot = i;
     82		slot->ctrl = ctrl;
     83		slot->bus = ctrl->pci_dev->subordinate->number;
     84		slot->device = ctrl->slot_device_offset + i;
     85		slot->hpc_ops = ctrl->hpc_ops;
     86		slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
     87
     88		slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
     89		if (!slot->wq) {
     90			retval = -ENOMEM;
     91			goto error_slot;
     92		}
     93
     94		mutex_init(&slot->lock);
     95		INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
     96
     97		/* register this slot with the hotplug pci core */
     98		snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
     99		hotplug_slot->ops = &shpchp_hotplug_slot_ops;
    100
    101		ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x hp_slot=%x sun=%x slot_device_offset=%x\n",
    102			 pci_domain_nr(ctrl->pci_dev->subordinate),
    103			 slot->bus, slot->device, slot->hp_slot, slot->number,
    104			 ctrl->slot_device_offset);
    105		retval = pci_hp_register(hotplug_slot,
    106				ctrl->pci_dev->subordinate, slot->device, name);
    107		if (retval) {
    108			ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
    109				 retval);
    110			goto error_slotwq;
    111		}
    112
    113		get_power_status(hotplug_slot, &slot->pwr_save);
    114		get_attention_status(hotplug_slot, &slot->attention_save);
    115		get_latch_status(hotplug_slot, &slot->latch_save);
    116		get_adapter_status(hotplug_slot, &slot->presence_save);
    117
    118		list_add(&slot->slot_list, &ctrl->slot_list);
    119	}
    120
    121	return 0;
    122error_slotwq:
    123	destroy_workqueue(slot->wq);
    124error_slot:
    125	kfree(slot);
    126error:
    127	return retval;
    128}
    129
    130void cleanup_slots(struct controller *ctrl)
    131{
    132	struct slot *slot, *next;
    133
    134	list_for_each_entry_safe(slot, next, &ctrl->slot_list, slot_list) {
    135		list_del(&slot->slot_list);
    136		cancel_delayed_work(&slot->work);
    137		destroy_workqueue(slot->wq);
    138		pci_hp_deregister(&slot->hotplug_slot);
    139		kfree(slot);
    140	}
    141}
    142
    143/*
    144 * set_attention_status - Turns the Amber LED for a slot on, off or blink
    145 */
    146static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
    147{
    148	struct slot *slot = get_slot(hotplug_slot);
    149
    150	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
    151		 __func__, slot_name(slot));
    152
    153	slot->attention_save = status;
    154	slot->hpc_ops->set_attention_status(slot, status);
    155
    156	return 0;
    157}
    158
    159static int enable_slot(struct hotplug_slot *hotplug_slot)
    160{
    161	struct slot *slot = get_slot(hotplug_slot);
    162
    163	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
    164		 __func__, slot_name(slot));
    165
    166	return shpchp_sysfs_enable_slot(slot);
    167}
    168
    169static int disable_slot(struct hotplug_slot *hotplug_slot)
    170{
    171	struct slot *slot = get_slot(hotplug_slot);
    172
    173	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
    174		 __func__, slot_name(slot));
    175
    176	return shpchp_sysfs_disable_slot(slot);
    177}
    178
    179static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
    180{
    181	struct slot *slot = get_slot(hotplug_slot);
    182	int retval;
    183
    184	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
    185		 __func__, slot_name(slot));
    186
    187	retval = slot->hpc_ops->get_power_status(slot, value);
    188	if (retval < 0)
    189		*value = slot->pwr_save;
    190
    191	return 0;
    192}
    193
    194static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
    195{
    196	struct slot *slot = get_slot(hotplug_slot);
    197	int retval;
    198
    199	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
    200		 __func__, slot_name(slot));
    201
    202	retval = slot->hpc_ops->get_attention_status(slot, value);
    203	if (retval < 0)
    204		*value = slot->attention_save;
    205
    206	return 0;
    207}
    208
    209static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
    210{
    211	struct slot *slot = get_slot(hotplug_slot);
    212	int retval;
    213
    214	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
    215		 __func__, slot_name(slot));
    216
    217	retval = slot->hpc_ops->get_latch_status(slot, value);
    218	if (retval < 0)
    219		*value = slot->latch_save;
    220
    221	return 0;
    222}
    223
    224static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
    225{
    226	struct slot *slot = get_slot(hotplug_slot);
    227	int retval;
    228
    229	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
    230		 __func__, slot_name(slot));
    231
    232	retval = slot->hpc_ops->get_adapter_status(slot, value);
    233	if (retval < 0)
    234		*value = slot->presence_save;
    235
    236	return 0;
    237}
    238
    239static bool shpc_capable(struct pci_dev *bridge)
    240{
    241	/*
    242	 * It is assumed that AMD GOLAM chips support SHPC but they do not
    243	 * have SHPC capability.
    244	 */
    245	if (bridge->vendor == PCI_VENDOR_ID_AMD &&
    246	    bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
    247		return true;
    248
    249	if (pci_find_capability(bridge, PCI_CAP_ID_SHPC))
    250		return true;
    251
    252	return false;
    253}
    254
    255static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
    256{
    257	int rc;
    258	struct controller *ctrl;
    259
    260	if (!shpc_capable(pdev))
    261		return -ENODEV;
    262
    263	if (acpi_get_hp_hw_control_from_firmware(pdev))
    264		return -ENODEV;
    265
    266	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
    267	if (!ctrl)
    268		goto err_out_none;
    269
    270	INIT_LIST_HEAD(&ctrl->slot_list);
    271
    272	rc = shpc_init(ctrl, pdev);
    273	if (rc) {
    274		ctrl_dbg(ctrl, "Controller initialization failed\n");
    275		goto err_out_free_ctrl;
    276	}
    277
    278	pci_set_drvdata(pdev, ctrl);
    279
    280	/* Setup the slot information structures */
    281	rc = init_slots(ctrl);
    282	if (rc) {
    283		ctrl_err(ctrl, "Slot initialization failed\n");
    284		goto err_out_release_ctlr;
    285	}
    286
    287	rc = shpchp_create_ctrl_files(ctrl);
    288	if (rc)
    289		goto err_cleanup_slots;
    290
    291	pdev->shpc_managed = 1;
    292	return 0;
    293
    294err_cleanup_slots:
    295	cleanup_slots(ctrl);
    296err_out_release_ctlr:
    297	ctrl->hpc_ops->release_ctlr(ctrl);
    298err_out_free_ctrl:
    299	kfree(ctrl);
    300err_out_none:
    301	return -ENODEV;
    302}
    303
    304static void shpc_remove(struct pci_dev *dev)
    305{
    306	struct controller *ctrl = pci_get_drvdata(dev);
    307
    308	dev->shpc_managed = 0;
    309	shpchp_remove_ctrl_files(ctrl);
    310	ctrl->hpc_ops->release_ctlr(ctrl);
    311	kfree(ctrl);
    312}
    313
    314static const struct pci_device_id shpcd_pci_tbl[] = {
    315	{PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_NORMAL, ~0)},
    316	{ /* end: all zeroes */ }
    317};
    318MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
    319
    320static struct pci_driver shpc_driver = {
    321	.name =		SHPC_MODULE_NAME,
    322	.id_table =	shpcd_pci_tbl,
    323	.probe =	shpc_probe,
    324	.remove =	shpc_remove,
    325};
    326
    327static int __init shpcd_init(void)
    328{
    329	int retval;
    330
    331	retval = pci_register_driver(&shpc_driver);
    332	dbg("%s: pci_register_driver = %d\n", __func__, retval);
    333	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
    334
    335	return retval;
    336}
    337
    338static void __exit shpcd_cleanup(void)
    339{
    340	dbg("unload_shpchpd()\n");
    341	pci_unregister_driver(&shpc_driver);
    342	info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
    343}
    344
    345module_init(shpcd_init);
    346module_exit(shpcd_cleanup);