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 
11 typedef 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 
29 typedef 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 
xefi_find_pci_mmio(efi_boot_services * bs,uint8_t cls,uint8_t sub,uint8_t ifc,uint64_t * mmio)56 efi_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 (size_t i = 0; i < num_handles; i++) {
67         printf("handle %zu\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 %zu: %s\n",
72                    i, xefi_strerror(status));
73             continue;
74         }
75         acpi_addrspace_desc64_t* descriptors;
76         status = iodev->Configuration(iodev, (void**)&descriptors);
77         if (EFI_ERROR(status)) {
78             printf("Could not get configuration for handle %zu: %s\n",
79                    i, xefi_strerror(status));
80             continue;
81         }
82 
83         uint16_t min_bus, max_bus;
84         while (descriptors->descriptor != ACPI_END_TAG_DESCRIPTOR) {
85             min_bus = (uint16_t)descriptors->addrrange_minimum;
86             max_bus = (uint16_t)descriptors->addrrange_maximum;
87 
88             if (descriptors->res_type != ACPI_ADDRESS_SPACE_TYPE_BUS) {
89                 descriptors++;
90                 continue;
91             }
92 
93             for (int bus = min_bus; bus <= max_bus; bus++) {
94                 for (int dev = 0; dev < PCI_MAX_DEVICES; dev++) {
95                     for (int func = 0; func < PCI_MAX_FUNCS; func++) {
96                         pci_common_header_t pci_hdr;
97                         bs->SetMem(&pci_hdr, sizeof(pci_hdr), 0);
98                         uint64_t address = (uint64_t)((bus << 24) + (dev << 16) + (func << 8));
99                         status = iodev->Pci.Read(iodev, EfiPciWidthUint16, address, sizeof(pci_hdr) / 2, &pci_hdr);
100                         if (EFI_ERROR(status)) {
101                             printf("could not read pci configuration for bus %d dev %d func %d: %s\n",
102                                     bus, dev, func, xefi_strerror(status));
103                             continue;
104                         }
105                         if (pci_hdr.vid == 0xffff) break;
106                         if ((pci_hdr.class_code[2] == cls) &&
107                             (pci_hdr.class_code[1] == sub) &&
108                             (pci_hdr.class_code[0] == ifc)) {
109                             uint64_t n = ((uint64_t) pci_hdr.bar[0]) |
110                                          ((uint64_t) pci_hdr.bar[1]) << 32UL;
111                             *mmio = n & 0xFFFFFFFFFFFFFFF0UL;
112                             status = EFI_SUCCESS;
113                             goto found_it;
114                         }
115 #if 0
116                         printf("bus %04x dev %02x func %02x: ", bus, dev, func);
117                         printf("VID: 0x%04x  DID: 0x%04x  Class: 0x%02x  Subclass: 0x%02x  Intf: 0x%02x\n",
118                                 pci_hdr.vid, pci_hdr.did, pci_hdr.class_code[2], pci_hdr.class_code[1],
119                                 pci_hdr.class_code[0]);
120                         printf("     hdr_type: %02x\n", pci_hdr.hdr_type);
121                         if ((pci_hdr.hdr_type & 0x7f) == 0x00) {
122                             for (int bar = 0; bar < 6; bar++) {
123                                 if (pci_hdr.bar[bar]) {
124                                     printf("     bar[%d]: 0x%08x\n", bar, pci_hdr.bar[bar]);
125                                 }
126                                 bool is64bit = (pci_hdr.bar[bar] & 0x06) == 0x04;
127                                 if (is64bit) {
128                                     printf("     bar[%d]: 0x%08x\n", bar+1, pci_hdr.bar[bar+1]);
129                                     bar++;
130                                 }
131                                 // TODO: get the BAR size
132                                 //   - disable IO
133                                 //   - write 1s
134                                 //   - read it back
135                                 //   - reset or map to somewhere else(?)
136                             }
137                         }
138 #endif
139                         if (!(pci_hdr.hdr_type & 0x80) && func == 0) {
140                             break;
141                         }
142                     }
143                 }
144             }
145             descriptors++;
146         }
147     }
148 
149     status = EFI_NOT_FOUND;
150 found_it:
151     bs->FreePool(handles);
152     return status;
153 }
154