linux/drivers/firmware/efi/libstub/pci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * PCI-related functions used by the EFI stub on multiple
   4 * architectures.
   5 *
   6 * Copyright 2019 Google, LLC
   7 */
   8
   9#include <linux/efi.h>
  10#include <linux/pci.h>
  11
  12#include <asm/efi.h>
  13
  14#include "efistub.h"
  15
  16void efi_pci_disable_bridge_busmaster(void)
  17{
  18        efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
  19        unsigned long pci_handle_size = 0;
  20        efi_handle_t *pci_handle = NULL;
  21        efi_handle_t handle;
  22        efi_status_t status;
  23        u16 class, command;
  24        int i;
  25
  26        status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
  27                             NULL, &pci_handle_size, NULL);
  28
  29        if (status != EFI_BUFFER_TOO_SMALL) {
  30                if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
  31                        efi_err("Failed to locate PCI I/O handles'\n");
  32                return;
  33        }
  34
  35        status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size,
  36                             (void **)&pci_handle);
  37        if (status != EFI_SUCCESS) {
  38                efi_err("Failed to allocate memory for 'pci_handle'\n");
  39                return;
  40        }
  41
  42        status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
  43                             NULL, &pci_handle_size, pci_handle);
  44        if (status != EFI_SUCCESS) {
  45                efi_err("Failed to locate PCI I/O handles'\n");
  46                goto free_handle;
  47        }
  48
  49        for_each_efi_handle(handle, pci_handle, pci_handle_size, i) {
  50                efi_pci_io_protocol_t *pci;
  51                unsigned long segment_nr, bus_nr, device_nr, func_nr;
  52
  53                status = efi_bs_call(handle_protocol, handle, &pci_proto,
  54                                     (void **)&pci);
  55                if (status != EFI_SUCCESS)
  56                        continue;
  57
  58                /*
  59                 * Disregard devices living on bus 0 - these are not behind a
  60                 * bridge so no point in disconnecting them from their drivers.
  61                 */
  62                status = efi_call_proto(pci, get_location, &segment_nr, &bus_nr,
  63                                        &device_nr, &func_nr);
  64                if (status != EFI_SUCCESS || bus_nr == 0)
  65                        continue;
  66
  67                /*
  68                 * Don't disconnect VGA controllers so we don't risk losing
  69                 * access to the framebuffer. Drivers for true PCIe graphics
  70                 * controllers that are behind a PCIe root port do not use
  71                 * DMA to implement the GOP framebuffer anyway [although they
  72                 * may use it in their implementation of Gop->Blt()], and so
  73                 * disabling DMA in the PCI bridge should not interfere with
  74                 * normal operation of the device.
  75                 */
  76                status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
  77                                        PCI_CLASS_DEVICE, 1, &class);
  78                if (status != EFI_SUCCESS || class == PCI_CLASS_DISPLAY_VGA)
  79                        continue;
  80
  81                /* Disconnect this handle from all its drivers */
  82                efi_bs_call(disconnect_controller, handle, NULL, NULL);
  83        }
  84
  85        for_each_efi_handle(handle, pci_handle, pci_handle_size, i) {
  86                efi_pci_io_protocol_t *pci;
  87
  88                status = efi_bs_call(handle_protocol, handle, &pci_proto,
  89                                     (void **)&pci);
  90                if (status != EFI_SUCCESS || !pci)
  91                        continue;
  92
  93                status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
  94                                        PCI_CLASS_DEVICE, 1, &class);
  95
  96                if (status != EFI_SUCCESS || class != PCI_CLASS_BRIDGE_PCI)
  97                        continue;
  98
  99                /* Disable busmastering */
 100                status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
 101                                        PCI_COMMAND, 1, &command);
 102                if (status != EFI_SUCCESS || !(command & PCI_COMMAND_MASTER))
 103                        continue;
 104
 105                command &= ~PCI_COMMAND_MASTER;
 106                status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16,
 107                                        PCI_COMMAND, 1, &command);
 108                if (status != EFI_SUCCESS)
 109                        efi_err("Failed to disable PCI busmastering\n");
 110        }
 111
 112free_handle:
 113        efi_bs_call(free_pool, pci_handle);
 114}
 115