Linux Audio

Check our new training course

Loading...
  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}