1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <efi/types.h>
6#include <efi/protocol/pci-root-bridge-io.h>
7
8#include <stdio.h>
9#include <xefi.h>
10
11typedef struct {
12    uint8_t descriptor;
13    uint16_t len;
14    uint8_t res_type;
15    uint8_t gen_flags;
16    uint8_t specific_flags;
17    uint64_t addrspace_granularity;
18    uint64_t addrrange_minimum;
19    uint64_t addrrange_maximum;
20    uint64_t addr_tr_offset;
21    uint64_t addr_len;
22} __attribute__((packed)) acpi_addrspace_desc64_t;
23
24#define ACPI_ADDRESS_SPACE_DESCRIPTOR 0x8A
25#define ACPI_END_TAG_DESCRIPTOR       0x79
26
27#define ACPI_ADDRESS_SPACE_TYPE_BUS   0x02
28
29typedef struct {
30    uint16_t vid;
31    uint16_t did;
32    uint16_t cmd;
33    uint16_t status;
34    uint8_t rev_id;
35    uint8_t class_code[3];
36    uint8_t cache_line_size;
37    uint8_t primary_lat_timer;
38    uint8_t hdr_type;
39    uint8_t bist;
40    uint32_t bar[6];
41    uint32_t cardbus_cis;
42    uint16_t subid;
43    uint16_t subvid;
44    uint32_t exprom_bar;
45    uint8_t cap_ptr;
46    uint8_t reserved[7];
47    uint8_t irq_line;
48    uint8_t irq_pin;
49    uint8_t min_grant;
50    uint8_t max_lat;
51} __attribute__((packed)) pci_common_header_t;
52
53#define PCI_MAX_DEVICES 32
54#define PCI_MAX_FUNCS 8
55
56efi_status xefi_find_pci_mmio(efi_boot_services* bs, uint8_t cls, uint8_t sub, uint8_t ifc, uint64_t* mmio) {
57    size_t num_handles;
58    efi_handle* handles;
59    efi_status status = bs->LocateHandleBuffer(ByProtocol, &PciRootBridgeIoProtocol,
60            NULL, &num_handles, &handles);
61    if (EFI_ERROR(status)) {
62        printf("Could not find PCI root bridge IO protocol: %s\n", xefi_strerror(status));
63        return status;
64    }
65
66    for (int i = 0; i < num_handles; i++) {
67        printf("handle %d\n", i);
68        efi_pci_root_bridge_io_protocol* iodev;
69        status = bs->HandleProtocol(handles[i], &PciRootBridgeIoProtocol, (void**)&iodev);
70        if (EFI_ERROR(status)) {
71            printf("Could not get protocol for handle %d: %s\n", i, xefi_strerror(status));
72            continue;
73        }
74        acpi_addrspace_desc64_t* descriptors;
75        status = iodev->Configuration(iodev, (void**)&descriptors);
76        if (EFI_ERROR(status)) {
77            printf("Could not get configuration for handle %d: %s\n", i, xefi_strerror(status));
78            continue;
79        }
80
81        uint16_t min_bus, max_bus;
82        while (descriptors->descriptor != ACPI_END_TAG_DESCRIPTOR) {
83            min_bus = (uint16_t)descriptors->addrrange_minimum;
84            max_bus = (uint16_t)descriptors->addrrange_maximum;
85
86            if (descriptors->res_type != ACPI_ADDRESS_SPACE_TYPE_BUS) {
87                descriptors++;
88                continue;
89            }
90
91            for (int bus = min_bus; bus <= max_bus; bus++) {
92                for (int dev = 0; dev < PCI_MAX_DEVICES; dev++) {
93                    for (int func = 0; func < PCI_MAX_FUNCS; func++) {
94                        pci_common_header_t pci_hdr;
95                        bs->SetMem(&pci_hdr, sizeof(pci_hdr), 0);
96                        uint64_t address = (uint64_t)((bus << 24) + (dev << 16) + (func << 8));
97                        status = iodev->Pci.Read(iodev, EfiPciWidthUint16, address, sizeof(pci_hdr) / 2, &pci_hdr);
98                        if (EFI_ERROR(status)) {
99                            printf("could not read pci configuration for bus %d dev %d func %d: %s\n",
100                                    bus, dev, func, xefi_strerror(status));
101                            continue;
102                        }
103                        if (pci_hdr.vid == 0xffff) break;
104                        if ((pci_hdr.class_code[2] == cls) &&
105                            (pci_hdr.class_code[1] == sub) &&
106                            (pci_hdr.class_code[0] == ifc)) {
107                            uint64_t n = ((uint64_t) pci_hdr.bar[0]) |
108                                         ((uint64_t) pci_hdr.bar[1]) << 32UL;
109                            *mmio = n & 0xFFFFFFFFFFFFFFF0UL;
110                            status = EFI_SUCCESS;
111                            goto found_it;
112                        }
113#if 0
114                        printf("bus %04x dev %02x func %02x: ", bus, dev, func);
115                        printf("VID: 0x%04x  DID: 0x%04x  Class: 0x%02x  Subclass: 0x%02x  Intf: 0x%02x\n",
116                                pci_hdr.vid, pci_hdr.did, pci_hdr.class_code[2], pci_hdr.class_code[1],
117                                pci_hdr.class_code[0]);
118                        printf("     hdr_type: %02x\n", pci_hdr.hdr_type);
119                        if ((pci_hdr.hdr_type & 0x7f) == 0x00) {
120                            for (int bar = 0; bar < 6; bar++) {
121                                if (pci_hdr.bar[bar]) {
122                                    printf("     bar[%d]: 0x%08x\n", bar, pci_hdr.bar[bar]);
123                                }
124                                bool is64bit = (pci_hdr.bar[bar] & 0x06) == 0x04;
125                                if (is64bit) {
126                                    printf("     bar[%d]: 0x%08x\n", bar+1, pci_hdr.bar[bar+1]);
127                                    bar++;
128                                }
129                                // TODO: get the BAR size
130                                //   - disable IO
131                                //   - write 1s
132                                //   - read it back
133                                //   - reset or map to somewhere else(?)
134                            }
135                        }
136#endif
137                        if (!(pci_hdr.hdr_type & 0x80) && func == 0) {
138                            break;
139                        }
140                    }
141                }
142            }
143            descriptors++;
144        }
145    }
146
147    status = EFI_NOT_FOUND;
148found_it:
149    bs->FreePool(handles);
150    return status;
151}
152