Files
my-os-project2/kernel/pci/pci.c
2025-11-29 23:54:12 +01:00

211 lines
5.1 KiB
C

#include <stdint.h>
#include <stddef.h>
#include "pci/pci.h"
#include "pci/ata/ata.h"
#include "pci/serial/serial.h"
#include "pci/qemu/testdev/testdev.h"
#include "io/io.h"
#include "std/string.h"
#include "util/util.h"
#include "kprintf.h"
static PciDev PCI_DEV_ZERO = {0};
uint32_t pci_read32(PciDev dev, uint32_t field) {
dev.fieldnum = (field & 0xFC) >> 2;
dev.enable = 1;
io_out32(PCI_CFG_ADDR, dev.bits);
return io_in32(PCI_CFG_DATA);
}
uint16_t pci_read16(PciDev dev, uint32_t field) {
dev.fieldnum = (field & 0xFC) >> 2;
dev.enable = 1;
io_out32(PCI_CFG_ADDR, dev.bits);
return io_in16(PCI_CFG_DATA + (field & 0x2));
}
uint8_t pci_read8(PciDev dev, uint32_t field) {
dev.fieldnum = (field & 0xFC) >> 2;
dev.enable = 1;
io_out32(PCI_CFG_ADDR, dev.bits);
return io_in8(PCI_CFG_DATA + (field & 0x3));
}
void pci_write32(PciDev dev, uint32_t field, uint32_t v) {
dev.fieldnum = (field & 0xFC) >> 2;
dev.enable = 1;
io_out32(PCI_CFG_ADDR, dev.bits);
io_out32(PCI_CFG_DATA, v);
}
void pci_write16(PciDev dev, uint32_t field, uint16_t v) {
dev.fieldnum = (field & 0xFC) >> 2;
dev.enable = 1;
io_out32(PCI_CFG_ADDR, dev.bits);
io_out16(PCI_CFG_DATA, v);
}
void pci_write8(PciDev dev, uint32_t field, uint8_t v) {
dev.fieldnum = (field & 0xFC) >> 2;
dev.enable = 1;
io_out32(PCI_CFG_ADDR, dev.bits);
io_out8(PCI_CFG_DATA, v);
}
void pci_readbar(PciDev dev, uint32_t bar, uint32_t *addr, uint32_t *mask) {
*addr = pci_read32(dev, bar);
pci_write32(dev, bar, 0xffffffff);
*mask = pci_read32(dev, bar);
pci_write32(dev, bar, *addr);
}
void pci_getbar(PciDev dev, PciBar *bar, uint32_t barid) {
uint32_t addrlo;
uint32_t masklo;
pci_readbar(dev, barid, &addrlo, &masklo);
if (addrlo & PCI_BAR_IO) {
bar->x.io.iobase = (uint16_t)(addrlo & ~0x3);
bar->size = (uint16_t)(~(masklo & ~0x3) + 1);
bar->flags = (addrlo & 0x3);
} else if (addrlo & PCI_BAR_MEM32) {
bar->x.mem.addr = (uint64_t)(addrlo & ~0xF);
bar->size = ~(masklo & ~0xF) + 1;
bar->flags = (addrlo & 0xF);
} else if (addrlo & PCI_BAR_MEM64) {
uint32_t addrhi;
uint32_t maskhi;
pci_readbar(dev, barid+4, &addrhi, &maskhi);
bar->x.mem.addr = (uint64_t)(((uint64_t)addrhi << 32) | (addrlo & ~0xF));
bar->size = ~(((uint64_t)maskhi << 32) | (masklo & ~0xF)) + 1;
bar->flags = (addrlo & 0xF);
}
}
uint32_t pci_devtype(PciDev dev) {
uint32_t a = pci_read8(dev, PCI_CLASS) << 8;
uint32_t b = pci_read8(dev, PCI_SUBCLASS);
return a | b;
}
uint32_t pci_scndrybus(PciDev dev) {
return pci_read8(dev, PCI_SCNDRY_BUS);
}
uint32_t pci_isend(PciDev dev) {
return !(pci_read8(dev, PCI_HDRTYPE));
}
PciDev pci_scanfn(uint16_t vendorid, uint16_t deviceid, uint32_t bus,
uint32_t device, uint32_t fn, int devtype) {
PciDev dev; memset(&dev, 0, sizeof(dev));
dev.busnum = bus;
dev.devnum = device;
dev.fnnum = fn;
// bridge
if (pci_devtype(dev) == 0x0604) {
pci_scanbus(vendorid, deviceid, pci_scndrybus(dev), devtype);
}
if (devtype == -1 || (uint32_t)devtype == pci_devtype(dev)) {
uint32_t venid = pci_read16(dev, PCI_VENDORID);
uint32_t devid = pci_read16(dev, PCI_DEVICEID);
if (devid == deviceid && venid == vendorid) {
return dev;
}
}
return PCI_DEV_ZERO;
}
PciDev pci_scanbus(uint16_t vendorid, uint16_t deviceid,
uint32_t bus, int devtype) {
for (uint32_t device = 0; device < PCI_DEV_PER_BUS; device++) {
PciDev d = pci_scandev(vendorid, deviceid, bus, device, devtype);
if (d.bits) {
return d;
}
}
return PCI_DEV_ZERO;
}
PciDev pci_scandev(uint16_t vendorid, uint16_t deviceid,
uint32_t bus, uint32_t device, int devtype) {
PciDev dev; memset(&dev, 0, sizeof(dev));
dev.busnum = bus;
dev.devnum = device;
if (pci_read16(dev, PCI_VENDORID) == 0xFFFF) {
return PCI_DEV_ZERO;
}
PciDev d = pci_scanfn(vendorid, deviceid, bus, device, 0, devtype);
if (d.bits) {
return d;
}
if (pci_isend(dev)) {
return PCI_DEV_ZERO;
}
for (uint32_t fn = 1; fn < PCI_FN_PER_DEV; fn++) {
if (pci_read16(dev, PCI_VENDORID) != 0xFFFF) {
d = pci_scanfn(vendorid, deviceid, bus, device, fn, devtype);
if (d.bits) {
return d;
}
}
}
return PCI_DEV_ZERO;
}
PciDev pci_getdev(uint16_t vendorid, uint16_t deviceid, int devtype) {
PciDev d = pci_scanbus(vendorid, deviceid, 0, devtype);
if (d.bits) {
return d;
}
if (pci_isend(PCI_DEV_ZERO)) {
ERR("pci", "pci_getdev() failed for 0x%04x/0x%04x,%d\n", vendorid, deviceid, devtype);
return PCI_DEV_ZERO;
}
for (uint32_t fn = 1; fn < PCI_FN_PER_DEV; fn++) {
PciDev d2; memset(&d2, 0, sizeof(d2));
d2.fnnum = fn;
if (pci_read16(d2, PCI_VENDORID) == 0xFFFF) {
break;
}
d = pci_scanbus(vendorid, deviceid, fn, devtype);
if (d.bits) {
return d;
}
}
return PCI_DEV_ZERO;
}
PciInitFn PCI_INIT_ARRAY[PCI_INIT_ARRAY_MAX] = {
&pci_ata_init,
&pci_serial_init,
&pci_qemu_testdev_init,
};
void pci_init_devs(void) {
for (size_t i = 0; i < LEN(PCI_INIT_ARRAY); i++) {
if (PCI_INIT_ARRAY[i] != NULL)
PCI_INIT_ARRAY[i]();
}
}
void pci_init(void) {
pci_init_devs();
}