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

pciehp_core.c (9643B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * PCI Express 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 * Authors:
     15 *   Dan Zink <dan.zink@compaq.com>
     16 *   Greg Kroah-Hartman <greg@kroah.com>
     17 *   Dely Sy <dely.l.sy@intel.com>"
     18 */
     19
     20#define pr_fmt(fmt) "pciehp: " fmt
     21#define dev_fmt pr_fmt
     22
     23#include <linux/moduleparam.h>
     24#include <linux/kernel.h>
     25#include <linux/slab.h>
     26#include <linux/types.h>
     27#include <linux/pci.h>
     28#include "pciehp.h"
     29
     30#include "../pci.h"
     31
     32/* Global variables */
     33bool pciehp_poll_mode;
     34int pciehp_poll_time;
     35
     36/*
     37 * not really modular, but the easiest way to keep compat with existing
     38 * bootargs behaviour is to continue using module_param here.
     39 */
     40module_param(pciehp_poll_mode, bool, 0644);
     41module_param(pciehp_poll_time, int, 0644);
     42MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
     43MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
     44
     45static int set_attention_status(struct hotplug_slot *slot, u8 value);
     46static int get_power_status(struct hotplug_slot *slot, u8 *value);
     47static int get_latch_status(struct hotplug_slot *slot, u8 *value);
     48static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
     49
     50static int init_slot(struct controller *ctrl)
     51{
     52	struct hotplug_slot_ops *ops;
     53	char name[SLOT_NAME_SIZE];
     54	int retval;
     55
     56	/* Setup hotplug slot ops */
     57	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
     58	if (!ops)
     59		return -ENOMEM;
     60
     61	ops->enable_slot = pciehp_sysfs_enable_slot;
     62	ops->disable_slot = pciehp_sysfs_disable_slot;
     63	ops->get_power_status = get_power_status;
     64	ops->get_adapter_status = get_adapter_status;
     65	ops->reset_slot = pciehp_reset_slot;
     66	if (MRL_SENS(ctrl))
     67		ops->get_latch_status = get_latch_status;
     68	if (ATTN_LED(ctrl)) {
     69		ops->get_attention_status = pciehp_get_attention_status;
     70		ops->set_attention_status = set_attention_status;
     71	} else if (ctrl->pcie->port->hotplug_user_indicators) {
     72		ops->get_attention_status = pciehp_get_raw_indicator_status;
     73		ops->set_attention_status = pciehp_set_raw_indicator_status;
     74	}
     75
     76	/* register this slot with the hotplug pci core */
     77	ctrl->hotplug_slot.ops = ops;
     78	snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
     79
     80	retval = pci_hp_initialize(&ctrl->hotplug_slot,
     81				   ctrl->pcie->port->subordinate, 0, name);
     82	if (retval) {
     83		ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
     84		kfree(ops);
     85	}
     86	return retval;
     87}
     88
     89static void cleanup_slot(struct controller *ctrl)
     90{
     91	struct hotplug_slot *hotplug_slot = &ctrl->hotplug_slot;
     92
     93	pci_hp_destroy(hotplug_slot);
     94	kfree(hotplug_slot->ops);
     95}
     96
     97/*
     98 * set_attention_status - Turns the Attention Indicator on, off or blinking
     99 */
    100static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
    101{
    102	struct controller *ctrl = to_ctrl(hotplug_slot);
    103	struct pci_dev *pdev = ctrl->pcie->port;
    104
    105	if (status)
    106		status <<= PCI_EXP_SLTCTL_ATTN_IND_SHIFT;
    107	else
    108		status = PCI_EXP_SLTCTL_ATTN_IND_OFF;
    109
    110	pci_config_pm_runtime_get(pdev);
    111	pciehp_set_indicators(ctrl, INDICATOR_NOOP, status);
    112	pci_config_pm_runtime_put(pdev);
    113	return 0;
    114}
    115
    116static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
    117{
    118	struct controller *ctrl = to_ctrl(hotplug_slot);
    119	struct pci_dev *pdev = ctrl->pcie->port;
    120
    121	pci_config_pm_runtime_get(pdev);
    122	pciehp_get_power_status(ctrl, value);
    123	pci_config_pm_runtime_put(pdev);
    124	return 0;
    125}
    126
    127static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
    128{
    129	struct controller *ctrl = to_ctrl(hotplug_slot);
    130	struct pci_dev *pdev = ctrl->pcie->port;
    131
    132	pci_config_pm_runtime_get(pdev);
    133	pciehp_get_latch_status(ctrl, value);
    134	pci_config_pm_runtime_put(pdev);
    135	return 0;
    136}
    137
    138static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
    139{
    140	struct controller *ctrl = to_ctrl(hotplug_slot);
    141	struct pci_dev *pdev = ctrl->pcie->port;
    142	int ret;
    143
    144	pci_config_pm_runtime_get(pdev);
    145	ret = pciehp_card_present_or_link_active(ctrl);
    146	pci_config_pm_runtime_put(pdev);
    147	if (ret < 0)
    148		return ret;
    149
    150	*value = ret;
    151	return 0;
    152}
    153
    154/**
    155 * pciehp_check_presence() - synthesize event if presence has changed
    156 * @ctrl: controller to check
    157 *
    158 * On probe and resume, an explicit presence check is necessary to bring up an
    159 * occupied slot or bring down an unoccupied slot.  This can't be triggered by
    160 * events in the Slot Status register, they may be stale and are therefore
    161 * cleared.  Secondly, sending an interrupt for "events that occur while
    162 * interrupt generation is disabled [when] interrupt generation is subsequently
    163 * enabled" is optional per PCIe r4.0, sec 6.7.3.4.
    164 */
    165static void pciehp_check_presence(struct controller *ctrl)
    166{
    167	int occupied;
    168
    169	down_read_nested(&ctrl->reset_lock, ctrl->depth);
    170	mutex_lock(&ctrl->state_lock);
    171
    172	occupied = pciehp_card_present_or_link_active(ctrl);
    173	if ((occupied > 0 && (ctrl->state == OFF_STATE ||
    174			  ctrl->state == BLINKINGON_STATE)) ||
    175	    (!occupied && (ctrl->state == ON_STATE ||
    176			   ctrl->state == BLINKINGOFF_STATE)))
    177		pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
    178
    179	mutex_unlock(&ctrl->state_lock);
    180	up_read(&ctrl->reset_lock);
    181}
    182
    183static int pciehp_probe(struct pcie_device *dev)
    184{
    185	int rc;
    186	struct controller *ctrl;
    187
    188	/* If this is not a "hotplug" service, we have no business here. */
    189	if (dev->service != PCIE_PORT_SERVICE_HP)
    190		return -ENODEV;
    191
    192	if (!dev->port->subordinate) {
    193		/* Can happen if we run out of bus numbers during probe */
    194		pci_err(dev->port,
    195			"Hotplug bridge without secondary bus, ignoring\n");
    196		return -ENODEV;
    197	}
    198
    199	ctrl = pcie_init(dev);
    200	if (!ctrl) {
    201		pci_err(dev->port, "Controller initialization failed\n");
    202		return -ENODEV;
    203	}
    204	set_service_data(dev, ctrl);
    205
    206	/* Setup the slot information structures */
    207	rc = init_slot(ctrl);
    208	if (rc) {
    209		if (rc == -EBUSY)
    210			ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
    211		else
    212			ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc);
    213		goto err_out_release_ctlr;
    214	}
    215
    216	/* Enable events after we have setup the data structures */
    217	rc = pcie_init_notification(ctrl);
    218	if (rc) {
    219		ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc);
    220		goto err_out_free_ctrl_slot;
    221	}
    222
    223	/* Publish to user space */
    224	rc = pci_hp_add(&ctrl->hotplug_slot);
    225	if (rc) {
    226		ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);
    227		goto err_out_shutdown_notification;
    228	}
    229
    230	pciehp_check_presence(ctrl);
    231
    232	return 0;
    233
    234err_out_shutdown_notification:
    235	pcie_shutdown_notification(ctrl);
    236err_out_free_ctrl_slot:
    237	cleanup_slot(ctrl);
    238err_out_release_ctlr:
    239	pciehp_release_ctrl(ctrl);
    240	return -ENODEV;
    241}
    242
    243static void pciehp_remove(struct pcie_device *dev)
    244{
    245	struct controller *ctrl = get_service_data(dev);
    246
    247	pci_hp_del(&ctrl->hotplug_slot);
    248	pcie_shutdown_notification(ctrl);
    249	cleanup_slot(ctrl);
    250	pciehp_release_ctrl(ctrl);
    251}
    252
    253#ifdef CONFIG_PM
    254static bool pme_is_native(struct pcie_device *dev)
    255{
    256	const struct pci_host_bridge *host;
    257
    258	host = pci_find_host_bridge(dev->port->bus);
    259	return pcie_ports_native || host->native_pme;
    260}
    261
    262static void pciehp_disable_interrupt(struct pcie_device *dev)
    263{
    264	/*
    265	 * Disable hotplug interrupt so that it does not trigger
    266	 * immediately when the downstream link goes down.
    267	 */
    268	if (pme_is_native(dev))
    269		pcie_disable_interrupt(get_service_data(dev));
    270}
    271
    272#ifdef CONFIG_PM_SLEEP
    273static int pciehp_suspend(struct pcie_device *dev)
    274{
    275	/*
    276	 * If the port is already runtime suspended we can keep it that
    277	 * way.
    278	 */
    279	if (dev_pm_skip_suspend(&dev->port->dev))
    280		return 0;
    281
    282	pciehp_disable_interrupt(dev);
    283	return 0;
    284}
    285
    286static int pciehp_resume_noirq(struct pcie_device *dev)
    287{
    288	struct controller *ctrl = get_service_data(dev);
    289
    290	/* pci_restore_state() just wrote to the Slot Control register */
    291	ctrl->cmd_started = jiffies;
    292	ctrl->cmd_busy = true;
    293
    294	/* clear spurious events from rediscovery of inserted card */
    295	if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE)
    296		pcie_clear_hotplug_events(ctrl);
    297
    298	return 0;
    299}
    300#endif
    301
    302static int pciehp_resume(struct pcie_device *dev)
    303{
    304	struct controller *ctrl = get_service_data(dev);
    305
    306	if (pme_is_native(dev))
    307		pcie_enable_interrupt(ctrl);
    308
    309	pciehp_check_presence(ctrl);
    310
    311	return 0;
    312}
    313
    314static int pciehp_runtime_suspend(struct pcie_device *dev)
    315{
    316	pciehp_disable_interrupt(dev);
    317	return 0;
    318}
    319
    320static int pciehp_runtime_resume(struct pcie_device *dev)
    321{
    322	struct controller *ctrl = get_service_data(dev);
    323
    324	/* pci_restore_state() just wrote to the Slot Control register */
    325	ctrl->cmd_started = jiffies;
    326	ctrl->cmd_busy = true;
    327
    328	/* clear spurious events from rediscovery of inserted card */
    329	if ((ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) &&
    330	     pme_is_native(dev))
    331		pcie_clear_hotplug_events(ctrl);
    332
    333	return pciehp_resume(dev);
    334}
    335#endif /* PM */
    336
    337static struct pcie_port_service_driver hpdriver_portdrv = {
    338	.name		= "pciehp",
    339	.port_type	= PCIE_ANY_PORT,
    340	.service	= PCIE_PORT_SERVICE_HP,
    341
    342	.probe		= pciehp_probe,
    343	.remove		= pciehp_remove,
    344
    345#ifdef	CONFIG_PM
    346#ifdef	CONFIG_PM_SLEEP
    347	.suspend	= pciehp_suspend,
    348	.resume_noirq	= pciehp_resume_noirq,
    349	.resume		= pciehp_resume,
    350#endif
    351	.runtime_suspend = pciehp_runtime_suspend,
    352	.runtime_resume	= pciehp_runtime_resume,
    353#endif	/* PM */
    354
    355	.slot_reset	= pciehp_slot_reset,
    356};
    357
    358int __init pcie_hp_init(void)
    359{
    360	int retval = 0;
    361
    362	retval = pcie_port_service_register(&hpdriver_portdrv);
    363	pr_debug("pcie_port_service_register = %d\n", retval);
    364	if (retval)
    365		pr_debug("Failure to register service\n");
    366
    367	return retval;
    368}