xref: /openbmc/linux/drivers/firmware/efi/libstub/pci.c (revision e7bae9bb)
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 
16 void 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 
112 free_handle:
113 	efi_bs_call(free_pool, pci_handle);
114 }
115