1 // Copyright 2018 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 <acpica/acpi.h>
6 #include <acpica/actypes.h>
7 #include <acpica/acuuid.h>
8 #include <bits/limits.h>
9 #include <ddk/debug.h>
10 #include <fbl/alloc_checker.h>
11 #include <fbl/unique_ptr.h>
12 #include <fbl/vector.h>
13 #include <inttypes.h>
14 #include <region-alloc/region-alloc.h>
15 #include <stdio.h>
16 #include <string.h>
17
18 #include "acpi-private.h"
19 #include "methods.h"
20 #include "pci.h"
21 #include "pciroot.h"
22 #include "resources.h"
23
24 // This file contains the implementation for the code supporting the in-progress userland
25 // pci bus driver.
26
27 static fbl::Vector<pci_mcfg_allocation_t*> mcfg_allocations;
28 const char* kLogTag = "acpi-pci:";
29
30 // These allocators container available regions of physical address space in the memory
31 // map that we should be able to allocate bars from. Different allocators exist for 32
32 // and 64 bit bars so that we can be sure addresses at < 4GB are preserved for 32 bit
33 // bars.
34 RegionAllocator kMmio32Alloc;
35 RegionAllocator kMmio64Alloc;
36 RegionAllocator kIoAlloc;
37
38 struct report_current_resources_ctx {
39 zx_handle_t pci_handle;
40 bool device_is_root_bridge;
41 bool add_pass;
42 };
43
report_current_resources_resource_cb_ex(ACPI_RESOURCE * res,void * _ctx)44 static ACPI_STATUS report_current_resources_resource_cb_ex(ACPI_RESOURCE* res, void* _ctx) {
45 auto* ctx = static_cast<struct report_current_resources_ctx*>(_ctx);
46 zx_status_t status;
47
48 bool is_mmio = false;
49 uint64_t base = 0;
50 uint64_t len = 0;
51 bool add_range = false;
52
53 if (resource_is_memory(res)) {
54 resource_memory_t mem;
55 status = resource_parse_memory(res, &mem);
56 if (status != ZX_OK || mem.minimum != mem.maximum) {
57 return AE_ERROR;
58 }
59
60 is_mmio = true;
61 base = mem.minimum;
62 len = mem.address_length;
63 } else if (resource_is_address(res)) {
64 resource_address_t addr;
65 status = resource_parse_address(res, &addr);
66 if (status != ZX_OK) {
67 return AE_ERROR;
68 }
69
70 if (addr.resource_type == RESOURCE_ADDRESS_MEMORY) {
71 is_mmio = true;
72 } else if (addr.resource_type == RESOURCE_ADDRESS_IO) {
73 is_mmio = false;
74 } else {
75 return AE_OK;
76 }
77
78 if (!addr.min_address_fixed || !addr.max_address_fixed || addr.maximum < addr.minimum) {
79 printf("WARNING: ACPI found bad _CRS address entry\n");
80 return AE_OK;
81 }
82
83 // We compute len from maximum rather than address_length, since some
84 // implementations don't set address_length...
85 base = addr.minimum;
86 len = addr.maximum - base + 1;
87
88 // PCI root bridges report downstream resources via _CRS. Since we're
89 // gathering data on acceptable ranges for PCI to use for MMIO, consider
90 // non-consume-only address resources to be valid for PCI MMIO.
91 if (ctx->device_is_root_bridge && !addr.consumed_only) {
92 add_range = true;
93 }
94 } else if (resource_is_io(res)) {
95 resource_io_t io;
96 status = resource_parse_io(res, &io);
97 if (status != ZX_OK) {
98 return AE_ERROR;
99 }
100
101 if (io.minimum != io.maximum) {
102 printf("WARNING: ACPI found bad _CRS IO entry\n");
103 return AE_OK;
104 }
105
106 is_mmio = false;
107 base = io.minimum;
108 len = io.address_length;
109 } else {
110 return AE_OK;
111 }
112
113 // Ignore empty regions that are reported, and skip any resources that
114 // aren't for the pass we're doing.
115 if (len == 0 || add_range != ctx->add_pass) {
116 return AE_OK;
117 }
118
119 if (add_range && is_mmio && base < 1024 * 1024) {
120 // The PC platform defines many legacy regions below 1MB that we do not
121 // want PCIe to try to map onto.
122 zxlogf(INFO, "Skipping adding MMIO range, due to being below 1MB\n");
123 return AE_OK;
124 }
125
126 // Add/Subtract the [base, len] region we found through ACPI to the allocators
127 // that PCI can use to allocate BARs.
128 RegionAllocator* alloc;
129 if (is_mmio) {
130 if (base + len < UINT32_MAX) {
131 alloc = &kMmio32Alloc;
132 } else {
133 alloc = &kMmio64Alloc;
134 }
135 } else {
136 alloc = &kIoAlloc;
137 }
138
139 zxlogf(TRACE, "ACPI range modification: %sing %s %016lx %016lx\n",
140 add_range ? "add" : "subtract", is_mmio ? "MMIO" : "PIO", base, len);
141 if (add_range) {
142 status = alloc->AddRegion({.base = base, .size = len}, true);
143 } else {
144 status = alloc->SubtractRegion({.base = base, .size = len}, true);
145 }
146
147 if (status != ZX_OK) {
148 if (add_range) {
149 zxlogf(INFO, "Failed to add range: %d\n", status);
150 } else {
151 // If we are subtracting a range and fail, abort. This is bad.
152 return AE_ERROR;
153 }
154 }
155 return AE_OK;
156 }
157
report_current_resources_device_cb_ex(ACPI_HANDLE object,uint32_t nesting_level,void * _ctx,void ** ret)158 static ACPI_STATUS report_current_resources_device_cb_ex(
159 ACPI_HANDLE object, uint32_t nesting_level, void* _ctx, void** ret) {
160
161 ACPI_DEVICE_INFO* info = NULL;
162 ACPI_STATUS status = AcpiGetObjectInfo(object, &info);
163 if (status != AE_OK) {
164 return status;
165 }
166
167 auto* ctx = static_cast<struct report_current_resources_ctx*>(_ctx);
168 ctx->device_is_root_bridge = (info->Flags & ACPI_PCI_ROOT_BRIDGE) != 0;
169
170 ACPI_FREE(info);
171
172 status = AcpiWalkResources(object, (char*)"_CRS", report_current_resources_resource_cb_ex, ctx);
173 if (status == AE_NOT_FOUND || status == AE_OK) {
174 return AE_OK;
175 }
176 return status;
177 }
178
179 /* @brief Report current resources to the kernel PCI driver
180 *
181 * Walks the ACPI namespace and use the reported current resources to inform
182 the kernel PCI interface about what memory it shouldn't use.
183 *
184 * @param root_resource_handle The handle to pass to the kernel when talking
185 * to the PCI driver.
186 *
187 * @return ZX_OK on success
188 */
pci_report_current_resources_ex(zx_handle_t root_resource_handle)189 zx_status_t pci_report_current_resources_ex(zx_handle_t root_resource_handle) {
190 // First we search for resources to add, then we subtract out things that
191 // are being consumed elsewhere. This forces an ordering on the
192 // operations so that it should be consistent, and should protect against
193 // inconistencies in the _CRS methods.
194
195 // Walk the device tree and add to the PCIe IO ranges any resources
196 // "produced" by the PCI root in the ACPI namespace.
197 struct report_current_resources_ctx ctx = {
198 .pci_handle = root_resource_handle,
199 .device_is_root_bridge = false,
200 .add_pass = true,
201 };
202 ACPI_STATUS status = AcpiGetDevices(NULL, report_current_resources_device_cb_ex, &ctx, NULL);
203 if (status != AE_OK) {
204 return ZX_ERR_INTERNAL;
205 }
206
207 // Removes resources we believe are in use by other parts of the platform
208 ctx = (struct report_current_resources_ctx){
209 .pci_handle = root_resource_handle,
210 .device_is_root_bridge = false,
211 .add_pass = false,
212 };
213 status = AcpiGetDevices(NULL, report_current_resources_device_cb_ex, &ctx, NULL);
214 if (status != AE_OK) {
215 return ZX_ERR_INTERNAL;
216 }
217
218 return ZX_OK;
219 }
220
221 // Reads the MCFG table from ACPI and caches it for later calls
222 // to pci_ger_segment_mcfg_alloc()
pci_read_mcfg_table(void)223 static zx_status_t pci_read_mcfg_table(void) {
224 // Systems will have an MCFG table unless they only support legacy PCI.
225 ACPI_TABLE_HEADER* raw_table = NULL;
226 ACPI_STATUS status = AcpiGetTable(const_cast<char*>(ACPI_SIG_MCFG), 1, &raw_table);
227 if (status != AE_OK) {
228 zxlogf(TRACE, "%s no MCFG table found.\n", kLogTag);
229 return ZX_ERR_NOT_FOUND;
230 }
231
232 // The MCFG table contains a variable number of Extended Config tables
233 // hanging off of the end. Typically there will be one, but more
234 // complicated systems may have multiple per PCI Host Bridge. The length in
235 // the header is the overall size, so that is used to calculate how many
236 // ECAMs are included.
237 ACPI_TABLE_MCFG* mcfg = reinterpret_cast<ACPI_TABLE_MCFG*>(raw_table);
238 uintptr_t table_start = reinterpret_cast<uintptr_t>(mcfg) + sizeof(ACPI_TABLE_MCFG);
239 uintptr_t table_end = reinterpret_cast<uintptr_t>(mcfg) + mcfg->Header.Length;
240 size_t table_bytes = table_end - table_start;
241 if (table_bytes % sizeof(pci_mcfg_allocation_t)) {
242 zxlogf(ERROR, "%s MCFG table has invalid size %zu\n", kLogTag, table_bytes);
243 return ZX_ERR_INTERNAL;
244 }
245
246 // Each allocation corresponds to a particular PCI Segment Group. We'll
247 // store them so that the protocol can return them for bus driver instances
248 // later.
249 for (unsigned i = 0; i < table_bytes / sizeof(pci_mcfg_allocation_t); i++) {
250 auto entry = &(reinterpret_cast<pci_mcfg_allocation_t*>(table_start))[i];
251 mcfg_allocations.push_back(entry);
252 zxlogf(TRACE, "%s MCFG allocation %u (Addr = %#lx, Segment = %u, Start = %u, End = %u)\n",
253 kLogTag, i, entry->base_address, entry->segment_group, entry->start_bus_num,
254 entry->end_bus_num);
255 }
256 return ZX_OK;
257 }
258
pci_platform_has_mcfg(void)259 bool pci_platform_has_mcfg(void) {
260 return (mcfg_allocations.size() != 0);
261 }
262
263 // Search the MCFG allocations found earlier for an entry matching a given
264 // segment a host bridge is a part of. Per the PCI Firmware spec v3 table 4-3
265 // note 1, a given segment group will contain only a single mcfg allocation
266 // entry.
pci_get_segment_mcfg_alloc(unsigned segment_group,pci_mcfg_allocation_t * out)267 zx_status_t pci_get_segment_mcfg_alloc(unsigned segment_group, pci_mcfg_allocation_t* out) {
268 for (auto& entry : mcfg_allocations) {
269 if (entry->segment_group == segment_group) {
270 *out = *entry;
271 return ZX_OK;
272 }
273 }
274 return ZX_ERR_NOT_FOUND;
275 }
276
277 static zx_protocol_device_t pciroot_device_ops = {
278 .version = DEVICE_OPS_VERSION,
279 .get_protocol = nullptr,
280 .open = nullptr,
281 .open_at = nullptr,
282 .close = nullptr,
283 .unbind = nullptr,
284 .release = nullptr,
285 .read = nullptr,
286 .write = nullptr,
287 .get_size = nullptr,
288 .ioctl = nullptr,
289 .suspend = nullptr,
290 .resume = nullptr,
291 .rxrpc = nullptr,
292 .message = nullptr,
293 };
294
pci_init_bookkeeping(void)295 zx_status_t pci_init_bookkeeping(void) {
296 zx_status_t status;
297 auto region_pool = RegionAllocator::RegionPool::Create(PAGE_SIZE);
298 if (region_pool == nullptr) {
299 return ZX_ERR_NO_MEMORY;
300 }
301
302 status = kMmio32Alloc.SetRegionPool(region_pool);
303 if (status != ZX_OK) {
304 return status;
305 }
306
307 status = kMmio64Alloc.SetRegionPool(region_pool);
308 if (status != ZX_OK) {
309 return status;
310 }
311
312 status = kIoAlloc.SetRegionPool(region_pool);
313 if (status != ZX_OK) {
314 return status;
315 }
316
317 // MCFG table will only exist with PCIe so 'not found' is not a failure case.
318 status = pci_read_mcfg_table();
319 if (status != ZX_OK && status != ZX_ERR_NOT_FOUND) {
320 zxlogf(ERROR, "%s error attempting to read mcfg table %d\n", kLogTag, status);
321 return status;
322 }
323
324 status = pci_report_current_resources_ex(get_root_resource());
325 if (status != ZX_OK) {
326 zxlogf(ERROR, "%s error attempting to populate PCI allocators %d\n", kLogTag, status);
327 return status;
328 }
329
330 return ZX_OK;
331 }
332
pci_init(zx_device_t * parent,ACPI_HANDLE object,ACPI_DEVICE_INFO * info,publish_acpi_device_ctx_t * ctx)333 zx_status_t pci_init(zx_device_t* parent,
334 ACPI_HANDLE object,
335 ACPI_DEVICE_INFO* info,
336 publish_acpi_device_ctx_t* ctx) {
337 static bool pci_bookkeeping_initialized = false;
338 zx_status_t status = ZX_OK;
339
340 // If it's the first time we've found a root we need to set up our address
341 // space allocators and walk the various ACPI tables.
342 if (!pci_bookkeeping_initialized) {
343 status = pci_init_bookkeeping();
344 if (status != ZX_OK) {
345 return status;
346 }
347 pci_bookkeeping_initialized = true;
348 }
349
350 // Build up a context structure for the PCI Root / Host Bridge we've found.
351 // If we find _BBN / _SEG we will use those, but if we don't we can fall
352 // back on having an ecam from mcfg allocations.
353 fbl::AllocChecker ac;
354 auto dev_ctx = fbl::unique_ptr<pciroot_ctx_t>(new (&ac) pciroot_ctx_t());
355 if (!ac.check()) {
356 zxlogf(ERROR, "failed to allocate pciroot ctx: %d!\n", status);
357 return ZX_ERR_NO_MEMORY;
358 }
359
360 dev_ctx->acpi_object = object;
361 dev_ctx->acpi_device_info = *info;
362 // ACPI names are stored as 4 bytes in a u32
363 memcpy(dev_ctx->name, &info->Name, 4);
364
365 status = acpi_bbn_call(object, &dev_ctx->info.start_bus_num);
366 if (status != ZX_OK && status != ZX_ERR_NOT_FOUND) {
367 zxlogf(TRACE, "%s Unable to read _BBN for '%s' (%d), assuming base bus of 0\n",
368 kLogTag, dev_ctx->name, status);
369
370 // Until we find an ecam we assume this potential legacy pci bus spans
371 // bus 0 to bus 255 in its segment group.
372 dev_ctx->info.end_bus_num = 255;
373 }
374 bool found_bbn = (status == ZX_OK);
375
376 status = acpi_seg_call(object, &dev_ctx->info.segment_group);
377 if (status != ZX_OK) {
378 dev_ctx->info.segment_group = 0;
379 zxlogf(TRACE, "%s Unable to read _SEG for '%s' (%d), assuming segment group 0.\n",
380 kLogTag, dev_ctx->name, status);
381 }
382
383 // If an MCFG is found for the given segment group this root has then we'll
384 // cache it for later pciroot operations and use its information to populate
385 // any fields missing via _BBN / _SEG.
386 auto& pinfo = dev_ctx->info;
387 pci_mcfg_allocation_t mcfg_alloc;
388 status = pci_get_segment_mcfg_alloc(dev_ctx->info.segment_group, &mcfg_alloc);
389 if (status == ZX_OK) {
390 // Do the bus values make sense?
391 if (found_bbn && mcfg_alloc.start_bus_num != pinfo.start_bus_num) {
392 zxlogf(ERROR,
393 "%s: conflicting base bus num for '%s', _BBN reports %u and MCFG reports %u\n",
394 kLogTag, dev_ctx->name, pinfo.start_bus_num, mcfg_alloc.start_bus_num);
395 }
396
397 // Do the segment values make sense?
398 if (pinfo.segment_group != 0 && pinfo.segment_group != mcfg_alloc.segment_group) {
399 zxlogf(ERROR,
400 "%s: conflicting segment group for '%s', _BBN reports %u and MCFG reports %u\n",
401 kLogTag, dev_ctx->name, pinfo.segment_group, mcfg_alloc.segment_group);
402 }
403
404 // Since we have an ecam its metadata will replace anything defined in the ACPI tables.
405 pinfo.segment_group = mcfg_alloc.segment_group;
406 pinfo.start_bus_num = mcfg_alloc.start_bus_num;
407 pinfo.end_bus_num = mcfg_alloc.end_bus_num;
408
409 // The bus driver needs a VMO representing the entire ecam region so it can map it in.
410 // The range from start_bus_num to end_bus_num is inclusive.
411 size_t ecam_size = (pinfo.end_bus_num - pinfo.start_bus_num + 1) * PCIE_ECAM_BYTES_PER_BUS;
412 zx_paddr_t vmo_base = mcfg_alloc.base_address +
413 (pinfo.start_bus_num * PCIE_ECAM_BYTES_PER_BUS);
414 status = zx_vmo_create_physical(get_root_resource(), vmo_base, ecam_size, &pinfo.ecam_vmo);
415 if (status != ZX_OK) {
416 zxlogf(ERROR, "couldn't create VMO for ecam, mmio cfg will not work: %d!\n", status);
417 return status;
418 }
419 }
420
421 if (zxlog_level_enabled(TRACE)) {
422 printf("%s %s { acpi_obj(%p), bus range: %u:%u, segment: %u }\n", kLogTag,
423 dev_ctx->name, dev_ctx->acpi_object, pinfo.start_bus_num, pinfo.end_bus_num,
424 pinfo.segment_group);
425 if (pinfo.ecam_vmo != ZX_HANDLE_INVALID) {
426 printf("%s ecam base %#" PRIxPTR "\n", kLogTag, mcfg_alloc.base_address);
427 }
428 }
429
430 device_add_args_t args = {
431 .version = DEVICE_ADD_ARGS_VERSION,
432 .name = dev_ctx->name,
433 .ctx = dev_ctx.get(),
434 .ops = &pciroot_device_ops,
435 .props = nullptr,
436 .prop_count = 0,
437 .proto_id = ZX_PROTOCOL_PCIROOT,
438 .proto_ops = get_pciroot_ops(),
439 .proxy_args = nullptr,
440 .flags = 0,
441 };
442
443 // These are cached here to work around dev_ctx potentially going out of scope
444 // after device_add in the event that unbind/release are called from the DDK. See
445 // the below TODO for more information.
446 char name[5];
447 uint8_t last_pci_bbn = dev_ctx->info.start_bus_num;
448 memcpy(name, dev_ctx->name, sizeof(name));
449
450 status = device_add(parent, &args, &dev_ctx->zxdev);
451 if (status != ZX_OK) {
452 zxlogf(ERROR, "%s failed to add pciroot device for '%s': %d\n",
453 kLogTag, dev_ctx->name, status);
454 } else {
455 // devmgr owns the ctx pointer now so release it from the uptr
456 __UNUSED auto p = dev_ctx.release();
457
458 // TODO(cja): these support the legacy-ish ACPI nhlt table handling that will need to be
459 // updated in the future.
460 ctx->found_pci = true;
461 ctx->last_pci = last_pci_bbn;
462 zxlogf(INFO, "%s published pciroot '%s'\n", kLogTag, name);
463 }
464
465 return status;
466 }
467