pci: Split low-level pci code from higher-level 'struct pci_device' code
Split pci.c into pci.c and pcidevice.c. The low-level code that interacts directly with the PCI devices remains in pci.c, while functions dealing with the higher level pci_device cache move to pcidevice.c. Only pci.c is needed in 16bit mode. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
62ff9d5f64
commit
4d8510cdcc
2
Makefile
2
Makefile
|
@ -38,7 +38,7 @@ SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \
|
|||
SRC16=$(SRCBOTH)
|
||||
SRC32FLAT=$(SRCBOTH) post.c e820map.c malloc.c romfile.c x86.c optionroms.c \
|
||||
pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c tcgbios.c sha1.c \
|
||||
hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \
|
||||
hw/pcidevice.c hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \
|
||||
fw/coreboot.c fw/lzmadecode.c fw/multiboot.c fw/csm.c fw/biostables.c \
|
||||
fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \
|
||||
fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c \
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "config.h" // CONFIG_*
|
||||
#include "fw/paravirt.h" // qemu_cfg_show_boot_menu
|
||||
#include "hw/pci.h" // pci_bdf_to_*
|
||||
#include "hw/pcidevice.h" // struct pci_device
|
||||
#include "hw/rtc.h" // rtc_read
|
||||
#include "hw/usb.h" // struct usbdevice_s
|
||||
#include "list.h" // hlist_node
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "config.h" // CONFIG_*
|
||||
#include "dev-q35.h"
|
||||
#include "dev-piix.h"
|
||||
#include "hw/pci.h" // pci_find_init_device
|
||||
#include "hw/pcidevice.h" // pci_find_init_device
|
||||
#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
|
||||
#include "hw/pci_regs.h" // PCI_INTERRUPT_LINE
|
||||
#include "malloc.h" // free
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "byteorder.h" // be32_to_cpu
|
||||
#include "config.h" // CONFIG_*
|
||||
#include "e820map.h" // e820_add
|
||||
#include "hw/pci.h" // pci_probe_devices
|
||||
#include "hw/pcidevice.h" // pci_probe_devices
|
||||
#include "lzmadecode.h" // LzmaDecode
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include "config.h" // CONFIG_*
|
||||
#include "e820map.h" // e820_add
|
||||
#include "farptr.h" // MAKE_FLATPTR
|
||||
#include "hw/pci.h" // pci_probe_devices
|
||||
#include "hw/pci.h" // pci_to_bdf
|
||||
#include "hw/pcidevice.h" // pci_probe_devices
|
||||
#include "hw/pic.h" // pic_irqmask_read
|
||||
#include "malloc.h" // malloc_csm_preinit
|
||||
#include "memmap.h" // SYMBOL
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
// This file may be distributed under the terms of the GNU LGPLv3 license.
|
||||
|
||||
#include "config.h" // CONFIG_*
|
||||
#include "hw/pci.h"
|
||||
#include "hw/pci_regs.h"
|
||||
#include "hw/pci.h" // pci_bdf_to_bus
|
||||
#include "hw/pcidevice.h" // foreachpci
|
||||
#include "hw/pci_regs.h" // PCI_INTERRUPT_PIN
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "romfile.h" // romfile_loadint
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
#include "byteorder.h" // be32_to_cpu
|
||||
#include "config.h" // CONFIG_QEMU
|
||||
#include "e820map.h" // e820_add
|
||||
#include "hw/pci.h" // create_pirtable
|
||||
#include "hw/pci.h" // pci_config_readw
|
||||
#include "hw/pcidevice.h" // pci_probe_devices
|
||||
#include "hw/pci_regs.h" // PCI_DEVICE_ID
|
||||
#include "hw/rtc.h" // CMOS_*
|
||||
#include "malloc.h" // malloc_tmp
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "e820map.h" // e820_add
|
||||
#include "hw/ata.h" // PORT_ATA1_CMD_BASE
|
||||
#include "hw/pci.h" // pci_config_readl
|
||||
#include "hw/pcidevice.h" // pci_probe_devices
|
||||
#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
|
||||
#include "hw/pci_regs.h" // PCI_COMMAND
|
||||
#include "list.h" // struct hlist_node
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "dev-q35.h"
|
||||
#include "dev-piix.h"
|
||||
#include "hw/pci.h" // pci_config_writel
|
||||
#include "hw/pcidevice.h" // pci_find_device
|
||||
#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
|
||||
#include "hw/pci_regs.h" // PCI_DEVICE_ID
|
||||
#include "output.h" // dprintf
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
#include "blockcmd.h" // CDB_CMD_READ_10
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pci.h" // pci_config_readb
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
|
||||
#include "pci_regs.h" // PCI_INTERRUPT_LINE
|
||||
#include "stacks.h" // yield
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
#include "byteorder.h" // be16_to_cpu
|
||||
#include "malloc.h" // malloc_fseg
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pci.h" // pci_config_readb
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
|
||||
#include "pci_regs.h" // PCI_INTERRUPT_LINE
|
||||
#include "pic.h" // enable_hwirq
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "fw/paravirt.h" // runningOnQEMU
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_DEVICE_ID
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "std/disk.h" // DISK_RET_SUCCESS
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "config.h" // CONFIG_FLOPPY
|
||||
#include "malloc.h" // malloc_fseg
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // pci_to_bdf
|
||||
#include "pcidevice.h" // pci_find_class
|
||||
#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA
|
||||
#include "pic.h" // pic_eoi1
|
||||
#include "romfile.h" // romfile_loadint
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "fw/paravirt.h" // runningOnQEMU
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "std/disk.h" // DISK_RET_SUCCESS
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
#include "config.h" // CONFIG_*
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pci.h" // pci_config_readl
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_DEVICE_ID_XXX
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "stacks.h" // yield
|
||||
|
|
213
src/hw/pci.c
213
src/hw/pci.c
|
@ -5,16 +5,16 @@
|
|||
//
|
||||
// This file may be distributed under the terms of the GNU LGPLv3 license.
|
||||
|
||||
#include "malloc.h" // malloc_tmp
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // pci_config_writel
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "romfile.h" // romfile_loadint
|
||||
#include "stacks.h" // wait_preempt
|
||||
#include "string.h" // memset
|
||||
#include "util.h" // udelay
|
||||
#include "x86.h" // outl
|
||||
|
||||
#define PORT_PCI_CMD 0x0cf8
|
||||
#define PORT_PCI_REBOOT 0x0cf9
|
||||
#define PORT_PCI_DATA 0x0cfc
|
||||
|
||||
void pci_config_writel(u16 bdf, u32 addr, u32 val)
|
||||
{
|
||||
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
|
||||
|
@ -87,9 +87,6 @@ pci_next(int bdf, int bus)
|
|||
}
|
||||
}
|
||||
|
||||
struct hlist_head PCIDevices VARVERIFY32INIT;
|
||||
int MaxPCIBus VARFSEG;
|
||||
|
||||
// Check if PCI is available at all
|
||||
int
|
||||
pci_probe_host(void)
|
||||
|
@ -102,208 +99,6 @@ pci_probe_host(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Find all PCI devices and populate PCIDevices linked list.
|
||||
void
|
||||
pci_probe_devices(void)
|
||||
{
|
||||
dprintf(3, "PCI probe\n");
|
||||
struct pci_device *busdevs[256];
|
||||
memset(busdevs, 0, sizeof(busdevs));
|
||||
struct hlist_node **pprev = &PCIDevices.first;
|
||||
int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
|
||||
int bus = -1, lastbus = 0, rootbuses = 0, count=0;
|
||||
while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
|
||||
bus++;
|
||||
int bdf;
|
||||
foreachbdf(bdf, bus) {
|
||||
// Create new pci_device struct and add to list.
|
||||
struct pci_device *dev = malloc_tmp(sizeof(*dev));
|
||||
if (!dev) {
|
||||
warn_noalloc();
|
||||
return;
|
||||
}
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
hlist_add(&dev->node, pprev);
|
||||
pprev = &dev->node.next;
|
||||
count++;
|
||||
|
||||
// Find parent device.
|
||||
int rootbus;
|
||||
struct pci_device *parent = busdevs[bus];
|
||||
if (!parent) {
|
||||
if (bus != lastbus)
|
||||
rootbuses++;
|
||||
lastbus = bus;
|
||||
rootbus = rootbuses;
|
||||
if (bus > MaxPCIBus)
|
||||
MaxPCIBus = bus;
|
||||
} else {
|
||||
rootbus = parent->rootbus;
|
||||
}
|
||||
|
||||
// Populate pci_device info.
|
||||
dev->bdf = bdf;
|
||||
dev->parent = parent;
|
||||
dev->rootbus = rootbus;
|
||||
u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
|
||||
dev->vendor = vendev & 0xffff;
|
||||
dev->device = vendev >> 16;
|
||||
u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
|
||||
dev->class = classrev >> 16;
|
||||
dev->prog_if = classrev >> 8;
|
||||
dev->revision = classrev & 0xff;
|
||||
dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
|
||||
u8 v = dev->header_type & 0x7f;
|
||||
if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
|
||||
u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
|
||||
dev->secondary_bus = secbus;
|
||||
if (secbus > bus && !busdevs[secbus])
|
||||
busdevs[secbus] = dev;
|
||||
if (secbus > MaxPCIBus)
|
||||
MaxPCIBus = secbus;
|
||||
}
|
||||
dprintf(4, "PCI device %pP (vd=%04x:%04x c=%04x)\n"
|
||||
, dev, dev->vendor, dev->device, dev->class);
|
||||
}
|
||||
}
|
||||
dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
|
||||
}
|
||||
|
||||
// Search for a device with the specified vendor and device ids.
|
||||
struct pci_device *
|
||||
pci_find_device(u16 vendid, u16 devid)
|
||||
{
|
||||
struct pci_device *pci;
|
||||
foreachpci(pci) {
|
||||
if (pci->vendor == vendid && pci->device == devid)
|
||||
return pci;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Search for a device with the specified class id.
|
||||
struct pci_device *
|
||||
pci_find_class(u16 classid)
|
||||
{
|
||||
struct pci_device *pci;
|
||||
foreachpci(pci) {
|
||||
if (pci->class == classid)
|
||||
return pci;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int pci_init_device(const struct pci_device_id *ids
|
||||
, struct pci_device *pci, void *arg)
|
||||
{
|
||||
while (ids->vendid || ids->class_mask) {
|
||||
if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
|
||||
(ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
|
||||
!((ids->class ^ pci->class) & ids->class_mask)) {
|
||||
if (ids->func)
|
||||
ids->func(pci, arg);
|
||||
return 0;
|
||||
}
|
||||
ids++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct pci_device *
|
||||
pci_find_init_device(const struct pci_device_id *ids, void *arg)
|
||||
{
|
||||
struct pci_device *pci;
|
||||
foreachpci(pci) {
|
||||
if (pci_init_device(ids, pci, arg) == 0)
|
||||
return pci;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap)
|
||||
{
|
||||
int i;
|
||||
u16 status = pci_config_readw(pci->bdf, PCI_STATUS);
|
||||
|
||||
if (!(status & PCI_STATUS_CAP_LIST))
|
||||
return 0;
|
||||
|
||||
if (cap == 0) {
|
||||
/* find first */
|
||||
cap = pci_config_readb(pci->bdf, PCI_CAPABILITY_LIST);
|
||||
} else {
|
||||
/* find next */
|
||||
cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT);
|
||||
}
|
||||
for (i = 0; cap && i <= 0xff; i++) {
|
||||
if (pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_ID) == cap_id)
|
||||
return cap;
|
||||
cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Enable PCI bus-mastering (ie, DMA) support on a pci device
|
||||
void
|
||||
pci_enable_busmaster(struct pci_device *pci)
|
||||
{
|
||||
ASSERT32FLAT();
|
||||
wait_preempt();
|
||||
pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
|
||||
pci->have_driver = 1;
|
||||
}
|
||||
|
||||
// Verify an IO bar and return it to the caller
|
||||
u16
|
||||
pci_enable_iobar(struct pci_device *pci, u32 addr)
|
||||
{
|
||||
ASSERT32FLAT();
|
||||
wait_preempt();
|
||||
u32 bar = pci_config_readl(pci->bdf, addr);
|
||||
if (!(bar & PCI_BASE_ADDRESS_SPACE_IO)) {
|
||||
warn_internalerror();
|
||||
return 0;
|
||||
}
|
||||
bar &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
if (bar == 0 || bar > 0xffff) {
|
||||
warn_internalerror();
|
||||
return 0;
|
||||
}
|
||||
pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_IO);
|
||||
pci->have_driver = 1;
|
||||
return bar;
|
||||
}
|
||||
|
||||
// Verify a memory bar and return it to the caller
|
||||
void *
|
||||
pci_enable_membar(struct pci_device *pci, u32 addr)
|
||||
{
|
||||
ASSERT32FLAT();
|
||||
wait_preempt();
|
||||
u32 bar = pci_config_readl(pci->bdf, addr);
|
||||
if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
warn_internalerror();
|
||||
return NULL;
|
||||
}
|
||||
if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
u32 high = pci_config_readl(pci->bdf, addr+4);
|
||||
if (high) {
|
||||
dprintf(1, "Can not map memory bar over 4Gig\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
bar &= PCI_BASE_ADDRESS_MEM_MASK;
|
||||
if (bar + 4*1024*1024 < 20*1024*1024) {
|
||||
// Bar doesn't look valid (it is in last 4M or first 16M)
|
||||
warn_internalerror();
|
||||
return NULL;
|
||||
}
|
||||
pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
|
||||
pci->have_driver = 1;
|
||||
return (void*)bar;
|
||||
}
|
||||
|
||||
void
|
||||
pci_reboot(void)
|
||||
{
|
||||
|
|
89
src/hw/pci.h
89
src/hw/pci.h
|
@ -2,11 +2,6 @@
|
|||
#define __PCI_H
|
||||
|
||||
#include "types.h" // u32
|
||||
#include "list.h" // hlist_node
|
||||
|
||||
#define PORT_PCI_CMD 0x0cf8
|
||||
#define PORT_PCI_REBOOT 0x0cf9
|
||||
#define PORT_PCI_DATA 0x0cfc
|
||||
|
||||
static inline u8 pci_bdf_to_bus(u16 bdf) {
|
||||
return bdf >> 8;
|
||||
|
@ -30,6 +25,11 @@ static inline u16 pci_bus_devfn_to_bdf(int bus, u16 devfn) {
|
|||
return (bus << 8) | devfn;
|
||||
}
|
||||
|
||||
#define foreachbdf(BDF, BUS) \
|
||||
for (BDF=pci_next(pci_bus_devfn_to_bdf((BUS), 0)-1, (BUS)) \
|
||||
; BDF >= 0 \
|
||||
; BDF=pci_next(BDF, (BUS)))
|
||||
|
||||
void pci_config_writel(u16 bdf, u32 addr, u32 val);
|
||||
void pci_config_writew(u16 bdf, u32 addr, u16 val);
|
||||
void pci_config_writeb(u16 bdf, u32 addr, u8 val);
|
||||
|
@ -37,83 +37,8 @@ u32 pci_config_readl(u16 bdf, u32 addr);
|
|||
u16 pci_config_readw(u16 bdf, u32 addr);
|
||||
u8 pci_config_readb(u16 bdf, u32 addr);
|
||||
void pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on);
|
||||
|
||||
struct pci_device *pci_find_device(u16 vendid, u16 devid);
|
||||
struct pci_device *pci_find_class(u16 classid);
|
||||
|
||||
struct pci_device {
|
||||
u16 bdf;
|
||||
u8 rootbus;
|
||||
struct hlist_node node;
|
||||
struct pci_device *parent;
|
||||
|
||||
// Configuration space device information
|
||||
u16 vendor, device;
|
||||
u16 class;
|
||||
u8 prog_if, revision;
|
||||
u8 header_type;
|
||||
u8 secondary_bus;
|
||||
|
||||
// Local information on device.
|
||||
int have_driver;
|
||||
};
|
||||
extern struct hlist_head PCIDevices;
|
||||
extern int MaxPCIBus;
|
||||
int pci_probe_host(void);
|
||||
void pci_probe_devices(void);
|
||||
static inline u32 pci_classprog(struct pci_device *pci) {
|
||||
return (pci->class << 8) | pci->prog_if;
|
||||
}
|
||||
|
||||
#define foreachpci(PCI) \
|
||||
hlist_for_each_entry(PCI, &PCIDevices, node)
|
||||
|
||||
int pci_next(int bdf, int bus);
|
||||
#define foreachbdf(BDF, BUS) \
|
||||
for (BDF=pci_next(pci_bus_devfn_to_bdf((BUS), 0)-1, (BUS)) \
|
||||
; BDF >= 0 \
|
||||
; BDF=pci_next(BDF, (BUS)))
|
||||
|
||||
#define PCI_ANY_ID (~0)
|
||||
struct pci_device_id {
|
||||
u32 vendid;
|
||||
u32 devid;
|
||||
u32 class;
|
||||
u32 class_mask;
|
||||
void (*func)(struct pci_device *pci, void *arg);
|
||||
};
|
||||
|
||||
#define PCI_DEVICE(vendor_id, device_id, init_func) \
|
||||
{ \
|
||||
.vendid = (vendor_id), \
|
||||
.devid = (device_id), \
|
||||
.class = PCI_ANY_ID, \
|
||||
.class_mask = 0, \
|
||||
.func = (init_func) \
|
||||
}
|
||||
|
||||
#define PCI_DEVICE_CLASS(vendor_id, device_id, class_code, init_func) \
|
||||
{ \
|
||||
.vendid = (vendor_id), \
|
||||
.devid = (device_id), \
|
||||
.class = (class_code), \
|
||||
.class_mask = ~0, \
|
||||
.func = (init_func) \
|
||||
}
|
||||
|
||||
#define PCI_DEVICE_END \
|
||||
{ \
|
||||
.vendid = 0, \
|
||||
}
|
||||
|
||||
int pci_init_device(const struct pci_device_id *ids
|
||||
, struct pci_device *pci, void *arg);
|
||||
struct pci_device *pci_find_init_device(const struct pci_device_id *ids
|
||||
, void *arg);
|
||||
u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap);
|
||||
void pci_enable_busmaster(struct pci_device *pci);
|
||||
u16 pci_enable_iobar(struct pci_device *pci, u32 addr);
|
||||
void *pci_enable_membar(struct pci_device *pci, u32 addr);
|
||||
int pci_probe_host(void);
|
||||
void pci_reboot(void);
|
||||
|
||||
#endif
|
||||
#endif // pci.h
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
// Code to maintain and access the pci_device cache
|
||||
//
|
||||
// Copyright (C) 2008-2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU LGPLv3 license.
|
||||
|
||||
#include "malloc.h" // malloc_tmp
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // pci_config_writel
|
||||
#include "pcidevice.h" // pci_probe_devices
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "romfile.h" // romfile_loadint
|
||||
#include "stacks.h" // wait_preempt
|
||||
#include "string.h" // memset
|
||||
|
||||
struct hlist_head PCIDevices VARVERIFY32INIT;
|
||||
int MaxPCIBus VARFSEG;
|
||||
|
||||
// Find all PCI devices and populate PCIDevices linked list.
|
||||
void
|
||||
pci_probe_devices(void)
|
||||
{
|
||||
dprintf(3, "PCI probe\n");
|
||||
struct pci_device *busdevs[256];
|
||||
memset(busdevs, 0, sizeof(busdevs));
|
||||
struct hlist_node **pprev = &PCIDevices.first;
|
||||
int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
|
||||
int bus = -1, lastbus = 0, rootbuses = 0, count=0;
|
||||
while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
|
||||
bus++;
|
||||
int bdf;
|
||||
foreachbdf(bdf, bus) {
|
||||
// Create new pci_device struct and add to list.
|
||||
struct pci_device *dev = malloc_tmp(sizeof(*dev));
|
||||
if (!dev) {
|
||||
warn_noalloc();
|
||||
return;
|
||||
}
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
hlist_add(&dev->node, pprev);
|
||||
pprev = &dev->node.next;
|
||||
count++;
|
||||
|
||||
// Find parent device.
|
||||
int rootbus;
|
||||
struct pci_device *parent = busdevs[bus];
|
||||
if (!parent) {
|
||||
if (bus != lastbus)
|
||||
rootbuses++;
|
||||
lastbus = bus;
|
||||
rootbus = rootbuses;
|
||||
if (bus > MaxPCIBus)
|
||||
MaxPCIBus = bus;
|
||||
} else {
|
||||
rootbus = parent->rootbus;
|
||||
}
|
||||
|
||||
// Populate pci_device info.
|
||||
dev->bdf = bdf;
|
||||
dev->parent = parent;
|
||||
dev->rootbus = rootbus;
|
||||
u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
|
||||
dev->vendor = vendev & 0xffff;
|
||||
dev->device = vendev >> 16;
|
||||
u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
|
||||
dev->class = classrev >> 16;
|
||||
dev->prog_if = classrev >> 8;
|
||||
dev->revision = classrev & 0xff;
|
||||
dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
|
||||
u8 v = dev->header_type & 0x7f;
|
||||
if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
|
||||
u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
|
||||
dev->secondary_bus = secbus;
|
||||
if (secbus > bus && !busdevs[secbus])
|
||||
busdevs[secbus] = dev;
|
||||
if (secbus > MaxPCIBus)
|
||||
MaxPCIBus = secbus;
|
||||
}
|
||||
dprintf(4, "PCI device %pP (vd=%04x:%04x c=%04x)\n"
|
||||
, dev, dev->vendor, dev->device, dev->class);
|
||||
}
|
||||
}
|
||||
dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
|
||||
}
|
||||
|
||||
// Search for a device with the specified vendor and device ids.
|
||||
struct pci_device *
|
||||
pci_find_device(u16 vendid, u16 devid)
|
||||
{
|
||||
struct pci_device *pci;
|
||||
foreachpci(pci) {
|
||||
if (pci->vendor == vendid && pci->device == devid)
|
||||
return pci;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Search for a device with the specified class id.
|
||||
struct pci_device *
|
||||
pci_find_class(u16 classid)
|
||||
{
|
||||
struct pci_device *pci;
|
||||
foreachpci(pci) {
|
||||
if (pci->class == classid)
|
||||
return pci;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int pci_init_device(const struct pci_device_id *ids
|
||||
, struct pci_device *pci, void *arg)
|
||||
{
|
||||
while (ids->vendid || ids->class_mask) {
|
||||
if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
|
||||
(ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
|
||||
!((ids->class ^ pci->class) & ids->class_mask)) {
|
||||
if (ids->func)
|
||||
ids->func(pci, arg);
|
||||
return 0;
|
||||
}
|
||||
ids++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct pci_device *
|
||||
pci_find_init_device(const struct pci_device_id *ids, void *arg)
|
||||
{
|
||||
struct pci_device *pci;
|
||||
foreachpci(pci) {
|
||||
if (pci_init_device(ids, pci, arg) == 0)
|
||||
return pci;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap)
|
||||
{
|
||||
int i;
|
||||
u16 status = pci_config_readw(pci->bdf, PCI_STATUS);
|
||||
|
||||
if (!(status & PCI_STATUS_CAP_LIST))
|
||||
return 0;
|
||||
|
||||
if (cap == 0) {
|
||||
/* find first */
|
||||
cap = pci_config_readb(pci->bdf, PCI_CAPABILITY_LIST);
|
||||
} else {
|
||||
/* find next */
|
||||
cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT);
|
||||
}
|
||||
for (i = 0; cap && i <= 0xff; i++) {
|
||||
if (pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_ID) == cap_id)
|
||||
return cap;
|
||||
cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Enable PCI bus-mastering (ie, DMA) support on a pci device
|
||||
void
|
||||
pci_enable_busmaster(struct pci_device *pci)
|
||||
{
|
||||
wait_preempt();
|
||||
pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
|
||||
pci->have_driver = 1;
|
||||
}
|
||||
|
||||
// Verify an IO bar and return it to the caller
|
||||
u16
|
||||
pci_enable_iobar(struct pci_device *pci, u32 addr)
|
||||
{
|
||||
wait_preempt();
|
||||
u32 bar = pci_config_readl(pci->bdf, addr);
|
||||
if (!(bar & PCI_BASE_ADDRESS_SPACE_IO)) {
|
||||
warn_internalerror();
|
||||
return 0;
|
||||
}
|
||||
bar &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
if (bar == 0 || bar > 0xffff) {
|
||||
warn_internalerror();
|
||||
return 0;
|
||||
}
|
||||
pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_IO);
|
||||
pci->have_driver = 1;
|
||||
return bar;
|
||||
}
|
||||
|
||||
// Verify a memory bar and return it to the caller
|
||||
void *
|
||||
pci_enable_membar(struct pci_device *pci, u32 addr)
|
||||
{
|
||||
wait_preempt();
|
||||
u32 bar = pci_config_readl(pci->bdf, addr);
|
||||
if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
warn_internalerror();
|
||||
return NULL;
|
||||
}
|
||||
if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
u32 high = pci_config_readl(pci->bdf, addr+4);
|
||||
if (high) {
|
||||
dprintf(1, "Can not map memory bar over 4Gig\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
bar &= PCI_BASE_ADDRESS_MEM_MASK;
|
||||
if (bar + 4*1024*1024 < 20*1024*1024) {
|
||||
// Bar doesn't look valid (it is in last 4M or first 16M)
|
||||
warn_internalerror();
|
||||
return NULL;
|
||||
}
|
||||
pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
|
||||
pci->have_driver = 1;
|
||||
return (void*)bar;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef __PCIDEVICE_H
|
||||
#define __PCIDEVICE_H
|
||||
|
||||
#include "types.h" // u32
|
||||
#include "list.h" // hlist_node
|
||||
|
||||
struct pci_device {
|
||||
u16 bdf;
|
||||
u8 rootbus;
|
||||
struct hlist_node node;
|
||||
struct pci_device *parent;
|
||||
|
||||
// Configuration space device information
|
||||
u16 vendor, device;
|
||||
u16 class;
|
||||
u8 prog_if, revision;
|
||||
u8 header_type;
|
||||
u8 secondary_bus;
|
||||
|
||||
// Local information on device.
|
||||
int have_driver;
|
||||
};
|
||||
extern struct hlist_head PCIDevices;
|
||||
extern int MaxPCIBus;
|
||||
|
||||
static inline u32 pci_classprog(struct pci_device *pci) {
|
||||
return (pci->class << 8) | pci->prog_if;
|
||||
}
|
||||
|
||||
#define foreachpci(PCI) \
|
||||
hlist_for_each_entry(PCI, &PCIDevices, node)
|
||||
|
||||
#define PCI_ANY_ID (~0)
|
||||
struct pci_device_id {
|
||||
u32 vendid;
|
||||
u32 devid;
|
||||
u32 class;
|
||||
u32 class_mask;
|
||||
void (*func)(struct pci_device *pci, void *arg);
|
||||
};
|
||||
|
||||
#define PCI_DEVICE(vendor_id, device_id, init_func) \
|
||||
{ \
|
||||
.vendid = (vendor_id), \
|
||||
.devid = (device_id), \
|
||||
.class = PCI_ANY_ID, \
|
||||
.class_mask = 0, \
|
||||
.func = (init_func) \
|
||||
}
|
||||
|
||||
#define PCI_DEVICE_CLASS(vendor_id, device_id, class_code, init_func) \
|
||||
{ \
|
||||
.vendid = (vendor_id), \
|
||||
.devid = (device_id), \
|
||||
.class = (class_code), \
|
||||
.class_mask = ~0, \
|
||||
.func = (init_func) \
|
||||
}
|
||||
|
||||
#define PCI_DEVICE_END \
|
||||
{ \
|
||||
.vendid = 0, \
|
||||
}
|
||||
|
||||
void pci_probe_devices(void);
|
||||
struct pci_device *pci_find_device(u16 vendid, u16 devid);
|
||||
struct pci_device *pci_find_class(u16 classid);
|
||||
int pci_init_device(const struct pci_device_id *ids
|
||||
, struct pci_device *pci, void *arg);
|
||||
struct pci_device *pci_find_init_device(const struct pci_device_id *ids
|
||||
, void *arg);
|
||||
u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap);
|
||||
void pci_enable_busmaster(struct pci_device *pci);
|
||||
u16 pci_enable_iobar(struct pci_device *pci, u32 addr);
|
||||
void *pci_enable_membar(struct pci_device *pci, u32 addr);
|
||||
|
||||
#endif // pcidevice.h
|
|
@ -13,7 +13,7 @@
|
|||
#include "malloc.h" // free
|
||||
#include "memmap.h" // PAGE_SHIFT, virt_to_phys
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_DEVICE_ID_VMWARE_PVSCSI
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "pvscsi.h" // pvscsi_setup
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "block.h" // struct drive_s
|
||||
#include "malloc.h" // malloc_fseg
|
||||
#include "output.h" // znprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_CLASS_SYSTEM_SDHCI
|
||||
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
|
||||
#include "romfile.h" // romfile_findprefix
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "output.h" // dprintf
|
||||
#include "malloc.h" // free
|
||||
#include "memmap.h" // PAGE_SIZE
|
||||
#include "pci.h" // pci_bdf_to_bus
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
|
||||
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
|
||||
#include "string.h" // memset
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "malloc.h" // free
|
||||
#include "memmap.h" // PAGE_SIZE
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // pci_bdf_to_bus
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_OHCI
|
||||
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
|
||||
#include "string.h" // memset
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include "config.h" // CONFIG_*
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // pci_bdf_to_bus
|
||||
#include "pci.h" // pci_config_writew
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
|
||||
#include "pci_regs.h" // PCI_BASE_ADDRESS_4
|
||||
#include "string.h" // memset
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "malloc.h" // memalign_low
|
||||
#include "memmap.h" // PAGE_SIZE
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // pci_bdf_to_bus
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_XHCI
|
||||
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
|
||||
#include "string.h" // memcpy
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "block.h" // struct drive_s
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "std/disk.h" // DISK_RET_SUCCESS
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // pci_config_readl
|
||||
#include "pcidevice.h" // pci_find_capability
|
||||
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
|
||||
#include "string.h" // memset
|
||||
#include "virtio-pci.h"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "config.h" // CONFIG_*
|
||||
#include "malloc.h" // free
|
||||
#include "output.h" // dprintf
|
||||
#include "pci.h" // foreachpci
|
||||
#include "pcidevice.h" // foreachpci
|
||||
#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
|
||||
#include "pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "std/disk.h" // DISK_RET_SUCCESS
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include "bregs.h" // struct bregs
|
||||
#include "config.h" // CONFIG_*
|
||||
#include "farptr.h" // FLATPTR_TO_SEG
|
||||
#include "hw/pci.h" // foreachpci
|
||||
#include "hw/pci.h" // pci_config_readl
|
||||
#include "hw/pcidevice.h" // foreachpci
|
||||
#include "hw/pci_ids.h" // PCI_CLASS_DISPLAY_VGA
|
||||
#include "hw/pci_regs.h" // PCI_ROM_ADDRESS
|
||||
#include "malloc.h" // rom_confirm
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "config.h" // CONFIG_*
|
||||
#include "biosvar.h" // GET_GLOBAL
|
||||
#include "hw/pci.h" // pci_bdf_to_bus
|
||||
#include "hw/pcidevice.h" // pci_device
|
||||
#include "hw/serialio.h" // serial_debug_putc
|
||||
#include "malloc.h" // malloc_tmp
|
||||
#include "output.h" // dprintf
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "biosvar.h" // GET_GLOBAL
|
||||
#include "bregs.h" // struct bregs
|
||||
#include "hw/pci.h" // pci_config_readl
|
||||
#include "hw/pcidevice.h" // MaxPCIBus
|
||||
#include "hw/pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "output.h" // dprintf
|
||||
#include "std/pirtable.h" // struct pir_header
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
#include "biosvar.h" // GET_GLOBAL
|
||||
#include "bregs.h" // set_code_invalid
|
||||
#include "config.h" // CONFIG_*
|
||||
#include "hw/pci.h" // pci_find_device
|
||||
#include "hw/pci.h" // pci_config_readb
|
||||
#include "hw/pcidevice.h" // pci_find_device
|
||||
#include "hw/pci_ids.h" // PCI_VENDOR_ID_VIA
|
||||
#include "hw/pci_regs.h" // PCI_VENDOR_ID
|
||||
#include "output.h" // dprintf
|
||||
|
|
Loading…
Reference in New Issue