xref: /openbmc/linux/drivers/firmware/efi/libstub/pci.c (revision 40262299)
14444f854SMatthew Garrett // SPDX-License-Identifier: GPL-2.0
24444f854SMatthew Garrett /*
34444f854SMatthew Garrett  * PCI-related functions used by the EFI stub on multiple
44444f854SMatthew Garrett  * architectures.
54444f854SMatthew Garrett  *
64444f854SMatthew Garrett  * Copyright 2019 Google, LLC
74444f854SMatthew Garrett  */
84444f854SMatthew Garrett 
94444f854SMatthew Garrett #include <linux/efi.h>
104444f854SMatthew Garrett #include <linux/pci.h>
114444f854SMatthew Garrett 
124444f854SMatthew Garrett #include <asm/efi.h>
134444f854SMatthew Garrett 
144444f854SMatthew Garrett #include "efistub.h"
154444f854SMatthew Garrett 
efi_pci_disable_bridge_busmaster(void)164444f854SMatthew Garrett void efi_pci_disable_bridge_busmaster(void)
174444f854SMatthew Garrett {
184444f854SMatthew Garrett 	efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
194444f854SMatthew Garrett 	unsigned long pci_handle_size = 0;
204444f854SMatthew Garrett 	efi_handle_t *pci_handle = NULL;
214444f854SMatthew Garrett 	efi_handle_t handle;
224444f854SMatthew Garrett 	efi_status_t status;
234444f854SMatthew Garrett 	u16 class, command;
244444f854SMatthew Garrett 	int i;
254444f854SMatthew Garrett 
264444f854SMatthew Garrett 	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
274444f854SMatthew Garrett 			     NULL, &pci_handle_size, NULL);
284444f854SMatthew Garrett 
294444f854SMatthew Garrett 	if (status != EFI_BUFFER_TOO_SMALL) {
304444f854SMatthew Garrett 		if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
31793473c2SArvind Sankar 			efi_err("Failed to locate PCI I/O handles'\n");
324444f854SMatthew Garrett 		return;
334444f854SMatthew Garrett 	}
344444f854SMatthew Garrett 
354444f854SMatthew Garrett 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size,
364444f854SMatthew Garrett 			     (void **)&pci_handle);
374444f854SMatthew Garrett 	if (status != EFI_SUCCESS) {
38793473c2SArvind Sankar 		efi_err("Failed to allocate memory for 'pci_handle'\n");
394444f854SMatthew Garrett 		return;
404444f854SMatthew Garrett 	}
414444f854SMatthew Garrett 
424444f854SMatthew Garrett 	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
434444f854SMatthew Garrett 			     NULL, &pci_handle_size, pci_handle);
444444f854SMatthew Garrett 	if (status != EFI_SUCCESS) {
45793473c2SArvind Sankar 		efi_err("Failed to locate PCI I/O handles'\n");
464444f854SMatthew Garrett 		goto free_handle;
474444f854SMatthew Garrett 	}
484444f854SMatthew Garrett 
494444f854SMatthew Garrett 	for_each_efi_handle(handle, pci_handle, pci_handle_size, i) {
504444f854SMatthew Garrett 		efi_pci_io_protocol_t *pci;
514444f854SMatthew Garrett 		unsigned long segment_nr, bus_nr, device_nr, func_nr;
524444f854SMatthew Garrett 
534444f854SMatthew Garrett 		status = efi_bs_call(handle_protocol, handle, &pci_proto,
544444f854SMatthew Garrett 				     (void **)&pci);
554444f854SMatthew Garrett 		if (status != EFI_SUCCESS)
564444f854SMatthew Garrett 			continue;
574444f854SMatthew Garrett 
584444f854SMatthew Garrett 		/*
594444f854SMatthew Garrett 		 * Disregard devices living on bus 0 - these are not behind a
604444f854SMatthew Garrett 		 * bridge so no point in disconnecting them from their drivers.
614444f854SMatthew Garrett 		 */
624444f854SMatthew Garrett 		status = efi_call_proto(pci, get_location, &segment_nr, &bus_nr,
634444f854SMatthew Garrett 					&device_nr, &func_nr);
644444f854SMatthew Garrett 		if (status != EFI_SUCCESS || bus_nr == 0)
654444f854SMatthew Garrett 			continue;
664444f854SMatthew Garrett 
674444f854SMatthew Garrett 		/*
684444f854SMatthew Garrett 		 * Don't disconnect VGA controllers so we don't risk losing
694444f854SMatthew Garrett 		 * access to the framebuffer. Drivers for true PCIe graphics
704444f854SMatthew Garrett 		 * controllers that are behind a PCIe root port do not use
714444f854SMatthew Garrett 		 * DMA to implement the GOP framebuffer anyway [although they
7240262299SJoe Perches 		 * may use it in their implementation of Gop->Blt()], and so
734444f854SMatthew Garrett 		 * disabling DMA in the PCI bridge should not interfere with
744444f854SMatthew Garrett 		 * normal operation of the device.
754444f854SMatthew Garrett 		 */
764444f854SMatthew Garrett 		status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
774444f854SMatthew Garrett 					PCI_CLASS_DEVICE, 1, &class);
784444f854SMatthew Garrett 		if (status != EFI_SUCCESS || class == PCI_CLASS_DISPLAY_VGA)
794444f854SMatthew Garrett 			continue;
804444f854SMatthew Garrett 
814444f854SMatthew Garrett 		/* Disconnect this handle from all its drivers */
824444f854SMatthew Garrett 		efi_bs_call(disconnect_controller, handle, NULL, NULL);
834444f854SMatthew Garrett 	}
844444f854SMatthew Garrett 
854444f854SMatthew Garrett 	for_each_efi_handle(handle, pci_handle, pci_handle_size, i) {
864444f854SMatthew Garrett 		efi_pci_io_protocol_t *pci;
874444f854SMatthew Garrett 
884444f854SMatthew Garrett 		status = efi_bs_call(handle_protocol, handle, &pci_proto,
894444f854SMatthew Garrett 				     (void **)&pci);
904444f854SMatthew Garrett 		if (status != EFI_SUCCESS || !pci)
914444f854SMatthew Garrett 			continue;
924444f854SMatthew Garrett 
934444f854SMatthew Garrett 		status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
944444f854SMatthew Garrett 					PCI_CLASS_DEVICE, 1, &class);
954444f854SMatthew Garrett 
964444f854SMatthew Garrett 		if (status != EFI_SUCCESS || class != PCI_CLASS_BRIDGE_PCI)
974444f854SMatthew Garrett 			continue;
984444f854SMatthew Garrett 
994444f854SMatthew Garrett 		/* Disable busmastering */
1004444f854SMatthew Garrett 		status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
1014444f854SMatthew Garrett 					PCI_COMMAND, 1, &command);
1024444f854SMatthew Garrett 		if (status != EFI_SUCCESS || !(command & PCI_COMMAND_MASTER))
1034444f854SMatthew Garrett 			continue;
1044444f854SMatthew Garrett 
1054444f854SMatthew Garrett 		command &= ~PCI_COMMAND_MASTER;
1064444f854SMatthew Garrett 		status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16,
1074444f854SMatthew Garrett 					PCI_COMMAND, 1, &command);
1084444f854SMatthew Garrett 		if (status != EFI_SUCCESS)
109793473c2SArvind Sankar 			efi_err("Failed to disable PCI busmastering\n");
1104444f854SMatthew Garrett 	}
1114444f854SMatthew Garrett 
1124444f854SMatthew Garrett free_handle:
1134444f854SMatthew Garrett 	efi_bs_call(free_pool, pci_handle);
1144444f854SMatthew Garrett }
115