1 // Copyright 2018 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 //
7 #include "bus.h"
8 #include "common.h"
9 #include "config.h"
10
11 #include <ddk/debug.h>
12 #include <ddk/device.h>
13 #include <ddk/mmio-buffer.h>
14 #include <fbl/alloc_checker.h>
15
16 namespace pci {
17
18 // Creates the PCI bus driver instance and attempts initialization.
Create(zx_device_t * parent)19 zx_status_t Bus::Create(zx_device_t* parent) {
20 zx_status_t status;
21 pciroot_protocol_t pciroot;
22 if ((status = device_get_protocol(parent, ZX_PROTOCOL_PCIROOT, &pciroot)) != ZX_OK) {
23 pci_errorf("failed to obtain pciroot protocol: %d!\n", status);
24 return status;
25 }
26
27 fbl::AllocChecker ac;
28 Bus* bus = new (&ac) Bus(parent, &pciroot);
29 if (!ac.check()) {
30 pci_errorf("failed to allocate bus object.\n");
31 return ZX_ERR_NO_MEMORY;
32 }
33
34 if ((status = bus->Initialize()) != ZX_OK) {
35 pci_errorf("failed to initialize bus driver: %d!\n", status);
36 return status;
37 }
38
39 // Grab the info beforehand so we can get segment/bus information from it
40 // and appropriately name our DDK device.
41 pci_platform_info_t info;
42 status = bus->pciroot().GetPciPlatformInfo(&info);
43 if (status != ZX_OK) {
44 pci_errorf("failed to obtain platform information: %d!\n", status);
45 return status;
46 }
47
48 // Name the bus instance with segment group and bus range, for example:
49 // pci[0][0:255] for a legacy pci bus in segment group 0.
50 char name[32];
51 snprintf(name, sizeof(name), "pci[%u][%u:%u]", info.segment_group, info.start_bus_num,
52 info.end_bus_num);
53
54 return bus->DdkAdd(name);
55 }
56
Initialize()57 zx_status_t Bus::Initialize() {
58 zx_status_t status = pciroot_.GetPciPlatformInfo(&info_);
59 if (status != ZX_OK) {
60 pci_errorf("failed to obtain platform information: %d!\n", status);
61 return status;
62 }
63
64 if (info_.ecam_vmo != ZX_HANDLE_INVALID) {
65 if ((status = MapEcam()) != ZX_OK) {
66 pci_errorf("failed to map ecam: %d!\n", status);
67 return status;
68 }
69 }
70
71 ScanDownstream();
72 return ZX_OK;
73 }
74
75 // Maps a vmo as an mmio_buffer to be used as this Bus driver's ECAM region
76 // for config space access.
MapEcam(void)77 zx_status_t Bus::MapEcam(void) {
78 ZX_DEBUG_ASSERT(info_.ecam_vmo != ZX_HANDLE_INVALID);
79
80 size_t size;
81 zx_status_t status = zx_vmo_get_size(info_.ecam_vmo, &size);
82 if (status != ZX_OK) {
83 pci_errorf("couldn't get ecam vmo size: %d!\n", status);
84 return status;
85 }
86
87 status = mmio_buffer_init(&ecam_, 0, size, info_.ecam_vmo, ZX_CACHE_POLICY_UNCACHED);
88 if (status != ZX_OK) {
89 pci_errorf("couldn't map ecam vmo: %d!\n", status);
90 return status;
91 }
92
93 pci_infof("ecam for segment %u mapped at %p (size: %#zx)\n", info_.segment_group,
94 ecam_.vaddr, ecam_.size);
95 has_ecam_ = true;
96 return ZX_OK;
97 }
98
MakeConfig(pci_bdf_t bdf,fbl::RefPtr<Config> * config)99 zx_status_t Bus::MakeConfig(pci_bdf_t bdf, fbl::RefPtr<Config>* config) {
100 if (has_ecam_) {
101 return MmioConfig::Create(bdf, &ecam_, info_.start_bus_num, info_.end_bus_num, config);
102 } else {
103 return ProxyConfig::Create(bdf, &pciroot_, config);
104 }
105 }
106
107 // Scan downstream starting at the start bus number provided to use by the platform.
108 // In the process of scanning, take note of bridges found and configure any that are
109 // unconfigured. In the end the Bus should have a list of all devides, and all bridges
110 // should have a list of references to their own downstream devices.
ScanDownstream(void)111 zx_status_t Bus::ScanDownstream(void) {
112 pci_infof("ScanDownstream %u:%u\n", info_.start_bus_num, info_.end_bus_num);
113 for (uint16_t bus_id = info_.start_bus_num; bus_id <= info_.end_bus_num; bus_id++) {
114 for (uint8_t dev_id = 0; dev_id < PCI_MAX_DEVICES_PER_BUS; dev_id++) {
115 for (uint8_t func_id = 0; func_id < PCI_MAX_FUNCTIONS_PER_DEVICE; func_id++) {
116 fbl::RefPtr<Config> config;
117 pci_bdf_t bdf = { static_cast<uint8_t>(bus_id), dev_id, func_id };
118 zx_status_t status = MakeConfig(bdf, &config);
119 if (status == ZX_OK) {
120 if (config->vendor_id() != 0xFFFF) {
121 pci_infof("found device at %02x:%02x.%1x\n", bus_id, dev_id, func_id);
122 }
123 }
124 }
125 }
126 }
127
128 return ZX_OK;
129 }
130
DdkRelease()131 void Bus::DdkRelease() {
132 if (ecam_.vaddr) {
133 mmio_buffer_release(&ecam_);
134 }
135 delete this;
136 }
137
138 } // namespace pci
139
pci_bus_bind(void * ctx,zx_device_t * parent)140 extern "C" zx_status_t pci_bus_bind(void* ctx, zx_device_t* parent) {
141 return pci::Bus::Create(parent);
142 }
143