| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <efi/types.h> |
| #include <efi/protocol/pci-root-bridge-io.h> |
| |
| #include <stdio.h> |
| #include <xefi.h> |
| |
| typedef struct { |
| uint8_t descriptor; |
| uint16_t len; |
| uint8_t res_type; |
| uint8_t gen_flags; |
| uint8_t specific_flags; |
| uint64_t addrspace_granularity; |
| uint64_t addrrange_minimum; |
| uint64_t addrrange_maximum; |
| uint64_t addr_tr_offset; |
| uint64_t addr_len; |
| } __attribute__((packed)) acpi_addrspace_desc64_t; |
| |
| #define ACPI_ADDRESS_SPACE_DESCRIPTOR 0x8A |
| #define ACPI_END_TAG_DESCRIPTOR 0x79 |
| |
| #define ACPI_ADDRESS_SPACE_TYPE_BUS 0x02 |
| |
| typedef struct { |
| uint16_t vid; |
| uint16_t did; |
| uint16_t cmd; |
| uint16_t status; |
| uint8_t rev_id; |
| uint8_t class_code[3]; |
| uint8_t cache_line_size; |
| uint8_t primary_lat_timer; |
| uint8_t hdr_type; |
| uint8_t bist; |
| uint32_t bar[6]; |
| uint32_t cardbus_cis; |
| uint16_t subid; |
| uint16_t subvid; |
| uint32_t exprom_bar; |
| uint8_t cap_ptr; |
| uint8_t reserved[7]; |
| uint8_t irq_line; |
| uint8_t irq_pin; |
| uint8_t min_grant; |
| uint8_t max_lat; |
| } __attribute__((packed)) pci_common_header_t; |
| |
| #define PCI_MAX_DEVICES 32 |
| #define PCI_MAX_FUNCS 8 |
| |
| efi_status xefi_find_pci_mmio(efi_boot_services* bs, uint8_t cls, uint8_t sub, uint8_t ifc, uint64_t* mmio) { |
| size_t num_handles; |
| efi_handle* handles; |
| efi_status status = bs->LocateHandleBuffer(ByProtocol, &PciRootBridgeIoProtocol, |
| NULL, &num_handles, &handles); |
| if (EFI_ERROR(status)) { |
| printf("Could not find PCI root bridge IO protocol: %s\n", xefi_strerror(status)); |
| return status; |
| } |
| |
| for (size_t i = 0; i < num_handles; i++) { |
| printf("handle %zu\n", i); |
| efi_pci_root_bridge_io_protocol* iodev; |
| status = bs->HandleProtocol(handles[i], &PciRootBridgeIoProtocol, (void**)&iodev); |
| if (EFI_ERROR(status)) { |
| printf("Could not get protocol for handle %zu: %s\n", |
| i, xefi_strerror(status)); |
| continue; |
| } |
| acpi_addrspace_desc64_t* descriptors; |
| status = iodev->Configuration(iodev, (void**)&descriptors); |
| if (EFI_ERROR(status)) { |
| printf("Could not get configuration for handle %zu: %s\n", |
| i, xefi_strerror(status)); |
| continue; |
| } |
| |
| uint16_t min_bus, max_bus; |
| while (descriptors->descriptor != ACPI_END_TAG_DESCRIPTOR) { |
| min_bus = (uint16_t)descriptors->addrrange_minimum; |
| max_bus = (uint16_t)descriptors->addrrange_maximum; |
| |
| if (descriptors->res_type != ACPI_ADDRESS_SPACE_TYPE_BUS) { |
| descriptors++; |
| continue; |
| } |
| |
| for (int bus = min_bus; bus <= max_bus; bus++) { |
| for (int dev = 0; dev < PCI_MAX_DEVICES; dev++) { |
| for (int func = 0; func < PCI_MAX_FUNCS; func++) { |
| pci_common_header_t pci_hdr; |
| bs->SetMem(&pci_hdr, sizeof(pci_hdr), 0); |
| uint64_t address = (uint64_t)((bus << 24) + (dev << 16) + (func << 8)); |
| status = iodev->Pci.Read(iodev, EfiPciWidthUint16, address, sizeof(pci_hdr) / 2, &pci_hdr); |
| if (EFI_ERROR(status)) { |
| printf("could not read pci configuration for bus %d dev %d func %d: %s\n", |
| bus, dev, func, xefi_strerror(status)); |
| continue; |
| } |
| if (pci_hdr.vid == 0xffff) break; |
| if ((pci_hdr.class_code[2] == cls) && |
| (pci_hdr.class_code[1] == sub) && |
| (pci_hdr.class_code[0] == ifc)) { |
| uint64_t n = ((uint64_t) pci_hdr.bar[0]) | |
| ((uint64_t) pci_hdr.bar[1]) << 32UL; |
| *mmio = n & 0xFFFFFFFFFFFFFFF0UL; |
| status = EFI_SUCCESS; |
| goto found_it; |
| } |
| #if 0 |
| printf("bus %04x dev %02x func %02x: ", bus, dev, func); |
| printf("VID: 0x%04x DID: 0x%04x Class: 0x%02x Subclass: 0x%02x Intf: 0x%02x\n", |
| pci_hdr.vid, pci_hdr.did, pci_hdr.class_code[2], pci_hdr.class_code[1], |
| pci_hdr.class_code[0]); |
| printf(" hdr_type: %02x\n", pci_hdr.hdr_type); |
| if ((pci_hdr.hdr_type & 0x7f) == 0x00) { |
| for (int bar = 0; bar < 6; bar++) { |
| if (pci_hdr.bar[bar]) { |
| printf(" bar[%d]: 0x%08x\n", bar, pci_hdr.bar[bar]); |
| } |
| bool is64bit = (pci_hdr.bar[bar] & 0x06) == 0x04; |
| if (is64bit) { |
| printf(" bar[%d]: 0x%08x\n", bar+1, pci_hdr.bar[bar+1]); |
| bar++; |
| } |
| // TODO: get the BAR size |
| // - disable IO |
| // - write 1s |
| // - read it back |
| // - reset or map to somewhere else(?) |
| } |
| } |
| #endif |
| if (!(pci_hdr.hdr_type & 0x80) && func == 0) { |
| break; |
| } |
| } |
| } |
| } |
| descriptors++; |
| } |
| } |
| |
| status = EFI_NOT_FOUND; |
| found_it: |
| bs->FreePool(handles); |
| return status; |
| } |