18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCI-related functions used by the EFI stub on multiple 48c2ecf20Sopenharmony_ci * architectures. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2019 Google, LLC 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/efi.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/efi.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "efistub.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_civoid efi_pci_disable_bridge_busmaster(void) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; 198c2ecf20Sopenharmony_ci unsigned long pci_handle_size = 0; 208c2ecf20Sopenharmony_ci efi_handle_t *pci_handle = NULL; 218c2ecf20Sopenharmony_ci efi_handle_t handle; 228c2ecf20Sopenharmony_ci efi_status_t status; 238c2ecf20Sopenharmony_ci u16 class, command; 248c2ecf20Sopenharmony_ci int i; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto, 278c2ecf20Sopenharmony_ci NULL, &pci_handle_size, NULL); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (status != EFI_BUFFER_TOO_SMALL) { 308c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) 318c2ecf20Sopenharmony_ci efi_err("Failed to locate PCI I/O handles'\n"); 328c2ecf20Sopenharmony_ci return; 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size, 368c2ecf20Sopenharmony_ci (void **)&pci_handle); 378c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 388c2ecf20Sopenharmony_ci efi_err("Failed to allocate memory for 'pci_handle'\n"); 398c2ecf20Sopenharmony_ci return; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto, 438c2ecf20Sopenharmony_ci NULL, &pci_handle_size, pci_handle); 448c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 458c2ecf20Sopenharmony_ci efi_err("Failed to locate PCI I/O handles'\n"); 468c2ecf20Sopenharmony_ci goto free_handle; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci for_each_efi_handle(handle, pci_handle, pci_handle_size, i) { 508c2ecf20Sopenharmony_ci efi_pci_io_protocol_t *pci; 518c2ecf20Sopenharmony_ci unsigned long segment_nr, bus_nr, device_nr, func_nr; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci status = efi_bs_call(handle_protocol, handle, &pci_proto, 548c2ecf20Sopenharmony_ci (void **)&pci); 558c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 568c2ecf20Sopenharmony_ci continue; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * Disregard devices living on bus 0 - these are not behind a 608c2ecf20Sopenharmony_ci * bridge so no point in disconnecting them from their drivers. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci status = efi_call_proto(pci, get_location, &segment_nr, &bus_nr, 638c2ecf20Sopenharmony_ci &device_nr, &func_nr); 648c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS || bus_nr == 0) 658c2ecf20Sopenharmony_ci continue; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * Don't disconnect VGA controllers so we don't risk losing 698c2ecf20Sopenharmony_ci * access to the framebuffer. Drivers for true PCIe graphics 708c2ecf20Sopenharmony_ci * controllers that are behind a PCIe root port do not use 718c2ecf20Sopenharmony_ci * DMA to implement the GOP framebuffer anyway [although they 728c2ecf20Sopenharmony_ci * may use it in their implementation of Gop->Blt()], and so 738c2ecf20Sopenharmony_ci * disabling DMA in the PCI bridge should not interfere with 748c2ecf20Sopenharmony_ci * normal operation of the device. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16, 778c2ecf20Sopenharmony_ci PCI_CLASS_DEVICE, 1, &class); 788c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS || class == PCI_CLASS_DISPLAY_VGA) 798c2ecf20Sopenharmony_ci continue; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Disconnect this handle from all its drivers */ 828c2ecf20Sopenharmony_ci efi_bs_call(disconnect_controller, handle, NULL, NULL); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci for_each_efi_handle(handle, pci_handle, pci_handle_size, i) { 868c2ecf20Sopenharmony_ci efi_pci_io_protocol_t *pci; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci status = efi_bs_call(handle_protocol, handle, &pci_proto, 898c2ecf20Sopenharmony_ci (void **)&pci); 908c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS || !pci) 918c2ecf20Sopenharmony_ci continue; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16, 948c2ecf20Sopenharmony_ci PCI_CLASS_DEVICE, 1, &class); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS || class != PCI_CLASS_BRIDGE_PCI) 978c2ecf20Sopenharmony_ci continue; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Disable busmastering */ 1008c2ecf20Sopenharmony_ci status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16, 1018c2ecf20Sopenharmony_ci PCI_COMMAND, 1, &command); 1028c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS || !(command & PCI_COMMAND_MASTER)) 1038c2ecf20Sopenharmony_ci continue; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci command &= ~PCI_COMMAND_MASTER; 1068c2ecf20Sopenharmony_ci status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16, 1078c2ecf20Sopenharmony_ci PCI_COMMAND, 1, &command); 1088c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 1098c2ecf20Sopenharmony_ci efi_err("Failed to disable PCI busmastering\n"); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cifree_handle: 1138c2ecf20Sopenharmony_ci efi_bs_call(free_pool, pci_handle); 1148c2ecf20Sopenharmony_ci} 115