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_ctrl.c (10839B)


      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 */
     15
     16#define dev_fmt(fmt) "pciehp: " fmt
     17
     18#include <linux/kernel.h>
     19#include <linux/types.h>
     20#include <linux/pm_runtime.h>
     21#include <linux/pci.h>
     22#include "pciehp.h"
     23
     24/* The following routines constitute the bulk of the
     25   hotplug controller logic
     26 */
     27
     28#define SAFE_REMOVAL	 true
     29#define SURPRISE_REMOVAL false
     30
     31static void set_slot_off(struct controller *ctrl)
     32{
     33	/*
     34	 * Turn off slot, turn on attention indicator, turn off power
     35	 * indicator
     36	 */
     37	if (POWER_CTRL(ctrl)) {
     38		pciehp_power_off_slot(ctrl);
     39
     40		/*
     41		 * After turning power off, we must wait for at least 1 second
     42		 * before taking any action that relies on power having been
     43		 * removed from the slot/adapter.
     44		 */
     45		msleep(1000);
     46	}
     47
     48	pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
     49			      PCI_EXP_SLTCTL_ATTN_IND_ON);
     50}
     51
     52/**
     53 * board_added - Called after a board has been added to the system.
     54 * @ctrl: PCIe hotplug controller where board is added
     55 *
     56 * Turns power on for the board.
     57 * Configures board.
     58 */
     59static int board_added(struct controller *ctrl)
     60{
     61	int retval = 0;
     62	struct pci_bus *parent = ctrl->pcie->port->subordinate;
     63
     64	if (POWER_CTRL(ctrl)) {
     65		/* Power on slot */
     66		retval = pciehp_power_on_slot(ctrl);
     67		if (retval)
     68			return retval;
     69	}
     70
     71	pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
     72			      INDICATOR_NOOP);
     73
     74	/* Check link training status */
     75	retval = pciehp_check_link_status(ctrl);
     76	if (retval)
     77		goto err_exit;
     78
     79	/* Check for a power fault */
     80	if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) {
     81		ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
     82		retval = -EIO;
     83		goto err_exit;
     84	}
     85
     86	retval = pciehp_configure_device(ctrl);
     87	if (retval) {
     88		if (retval != -EEXIST) {
     89			ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
     90				 pci_domain_nr(parent), parent->number);
     91			goto err_exit;
     92		}
     93	}
     94
     95	pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
     96			      PCI_EXP_SLTCTL_ATTN_IND_OFF);
     97	return 0;
     98
     99err_exit:
    100	set_slot_off(ctrl);
    101	return retval;
    102}
    103
    104/**
    105 * remove_board - Turn off slot and Power Indicator
    106 * @ctrl: PCIe hotplug controller where board is being removed
    107 * @safe_removal: whether the board is safely removed (versus surprise removed)
    108 */
    109static void remove_board(struct controller *ctrl, bool safe_removal)
    110{
    111	pciehp_unconfigure_device(ctrl, safe_removal);
    112
    113	if (POWER_CTRL(ctrl)) {
    114		pciehp_power_off_slot(ctrl);
    115
    116		/*
    117		 * After turning power off, we must wait for at least 1 second
    118		 * before taking any action that relies on power having been
    119		 * removed from the slot/adapter.
    120		 */
    121		msleep(1000);
    122
    123		/* Ignore link or presence changes caused by power off */
    124		atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
    125			   &ctrl->pending_events);
    126	}
    127
    128	pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
    129			      INDICATOR_NOOP);
    130}
    131
    132static int pciehp_enable_slot(struct controller *ctrl);
    133static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal);
    134
    135void pciehp_request(struct controller *ctrl, int action)
    136{
    137	atomic_or(action, &ctrl->pending_events);
    138	if (!pciehp_poll_mode)
    139		irq_wake_thread(ctrl->pcie->irq, ctrl);
    140}
    141
    142void pciehp_queue_pushbutton_work(struct work_struct *work)
    143{
    144	struct controller *ctrl = container_of(work, struct controller,
    145					       button_work.work);
    146
    147	mutex_lock(&ctrl->state_lock);
    148	switch (ctrl->state) {
    149	case BLINKINGOFF_STATE:
    150		pciehp_request(ctrl, DISABLE_SLOT);
    151		break;
    152	case BLINKINGON_STATE:
    153		pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
    154		break;
    155	default:
    156		break;
    157	}
    158	mutex_unlock(&ctrl->state_lock);
    159}
    160
    161void pciehp_handle_button_press(struct controller *ctrl)
    162{
    163	mutex_lock(&ctrl->state_lock);
    164	switch (ctrl->state) {
    165	case OFF_STATE:
    166	case ON_STATE:
    167		if (ctrl->state == ON_STATE) {
    168			ctrl->state = BLINKINGOFF_STATE;
    169			ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
    170				  slot_name(ctrl));
    171		} else {
    172			ctrl->state = BLINKINGON_STATE;
    173			ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
    174				  slot_name(ctrl));
    175		}
    176		/* blink power indicator and turn off attention */
    177		pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
    178				      PCI_EXP_SLTCTL_ATTN_IND_OFF);
    179		schedule_delayed_work(&ctrl->button_work, 5 * HZ);
    180		break;
    181	case BLINKINGOFF_STATE:
    182	case BLINKINGON_STATE:
    183		/*
    184		 * Cancel if we are still blinking; this means that we
    185		 * press the attention again before the 5 sec. limit
    186		 * expires to cancel hot-add or hot-remove
    187		 */
    188		ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl));
    189		cancel_delayed_work(&ctrl->button_work);
    190		if (ctrl->state == BLINKINGOFF_STATE) {
    191			ctrl->state = ON_STATE;
    192			pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
    193					      PCI_EXP_SLTCTL_ATTN_IND_OFF);
    194		} else {
    195			ctrl->state = OFF_STATE;
    196			pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
    197					      PCI_EXP_SLTCTL_ATTN_IND_OFF);
    198		}
    199		ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
    200			  slot_name(ctrl));
    201		break;
    202	default:
    203		ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
    204			 slot_name(ctrl), ctrl->state);
    205		break;
    206	}
    207	mutex_unlock(&ctrl->state_lock);
    208}
    209
    210void pciehp_handle_disable_request(struct controller *ctrl)
    211{
    212	mutex_lock(&ctrl->state_lock);
    213	switch (ctrl->state) {
    214	case BLINKINGON_STATE:
    215	case BLINKINGOFF_STATE:
    216		cancel_delayed_work(&ctrl->button_work);
    217		break;
    218	}
    219	ctrl->state = POWEROFF_STATE;
    220	mutex_unlock(&ctrl->state_lock);
    221
    222	ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL);
    223}
    224
    225void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
    226{
    227	int present, link_active;
    228
    229	/*
    230	 * If the slot is on and presence or link has changed, turn it off.
    231	 * Even if it's occupied again, we cannot assume the card is the same.
    232	 */
    233	mutex_lock(&ctrl->state_lock);
    234	switch (ctrl->state) {
    235	case BLINKINGOFF_STATE:
    236		cancel_delayed_work(&ctrl->button_work);
    237		fallthrough;
    238	case ON_STATE:
    239		ctrl->state = POWEROFF_STATE;
    240		mutex_unlock(&ctrl->state_lock);
    241		if (events & PCI_EXP_SLTSTA_DLLSC)
    242			ctrl_info(ctrl, "Slot(%s): Link Down\n",
    243				  slot_name(ctrl));
    244		if (events & PCI_EXP_SLTSTA_PDC)
    245			ctrl_info(ctrl, "Slot(%s): Card not present\n",
    246				  slot_name(ctrl));
    247		pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
    248		break;
    249	default:
    250		mutex_unlock(&ctrl->state_lock);
    251		break;
    252	}
    253
    254	/* Turn the slot on if it's occupied or link is up */
    255	mutex_lock(&ctrl->state_lock);
    256	present = pciehp_card_present(ctrl);
    257	link_active = pciehp_check_link_active(ctrl);
    258	if (present <= 0 && link_active <= 0) {
    259		mutex_unlock(&ctrl->state_lock);
    260		return;
    261	}
    262
    263	switch (ctrl->state) {
    264	case BLINKINGON_STATE:
    265		cancel_delayed_work(&ctrl->button_work);
    266		fallthrough;
    267	case OFF_STATE:
    268		ctrl->state = POWERON_STATE;
    269		mutex_unlock(&ctrl->state_lock);
    270		if (present)
    271			ctrl_info(ctrl, "Slot(%s): Card present\n",
    272				  slot_name(ctrl));
    273		if (link_active)
    274			ctrl_info(ctrl, "Slot(%s): Link Up\n",
    275				  slot_name(ctrl));
    276		ctrl->request_result = pciehp_enable_slot(ctrl);
    277		break;
    278	default:
    279		mutex_unlock(&ctrl->state_lock);
    280		break;
    281	}
    282}
    283
    284static int __pciehp_enable_slot(struct controller *ctrl)
    285{
    286	u8 getstatus = 0;
    287
    288	if (MRL_SENS(ctrl)) {
    289		pciehp_get_latch_status(ctrl, &getstatus);
    290		if (getstatus) {
    291			ctrl_info(ctrl, "Slot(%s): Latch open\n",
    292				  slot_name(ctrl));
    293			return -ENODEV;
    294		}
    295	}
    296
    297	if (POWER_CTRL(ctrl)) {
    298		pciehp_get_power_status(ctrl, &getstatus);
    299		if (getstatus) {
    300			ctrl_info(ctrl, "Slot(%s): Already enabled\n",
    301				  slot_name(ctrl));
    302			return 0;
    303		}
    304	}
    305
    306	return board_added(ctrl);
    307}
    308
    309static int pciehp_enable_slot(struct controller *ctrl)
    310{
    311	int ret;
    312
    313	pm_runtime_get_sync(&ctrl->pcie->port->dev);
    314	ret = __pciehp_enable_slot(ctrl);
    315	if (ret && ATTN_BUTTN(ctrl))
    316		/* may be blinking */
    317		pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
    318				      INDICATOR_NOOP);
    319	pm_runtime_put(&ctrl->pcie->port->dev);
    320
    321	mutex_lock(&ctrl->state_lock);
    322	ctrl->state = ret ? OFF_STATE : ON_STATE;
    323	mutex_unlock(&ctrl->state_lock);
    324
    325	return ret;
    326}
    327
    328static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
    329{
    330	u8 getstatus = 0;
    331
    332	if (POWER_CTRL(ctrl)) {
    333		pciehp_get_power_status(ctrl, &getstatus);
    334		if (!getstatus) {
    335			ctrl_info(ctrl, "Slot(%s): Already disabled\n",
    336				  slot_name(ctrl));
    337			return -EINVAL;
    338		}
    339	}
    340
    341	remove_board(ctrl, safe_removal);
    342	return 0;
    343}
    344
    345static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
    346{
    347	int ret;
    348
    349	pm_runtime_get_sync(&ctrl->pcie->port->dev);
    350	ret = __pciehp_disable_slot(ctrl, safe_removal);
    351	pm_runtime_put(&ctrl->pcie->port->dev);
    352
    353	mutex_lock(&ctrl->state_lock);
    354	ctrl->state = OFF_STATE;
    355	mutex_unlock(&ctrl->state_lock);
    356
    357	return ret;
    358}
    359
    360int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
    361{
    362	struct controller *ctrl = to_ctrl(hotplug_slot);
    363
    364	mutex_lock(&ctrl->state_lock);
    365	switch (ctrl->state) {
    366	case BLINKINGON_STATE:
    367	case OFF_STATE:
    368		mutex_unlock(&ctrl->state_lock);
    369		/*
    370		 * The IRQ thread becomes a no-op if the user pulls out the
    371		 * card before the thread wakes up, so initialize to -ENODEV.
    372		 */
    373		ctrl->request_result = -ENODEV;
    374		pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
    375		wait_event(ctrl->requester,
    376			   !atomic_read(&ctrl->pending_events) &&
    377			   !ctrl->ist_running);
    378		return ctrl->request_result;
    379	case POWERON_STATE:
    380		ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
    381			  slot_name(ctrl));
    382		break;
    383	case BLINKINGOFF_STATE:
    384	case ON_STATE:
    385	case POWEROFF_STATE:
    386		ctrl_info(ctrl, "Slot(%s): Already enabled\n",
    387			  slot_name(ctrl));
    388		break;
    389	default:
    390		ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
    391			 slot_name(ctrl), ctrl->state);
    392		break;
    393	}
    394	mutex_unlock(&ctrl->state_lock);
    395
    396	return -ENODEV;
    397}
    398
    399int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
    400{
    401	struct controller *ctrl = to_ctrl(hotplug_slot);
    402
    403	mutex_lock(&ctrl->state_lock);
    404	switch (ctrl->state) {
    405	case BLINKINGOFF_STATE:
    406	case ON_STATE:
    407		mutex_unlock(&ctrl->state_lock);
    408		pciehp_request(ctrl, DISABLE_SLOT);
    409		wait_event(ctrl->requester,
    410			   !atomic_read(&ctrl->pending_events) &&
    411			   !ctrl->ist_running);
    412		return ctrl->request_result;
    413	case POWEROFF_STATE:
    414		ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
    415			  slot_name(ctrl));
    416		break;
    417	case BLINKINGON_STATE:
    418	case OFF_STATE:
    419	case POWERON_STATE:
    420		ctrl_info(ctrl, "Slot(%s): Already disabled\n",
    421			  slot_name(ctrl));
    422		break;
    423	default:
    424		ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
    425			 slot_name(ctrl), ctrl->state);
    426		break;
    427	}
    428	mutex_unlock(&ctrl->state_lock);
    429
    430	return -ENODEV;
    431}