blob: e22801608d27d81460aa831e6ae3bde73baa0af5 [file] [log] [blame]
// 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;
}