[utils] find a pci device by class/subclass/interface and get bar[0] mmio

Change-Id: Ib60a40179cdf887b2eb5cb0a61d143ea7346c6bf
diff --git a/include/utils.h b/include/utils.h
index 3d8d3e8..3a7b225 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -20,6 +20,8 @@
 
 void* LoadFile(CHAR16* filename, UINTN* size_out);
 
+EFI_STATUS FindPCIMMIO(EFI_BOOT_SERVICES* bs, uint8_t cls, uint8_t sub, uint8_t ifc, uint64_t* mmio);
+
 // GUIDs
 extern EFI_GUID SimpleFileSystemProtocol;
 extern EFI_GUID FileInfoGUID;
diff --git a/src/pci.c b/src/pci.c
new file mode 100644
index 0000000..6bf0dfa
--- /dev/null
+++ b/src/pci.c
@@ -0,0 +1,152 @@
+// 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.h>
+#include <efilib.h>
+#include <efi/protocol/pci-root-bridge-io.h>
+
+#include <stdio.h>
+#include <utils.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 FindPCIMMIO(EFI_BOOT_SERVICES* bs, uint8_t cls, uint8_t sub, uint8_t ifc, uint64_t* mmio) {
+    UINTN 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", efi_strerror(status));
+        return status;
+    }
+
+    for (int i = 0; i < num_handles; i++) {
+        printf("handle %d\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 %d: %s\n", i, efi_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 %d: %s\n", i, efi_strerror(status));
+            continue;
+        }
+
+        UINT16 min_bus, max_bus;
+        while (descriptors->descriptor != ACPI_END_TAG_DESCRIPTOR) {
+            min_bus = (UINT16)descriptors->addrrange_minimum;
+            max_bus = (UINT16)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 address = (UINT64)((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, efi_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;
+}