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

hwpci.c (11906B)


      1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
      2/*******************************************************************************
      3 *
      4 * Module Name: hwpci - Obtain PCI bus, device, and function numbers
      5 *
      6 ******************************************************************************/
      7
      8#include <acpi/acpi.h>
      9#include "accommon.h"
     10
     11#define _COMPONENT          ACPI_NAMESPACE
     12ACPI_MODULE_NAME("hwpci")
     13
     14/* PCI configuration space values */
     15#define PCI_CFG_HEADER_TYPE_REG             0x0E
     16#define PCI_CFG_PRIMARY_BUS_NUMBER_REG      0x18
     17#define PCI_CFG_SECONDARY_BUS_NUMBER_REG    0x19
     18/* PCI header values */
     19#define PCI_HEADER_TYPE_MASK                0x7F
     20#define PCI_TYPE_BRIDGE                     0x01
     21#define PCI_TYPE_CARDBUS_BRIDGE             0x02
     22typedef struct acpi_pci_device {
     23	acpi_handle device;
     24	struct acpi_pci_device *next;
     25
     26} acpi_pci_device;
     27
     28/* Local prototypes */
     29
     30static acpi_status
     31acpi_hw_build_pci_list(acpi_handle root_pci_device,
     32		       acpi_handle pci_region,
     33		       struct acpi_pci_device **return_list_head);
     34
     35static acpi_status
     36acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
     37			 struct acpi_pci_device *list_head);
     38
     39static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head);
     40
     41static acpi_status
     42acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
     43			    acpi_handle pci_device,
     44			    u16 *bus_number, u8 *is_bridge);
     45
     46/*******************************************************************************
     47 *
     48 * FUNCTION:    acpi_hw_derive_pci_id
     49 *
     50 * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be
     51 *                                    modified by this function.
     52 *              root_pci_device     - A handle to a PCI device object. This
     53 *                                    object must be a PCI Root Bridge having a
     54 *                                    _HID value of either PNP0A03 or PNP0A08
     55 *              pci_region          - A handle to a PCI configuration space
     56 *                                    Operation Region being initialized
     57 *
     58 * RETURN:      Status
     59 *
     60 * DESCRIPTION: This function derives a full PCI ID for a PCI device,
     61 *              consisting of a Segment number, Bus number, Device number,
     62 *              and function code.
     63 *
     64 *              The PCI hardware dynamically configures PCI bus numbers
     65 *              depending on the bus topology discovered during system
     66 *              initialization. This function is invoked during configuration
     67 *              of a PCI_Config Operation Region in order to (possibly) update
     68 *              the Bus/Device/Function numbers in the pci_id with the actual
     69 *              values as determined by the hardware and operating system
     70 *              configuration.
     71 *
     72 *              The pci_id parameter is initially populated during the Operation
     73 *              Region initialization. This function is then called, and is
     74 *              will make any necessary modifications to the Bus, Device, or
     75 *              Function number PCI ID subfields as appropriate for the
     76 *              current hardware and OS configuration.
     77 *
     78 * NOTE:        Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id
     79 *              interface since this feature is OS-independent. This module
     80 *              specifically avoids any use of recursion by building a local
     81 *              temporary device list.
     82 *
     83 ******************************************************************************/
     84
     85acpi_status
     86acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
     87		      acpi_handle root_pci_device, acpi_handle pci_region)
     88{
     89	acpi_status status;
     90	struct acpi_pci_device *list_head;
     91
     92	ACPI_FUNCTION_TRACE(hw_derive_pci_id);
     93
     94	if (!pci_id) {
     95		return_ACPI_STATUS(AE_BAD_PARAMETER);
     96	}
     97
     98	/* Build a list of PCI devices, from pci_region up to root_pci_device */
     99
    100	status =
    101	    acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head);
    102	if (ACPI_SUCCESS(status)) {
    103
    104		/* Walk the list, updating the PCI device/function/bus numbers */
    105
    106		status = acpi_hw_process_pci_list(pci_id, list_head);
    107
    108		/* Delete the list */
    109
    110		acpi_hw_delete_pci_list(list_head);
    111	}
    112
    113	return_ACPI_STATUS(status);
    114}
    115
    116/*******************************************************************************
    117 *
    118 * FUNCTION:    acpi_hw_build_pci_list
    119 *
    120 * PARAMETERS:  root_pci_device     - A handle to a PCI device object. This
    121 *                                    object is guaranteed to be a PCI Root
    122 *                                    Bridge having a _HID value of either
    123 *                                    PNP0A03 or PNP0A08
    124 *              pci_region          - A handle to the PCI configuration space
    125 *                                    Operation Region
    126 *              return_list_head    - Where the PCI device list is returned
    127 *
    128 * RETURN:      Status
    129 *
    130 * DESCRIPTION: Builds a list of devices from the input PCI region up to the
    131 *              Root PCI device for this namespace subtree.
    132 *
    133 ******************************************************************************/
    134
    135static acpi_status
    136acpi_hw_build_pci_list(acpi_handle root_pci_device,
    137		       acpi_handle pci_region,
    138		       struct acpi_pci_device **return_list_head)
    139{
    140	acpi_handle current_device;
    141	acpi_handle parent_device;
    142	acpi_status status;
    143	struct acpi_pci_device *list_element;
    144
    145	/*
    146	 * Ascend namespace branch until the root_pci_device is reached, building
    147	 * a list of device nodes. Loop will exit when either the PCI device is
    148	 * found, or the root of the namespace is reached.
    149	 */
    150	*return_list_head = NULL;
    151	current_device = pci_region;
    152	while (1) {
    153		status = acpi_get_parent(current_device, &parent_device);
    154		if (ACPI_FAILURE(status)) {
    155
    156			/* Must delete the list before exit */
    157
    158			acpi_hw_delete_pci_list(*return_list_head);
    159			return (status);
    160		}
    161
    162		/* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
    163
    164		if (parent_device == root_pci_device) {
    165			return (AE_OK);
    166		}
    167
    168		list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));
    169		if (!list_element) {
    170
    171			/* Must delete the list before exit */
    172
    173			acpi_hw_delete_pci_list(*return_list_head);
    174			return (AE_NO_MEMORY);
    175		}
    176
    177		/* Put new element at the head of the list */
    178
    179		list_element->next = *return_list_head;
    180		list_element->device = parent_device;
    181		*return_list_head = list_element;
    182
    183		current_device = parent_device;
    184	}
    185}
    186
    187/*******************************************************************************
    188 *
    189 * FUNCTION:    acpi_hw_process_pci_list
    190 *
    191 * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be
    192 *                                    modified by this function.
    193 *              list_head           - Device list created by
    194 *                                    acpi_hw_build_pci_list
    195 *
    196 * RETURN:      Status
    197 *
    198 * DESCRIPTION: Walk downward through the PCI device list, getting the device
    199 *              info for each, via the PCI configuration space and updating
    200 *              the PCI ID as necessary. Deletes the list during traversal.
    201 *
    202 ******************************************************************************/
    203
    204static acpi_status
    205acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
    206			 struct acpi_pci_device *list_head)
    207{
    208	acpi_status status = AE_OK;
    209	struct acpi_pci_device *info;
    210	u16 bus_number;
    211	u8 is_bridge = TRUE;
    212
    213	ACPI_FUNCTION_NAME(hw_process_pci_list);
    214
    215	ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
    216			  "Input PciId:  Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",
    217			  pci_id->segment, pci_id->bus, pci_id->device,
    218			  pci_id->function));
    219
    220	bus_number = pci_id->bus;
    221
    222	/*
    223	 * Descend down the namespace tree, collecting PCI device, function,
    224	 * and bus numbers. bus_number is only important for PCI bridges.
    225	 * Algorithm: As we descend the tree, use the last valid PCI device,
    226	 * function, and bus numbers that are discovered, and assign them
    227	 * to the PCI ID for the target device.
    228	 */
    229	info = list_head;
    230	while (info) {
    231		status = acpi_hw_get_pci_device_info(pci_id, info->device,
    232						     &bus_number, &is_bridge);
    233		if (ACPI_FAILURE(status)) {
    234			return (status);
    235		}
    236
    237		info = info->next;
    238	}
    239
    240	ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
    241			  "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "
    242			  "Status %X BusNumber %X IsBridge %X\n",
    243			  pci_id->segment, pci_id->bus, pci_id->device,
    244			  pci_id->function, status, bus_number, is_bridge));
    245
    246	return (AE_OK);
    247}
    248
    249/*******************************************************************************
    250 *
    251 * FUNCTION:    acpi_hw_delete_pci_list
    252 *
    253 * PARAMETERS:  list_head           - Device list created by
    254 *                                    acpi_hw_build_pci_list
    255 *
    256 * RETURN:      None
    257 *
    258 * DESCRIPTION: Free the entire PCI list.
    259 *
    260 ******************************************************************************/
    261
    262static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head)
    263{
    264	struct acpi_pci_device *next;
    265	struct acpi_pci_device *previous;
    266
    267	next = list_head;
    268	while (next) {
    269		previous = next;
    270		next = previous->next;
    271		ACPI_FREE(previous);
    272	}
    273}
    274
    275/*******************************************************************************
    276 *
    277 * FUNCTION:    acpi_hw_get_pci_device_info
    278 *
    279 * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be
    280 *                                    modified by this function.
    281 *              pci_device          - Handle for the PCI device object
    282 *              bus_number          - Where a PCI bridge bus number is returned
    283 *              is_bridge           - Return value, indicates if this PCI
    284 *                                    device is a PCI bridge
    285 *
    286 * RETURN:      Status
    287 *
    288 * DESCRIPTION: Get the device info for a single PCI device object. Get the
    289 *              _ADR (contains PCI device and function numbers), and for PCI
    290 *              bridge devices, get the bus number from PCI configuration
    291 *              space.
    292 *
    293 ******************************************************************************/
    294
    295static acpi_status
    296acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
    297			    acpi_handle pci_device,
    298			    u16 *bus_number, u8 *is_bridge)
    299{
    300	acpi_status status;
    301	acpi_object_type object_type;
    302	u64 return_value;
    303	u64 pci_value;
    304
    305	/* We only care about objects of type Device */
    306
    307	status = acpi_get_type(pci_device, &object_type);
    308	if (ACPI_FAILURE(status)) {
    309		return (status);
    310	}
    311
    312	if (object_type != ACPI_TYPE_DEVICE) {
    313		return (AE_OK);
    314	}
    315
    316	/* We need an _ADR. Ignore device if not present */
    317
    318	status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
    319						 pci_device, &return_value);
    320	if (ACPI_FAILURE(status)) {
    321		return (AE_OK);
    322	}
    323
    324	/*
    325	 * From _ADR, get the PCI Device and Function and
    326	 * update the PCI ID.
    327	 */
    328	pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value));
    329	pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value));
    330
    331	/*
    332	 * If the previous device was a bridge, use the previous
    333	 * device bus number
    334	 */
    335	if (*is_bridge) {
    336		pci_id->bus = *bus_number;
    337	}
    338
    339	/*
    340	 * Get the bus numbers from PCI Config space:
    341	 *
    342	 * First, get the PCI header_type
    343	 */
    344	*is_bridge = FALSE;
    345	status = acpi_os_read_pci_configuration(pci_id,
    346						PCI_CFG_HEADER_TYPE_REG,
    347						&pci_value, 8);
    348	if (ACPI_FAILURE(status)) {
    349		return (status);
    350	}
    351
    352	/* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */
    353
    354	pci_value &= PCI_HEADER_TYPE_MASK;
    355
    356	if ((pci_value != PCI_TYPE_BRIDGE) &&
    357	    (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) {
    358		return (AE_OK);
    359	}
    360
    361	/* Bridge: Get the Primary bus_number */
    362
    363	status = acpi_os_read_pci_configuration(pci_id,
    364						PCI_CFG_PRIMARY_BUS_NUMBER_REG,
    365						&pci_value, 8);
    366	if (ACPI_FAILURE(status)) {
    367		return (status);
    368	}
    369
    370	*is_bridge = TRUE;
    371	pci_id->bus = (u16)pci_value;
    372
    373	/* Bridge: Get the Secondary bus_number */
    374
    375	status = acpi_os_read_pci_configuration(pci_id,
    376						PCI_CFG_SECONDARY_BUS_NUMBER_REG,
    377						&pci_value, 8);
    378	if (ACPI_FAILURE(status)) {
    379		return (status);
    380	}
    381
    382	*bus_number = (u16)pci_value;
    383	return (AE_OK);
    384}