// SPDX-License-Identifier: GPL-2.0 /* * pci_found: a minimal PCI driver that prints what boot discovery found * before it ran: the function's IDs and config space size (read during the * scan), the firmware node that described its host bridge (ACPI or Device * Tree), the host bridge window its BAR was placed in, and the interrupt * its pin was routed to. * * It binds only to QEMU's teaching device (1234:11e8, "-device edu"), * which no other driver claims. Build it against the headers of the kernel * you run, then load it: * echo 'obj-m := pci_found.o' > Kbuild * make -C /lib/modules/$(uname -r)/build M=$PWD modules * sudo insmod pci_found.ko && sudo dmesg | grep pci_found */ #include #include #include #include #include #define PCI_VENDOR_ID_QEMU_EDU 0x1234 /* QEMU's "edu" teaching device */ #define PCI_DEVICE_ID_QEMU_EDU 0x11e8 /* Who described the host bridge: the first ancestor with a firmware node. */ static void pci_found_firmware(struct pci_dev *pdev) { struct device *dev = &pci_find_host_bridge(pdev->bus)->dev; for (; dev; dev = dev->parent) { struct fwnode_handle *fwnode = dev_fwnode(dev); if (is_acpi_node(fwnode)) { pci_info(pdev, "host bridge described by ACPI: %pfwf\n", fwnode); return; } if (is_of_node(fwnode)) { pci_info(pdev, "host bridge described by Device Tree: %pfwf\n", fwnode); return; } } pci_info(pdev, "host bridge has no firmware node\n"); } /* Each BAR, and the host bridge window (from _CRS or ranges) that holds it. */ static void pci_found_bars(struct pci_dev *pdev) { struct pci_bus *root = pdev->bus; struct resource *res, *window; unsigned int bar; while (!pci_is_root_bus(root)) root = root->parent; for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { res = pci_resource_n(pdev, bar); if (!resource_size(res)) continue; pci_bus_for_each_resource(root, window) { if (window && resource_contains(window, res)) { pci_info(pdev, "BAR %u %pR is in host bridge window %pR\n", bar, res, window); break; } } } } static int pci_found_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; pci_info(pdev, "found during the scan: [%04x:%04x] class %06x, %d-byte config space\n", pdev->vendor, pdev->device, pdev->class, pdev->cfg_size); pci_found_firmware(pdev); /* x86 looks INTA up in _PRT here; arm64 did, from _PRT or interrupt-map, before probe. */ ret = pcim_enable_device(pdev); if (ret) return ret; pci_found_bars(pdev); if (pdev->pin) pci_info(pdev, "INT%c routed to IRQ %d\n", 'A' + pdev->pin - 1, pdev->irq); return 0; } static const struct pci_device_id pci_found_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_QEMU_EDU, PCI_DEVICE_ID_QEMU_EDU) }, { } }; MODULE_DEVICE_TABLE(pci, pci_found_ids); static struct pci_driver pci_found_driver = { .name = "pci_found", .id_table = pci_found_ids, .probe = pci_found_probe, }; module_pci_driver(pci_found_driver); MODULE_DESCRIPTION("Print what PCI boot discovery found for one device"); MODULE_LICENSE("GPL");