119 lines
4.9 KiB
C
119 lines
4.9 KiB
C
#include "pci.h"
|
|
#include "qemu_vga.h"
|
|
#include "rtl8139.h"
|
|
|
|
/* int32_t pci_get_device(uint8_t bus, uint8_t device, uint8_t func, pci_device_t* ret)
|
|
* @input: bus, device, func - index of the PCI slot
|
|
* ret - where to write the return value to
|
|
* @output: ret val - SUCCESS / FAIL
|
|
* ret - written with description for this PCI device
|
|
* @description: probes if there is PCI device connected to the slot, and if there is any,
|
|
* get its 64-to-72-byte information and write it into the return value.
|
|
* C doesn't support returning a 64-byte value on stack, so pointer is used.
|
|
*/
|
|
int32_t pci_get_device(uint8_t bus, uint8_t device, uint8_t func, pci_device_t* ret) {
|
|
if(bus >= PCI_COUNT_BUS
|
|
|| device >= PCI_COUNT_DEVICE
|
|
|| func >= PCI_COUNT_FUNC
|
|
|| ret == NULL) return FAIL;
|
|
// Compose the address for the query
|
|
pci_addr_t addr = {0};
|
|
addr.func_num = func;
|
|
addr.device_num = device;
|
|
addr.bus_num = bus;
|
|
addr.enable = 1;
|
|
// Register upper bound, 0x10 for standard devices, 0x12 for CardBus
|
|
int reg_bound = 0x10;
|
|
// Get each of the 16 segments of the 64-byte information
|
|
for(addr.reg_num = 0; addr.reg_num < 0x10; addr.reg_num++) {
|
|
outl(addr.val, PCI_REG_ADDR);
|
|
ret->val[addr.reg_num] = inl(PCI_REG_DATA);
|
|
// If this device doesn't exist (vendor_id and device_id are both 0xffff),
|
|
// return FAIL.
|
|
if(addr.reg_num == 0 && (ret->vendor_id == 0xffff || ret->device_id == 0xffff)) {
|
|
return FAIL;
|
|
}
|
|
if(addr.reg_num == 3 && ret->header_type == PCI_TYPE_CARDBUS) {
|
|
reg_bound = 0x12;
|
|
}
|
|
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* void pci_register_device(uint8_t bus, uint8_t device, uint8_t func, pci_device_t* dev_info)
|
|
* @input: bus, device, func - index of the PCI slot
|
|
* dev_info - pointer to the description of the device
|
|
* @output: if the device is recognized, then some drivers are configured according to PCI info
|
|
* @description: configures some devices based on PCI info.
|
|
* currently only configures QEMU VGA device.
|
|
*/
|
|
void pci_register_device(uint8_t bus, uint8_t device, uint8_t func, pci_device_t* dev_info) {
|
|
if(bus >= PCI_COUNT_BUS
|
|
|| device >= PCI_COUNT_DEVICE
|
|
|| func >= PCI_COUNT_FUNC
|
|
|| dev_info == NULL) return;
|
|
// Device specific handler
|
|
switch((uint32_t) dev_info->vendor_id << 16 | dev_info->device_id) {
|
|
case 0x12341111: // vendor device id pair for QEMU VGA device
|
|
// Register bar0 address as the address for linear buffer
|
|
qemu_vga_addr = dev_info->device.bar[0] & PCI_MASK_BAR_MEMSPACE;
|
|
printf("QEMU VGA Adapter detected, vram=%x\n", qemu_vga_addr);
|
|
break;
|
|
case 0x10ec8139: // for RTL8139 Ethernet Adapter
|
|
{ // Enable PCI bus mastering by setting bit 2
|
|
dev_info->command |= 0x4;
|
|
|
|
pci_addr_t addr = {0};
|
|
addr.func_num = func;
|
|
addr.device_num = device;
|
|
addr.bus_num = bus;
|
|
addr.reg_num = 1;
|
|
addr.enable = 1;
|
|
|
|
outl(addr.val, PCI_REG_ADDR);
|
|
outl(dev_info->val[1], PCI_REG_DATA);
|
|
}
|
|
|
|
// Let driver do the rest
|
|
rtl8139_init(dev_info->device.bar[0] & PCI_MASK_BAR_IOSPACE, dev_info->device.interrupt_line);
|
|
}
|
|
}
|
|
|
|
/* void pci_init()
|
|
* @output: PCI devices get scanned and initialized
|
|
* @description: scans every slot of the PCI bus, detects and initializes everything.
|
|
*/
|
|
void pci_init() {
|
|
uint8_t bus, device, func;
|
|
pci_device_t dev_info;
|
|
|
|
// 32 * 8 = 256 bits, marks the presence of all PCI buses.
|
|
// PCI bus num = array index * 8 + bit index
|
|
uint8_t buses[32] = {0};
|
|
buses[0] = 1; // Mark bus 0 as present
|
|
|
|
// Smart scan of the whole PCI bus.
|
|
// Start with bus 0, and if a PCI bridge is detected, add that bridge into list.
|
|
for(bus = 0; bus < PCI_COUNT_BUS; bus++) {
|
|
if(!(buses[bus / 8] & (1 << (bus % 8)))) continue;
|
|
printf("PCI scan on bus %d\n", bus);
|
|
for(device = 0; device < PCI_COUNT_DEVICE; device++) {
|
|
for(func = 0; func < PCI_COUNT_FUNC; func++) {
|
|
if(FAIL == pci_get_device(bus, device, func, &dev_info)) continue;
|
|
if(dev_info.header_type == PCI_TYPE_PCI_PCI) {
|
|
// Schedule the bus for scanning
|
|
int new_bus = dev_info.pci_bridge.secondary_bus_num;
|
|
printf("PCI bus %x:%x.%x: bus %x\n", bus, device, func, new_bus);
|
|
buses[new_bus / 8] |= (1 << (new_bus % 8));
|
|
} else {
|
|
printf("PCI device %x:%x.%x: %x:%x\n", bus, device, func, dev_info.vendor_id, dev_info.device_id);
|
|
pci_register_device(bus, device, func, &dev_info);
|
|
}
|
|
}
|
|
if(func == 0) continue;
|
|
}
|
|
}
|
|
printf("PCI scan complete\n");
|
|
}
|