Files
MentOS/mentos/src/devices/pci.c
T
Enrico Fraccaroli (Galfurian) 1b2bc49d41 Update license and remove unused files.
2022-01-27 15:12:36 -05:00

499 lines
17 KiB
C

/// @file pci.c
/// @brief Routines for PCI initialization.
/// @copyright (c) 2014-2022 This file is distributed under the MIT License.
/// See LICENSE.md for details.
///! @cond Doxygen_Suppress
// Include the kernel log levels.
#include "sys/kernel_levels.h"
/// Change the header.
#define __DEBUG_HEADER__ "[PCI ]"
/// Set the log level.
#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE
#include "devices/pci.h"
#include "io/debug.h"
#include "string.h"
#include "io/port_io.h"
void pci_write_field(uint32_t device, int field, int size, uint32_t value)
{
(void)size;
outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field));
#if 0
if (size == 4)
outportl(PCI_VALUE_PORT, value);
else if (size == 2)
outports(PCI_VALUE_PORT, value);
else if (size == 1)
outportb(PCI_VALUE_PORT, value);
#else
outportl(PCI_VALUE_PORT, value);
#endif
}
uint32_t pci_read_field(uint32_t device, int field, int size)
{
outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field));
if (size == 4) {
return inportl(PCI_VALUE_PORT);
} else if (size == 2) {
return inports(PCI_VALUE_PORT + (field & 2));
} else if (size == 1) {
return inportb(PCI_VALUE_PORT + (field & 3));
}
return 0xFFFF;
}
uint32_t pci_find_type(uint32_t device)
{
return pci_read_field(device, PCI_CLASS, 1) << 16 |
pci_read_field(device, PCI_SUBCLASS, 1) << 8 |
pci_read_field(device, PCI_PROG_IF, 1);
}
struct {
uint16_t id;
const char *name;
} _pci_vendors[] = {
{ 0x1022, "AMD" },
{ 0x106b, "Apple, Inc." },
{ 0x1234, "Bochs/QEMU" },
{ 0x1274, "Ensoniq" },
{ 0x15ad, "VMWare" },
{ 0x8086, "Intel Corporation" },
{ 0x80EE, "VirtualBox" },
};
struct {
uint16_t ven_id;
uint16_t dev_id;
const char *name;
} _pci_devices[] = {
{ 0x1022, 0x2000, "PCNet Ethernet Controller (pcnet)" },
{ 0x106b, 0x003f, "OHCI Controller" },
{ 0x1234, 0x1111, "VGA BIOS Graphics Extensions" },
{ 0x1274, 0x1371, "Creative Labs CT2518 (ensoniq audio)" },
{ 0x15ad, 0x0740, "VM Communication Interface" },
{ 0x15ad, 0x0405, "SVGA II Adapter" },
{ 0x15ad, 0x0790, "PCI bridge" },
{ 0x15ad, 0x07a0, "PCI Express Root Port" },
{ 0x8086, 0x100e, "Gigabit Ethernet Controller (e1000)" },
{ 0x8086, 0x100f, "Gigabit Ethernet Controller (e1000)" },
{ 0x8086, 0x1237, "PCI & Memory" },
{ 0x8086, 0x2415, "AC'97 Audio Chipset" },
{ 0x8086, 0x7000, "PCI-to-ISA Bridge" },
{ 0x8086, 0x7010, "IDE Interface" },
{ 0x8086, 0x7110, "PIIX4 ISA" },
{ 0x8086, 0x7111, "PIIX4 IDE" },
{ 0x8086, 0x7113, "Power Management Controller" },
{ 0x8086, 0x7190, "Host Bridge" },
{ 0x8086, 0x7191, "AGP Bridge" },
{ 0x80EE, 0xBEEF, "Bochs/QEMU-compatible Graphics Adapter" },
{ 0x80EE, 0xCAFE, "Guest Additions Device" },
};
struct {
uint32_t id;
const char *name;
} _pci_classes[] = {
{ 0x000000, "Legacy Device" },
{ 0x000100, "VGA-Compatible Device" },
{ 0x010000, "SCSI bus controller" },
{ 0x010100, "ISA Compatibility mode-only controller" },
{ 0x010105, "PCI native mode-only controller" },
{ 0x01010a, "ISA Compatibility mode controller, supports both channels "
"switched to PCI native mode" },
{ 0x01010f, "PCI native mode controller, supports both channels switched "
"to ISA compatibility mode" },
{ 0x010180,
"ISA Compatibility mode-only controller, supports bus mastering" },
{ 0x010185, "PCI native mode-only controller, supports bus mastering" },
{ 0x01018a, "ISA Compatibility mode controller, supports both channels "
"switched to PCI native mode, supports bus mastering" },
{ 0x01018f, "PCI native mode controller, supports both channels switched "
"\to ISA compatibility mode, supports bus mastering" },
{ 0x010200, "Floppy disk controller" },
{ 0x010300, "IPI bus controller" },
{ 0x010400, "RAID controller" },
{ 0x010520, "ATA controller, single stepping" },
{ 0x010530, "ATA controller, continuous" },
{ 0x010600, "Serial ATA controller - vendor specific interface" },
{ 0x010601, "Serial ATA controller - AHCI 1.0 interface" },
{ 0x010700, "Serial Attached SCSI controller" },
{ 0x018000, "Mass Storage controller" },
{ 0x020000, "Ethernet controller" },
{ 0x020100, "Token Ring controller" },
{ 0x020200, "FDDI controller" },
{ 0x020300, "ATM controller" },
{ 0x020400, "ISDN controller" },
{ 0x020500, "WorldFip controller" },
// { 0x0206xx , "PICMG 2.14 Multi Computing" },
{ 0x028000, "Network controller" },
{ 0x030000, "VGA Display controller" },
{ 0x030001, "8514-compatible Display controller" },
{ 0x030100, "XGA Display controller" },
{ 0x030200, "3D Display controller" },
{ 0x038000, "Display controller" },
{ 0x040000, "Video device" },
{ 0x040100, "Audio device" },
{ 0x040200, "Computer Telephony device" },
{ 0x048000, "Multimedia device" },
{ 0x050000, "RAM memory controller" },
{ 0x050100, "Flash memory controller" },
{ 0x058000, "Memory controller" },
{ 0x060000, "Host bridge" },
{ 0x060100, "ISA bridge" },
{ 0x060200, "EISA bridge" },
{ 0x060300, "MCA bridge" },
{ 0x060400, "PCI-to-PCI bridge" },
{ 0x060401, "PCI-to-PCI bridge (subtractive decoding)" },
{ 0x060500, "PCMCIA bridge" },
{ 0x060600, "NuBus bridge" },
{ 0x060700, "CardBus bridge" },
// { 0x0608xx , "RACEway bridge" },
{ 0x060940, "PCI-to-PCI bridge, Semi-transparent, primary facing Host" },
{ 0x060980, "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" },
{ 0x060A00, "InfiniBand-to-PCI host bridge" },
{ 0x068000, "Bridge device" },
{ 0x070000, "Generic XT-compatible serial controller" },
{ 0x070001, "16450-compatible serial controller" },
{ 0x070002, "16550-compatible serial controller" },
{ 0x070003, "16650-compatible serial controller" },
{ 0x070004, "16750-compatible serial controller" },
{ 0x070005, "16850-compatible serial controller" },
{ 0x070006, "16950-compatible serial controller" },
{ 0x070100, "Parallel port" },
{ 0x070101, "Bi-directional parallel port" },
{ 0x070102, "ECP 1.X compliant parallel port" },
{ 0x070103, "IEEE1284 controller" },
{ 0x0701FE, "IEEE1284 target device" },
{ 0x070200, "Multiport serial controller" },
{ 0x070300, "Generic modem" },
{ 0x070301, "Hayes 16450-compatible modem" },
{ 0x070302, "Hayes 16550-compatible modem" },
{ 0x070303, "Hayes 16650-compatible modem" },
{ 0x070304, "Hayes 16750-compatible modem" },
{ 0x070400, "GPIB (IEEE 488.1/2) controller" },
{ 0x070500, "Smart Card" },
{ 0x078000, "Communications device" },
{ 0x080000, "Generic 8259 PIC" },
{ 0x080001, "ISA PIC" },
{ 0x080002, "EISA PIC" },
{ 0x080010, "I/O APIC interrupt controller" },
{ 0x080020, "I/O(x) APIC interrupt controller" },
{ 0x080100, "Generic 8237 DMA controller" },
{ 0x080101, "ISA DMA controller" },
{ 0x080102, "EISA DMA controller" },
{ 0x080200, "Generic 8254 system timer" },
{ 0x080201, "ISA system timer" },
{ 0x080202, "EISA system timer-pair" },
{ 0x080300, "Generic RTC controller" },
{ 0x080301, "ISA RTC controller" },
{ 0x080400, "Generic PCI Hot-Plug controller" },
{ 0x080500, "SD Host controller" },
{ 0x088000, "System peripheral" },
{ 0x090000, "Keyboard controller" },
{ 0x090100, "Digitizer (pen)" },
{ 0x090200, "Mouse controller" },
{ 0x090300, "Scanner controller" },
{ 0x090400, "Generic Gameport controller" },
{ 0x090410, "Legacy Gameport controller" },
{ 0x098000, "Input controller" },
{ 0x0a0000, "Generic docking station" },
{ 0x0a8000, "Docking station" },
{ 0x0b0000, "386 Processor" },
{ 0x0b0100, "486 Processor" },
{ 0x0b0200, "Pentium Processor" },
{ 0x0b1000, "Alpha Processor" },
{ 0x0b2000, "PowerPC Processor" },
{ 0x0b3000, "MIPS Processor" },
{ 0x0b4000, "Co-processor" },
{ 0x0c0000, "IEEE 1394 (FireWire)" },
{ 0x0c0010, "IEEE 1394 -- OpenHCI spec" },
{ 0x0c0100, "ACCESS.bus" },
{ 0x0c0200, "SSA" },
{ 0x0c0300, "Universal Serial Bus (UHC spec)" },
{ 0x0c0310, "Universal Serial Bus (Open Host spec)" },
{ 0x0c0320, "USB2 Host controller (Intel Enhanced HCI spec)" },
{ 0x0c0380, "Universal Serial Bus (no PI spec)" },
{ 0x0c03FE, "USB Target Device" },
{ 0x0c0400, "Fibre Channel" },
{ 0x0c0500, "System Management Bus" },
{ 0x0c0600, "InfiniBand" },
{ 0x0c0700, "IPMI SMIC Interface" },
{ 0x0c0701, "IPMI Kybd Controller Style Interface" },
{ 0x0c0702, "IPMI Block Transfer Interface" },
// { 0x0c08xx , "SERCOS Interface" },
{ 0x0c0900, "CANbus" },
{ 0x0d100, "iRDA compatible controller" },
{ 0x0d100, "Consumer IR controller" },
{ 0x0d100, "RF controller" },
{ 0x0d100, "Bluetooth controller" },
{ 0x0d100, "Broadband controller" },
{ 0x0d100, "Ethernet (802.11a 5 GHz) controller" },
{ 0x0d100, "Ethernet (802.11b 2.4 GHz) controller" },
{ 0x0d100, "Wireless controller" },
// { 0x0e00xx , "I2O Intelligent I/O, spec 1.0" },
{ 0x0e0000, "Message FIFO at offset 040h" },
{ 0x0f0100, "TV satellite comm. controller" },
{ 0x0f0200, "Audio satellite comm. controller" },
{ 0x0f0300, "Voice satellite comm. controller" },
{ 0x0f0400, "Data satellite comm. controller" },
{ 0x100000, "Network and computing en/decryption" },
{ 0x101000, "Entertainment en/decryption" },
{ 0x108000, "En/Decryption" },
{ 0x110000, "DPIO modules" },
{ 0x110100, "Perf. counters" },
{ 0x111000, "Comm. synch., time and freq. test" },
{ 0x112000, "Management card" },
{ 0x118000, "Data acq./Signal proc." },
};
const char *pci_vendor_lookup(unsigned short vendor_id)
{
for (int i = 0; i < sizeof(_pci_vendors) / sizeof(_pci_vendors[0]); ++i) {
if (_pci_vendors[i].id == vendor_id) {
return _pci_vendors[i].name;
}
}
return "Unknown";
}
const char *pci_device_lookup(unsigned short vendor_id, unsigned short device_id)
{
for (int i = 0; i < sizeof(_pci_devices) / sizeof(_pci_devices[0]); ++i) {
if (_pci_devices[i].ven_id == vendor_id &&
_pci_devices[i].dev_id == device_id) {
return _pci_devices[i].name;
}
}
return "Unknown";
}
const char *pci_class_lookup(uint32_t class_code)
{
for (int i = 0; i < sizeof(_pci_classes) / sizeof(_pci_classes[0]); ++i) {
if (_pci_classes[i].id == class_code) {
return _pci_classes[i].name;
}
}
return "Unknown";
}
void pci_scan_hit(pci_scan_func_t f, uint32_t device, void *extra)
{
uint16_t vendor_id = (uint16_t)pci_read_field(device, PCI_VENDOR_ID, 2);
uint16_t device_id = (uint16_t)pci_read_field(device, PCI_DEVICE_ID, 2);
f(device, vendor_id, device_id, extra);
}
void pci_scan_func(pci_scan_func_t f, int type, int bus, int slot, int func, void *extra)
{
uint32_t device = pci_box_device(bus, slot, func);
if ((type == -1) || (type == pci_find_type(device))) {
pci_scan_hit(f, device, extra);
}
if (pci_find_type(device) == PCI_TYPE_BRIDGE) {
pci_scan_bus(f, type, pci_read_field(device, PCI_SECONDARY_BUS, 1), extra);
}
}
void pci_scan_slot(pci_scan_func_t f, int type, int bus, int slot, void *extra)
{
uint32_t device = pci_box_device(bus, slot, 0);
if (pci_read_field(device, PCI_VENDOR_ID, 2) == PCI_NONE) {
return;
}
pci_scan_func(f, type, bus, slot, 0, extra);
if (!pci_read_field(device, PCI_HEADER_TYPE, 1)) {
return;
}
for (int func = 1; func < 8; func++) {
device = pci_box_device(bus, slot, func);
if (pci_read_field(device, PCI_VENDOR_ID, 2) != PCI_NONE) {
pci_scan_func(f, type, bus, slot, func, extra);
}
}
}
void pci_scan_bus(pci_scan_func_t f, int type, int bus, void *extra)
{
for (int slot = 0; slot < 32; ++slot) {
pci_scan_slot(f, type, bus, slot, extra);
}
}
void pci_scan(pci_scan_func_t f, int type, void *extra)
{
if ((pci_read_field(0, PCI_HEADER_TYPE, 1) & 0x80) == 0) {
pci_scan_bus(f, type, 0, extra);
return;
}
for (int func = 0; func < 8; ++func) {
uint32_t device = pci_box_device(0, 0, func);
if (pci_read_field(device, PCI_VENDOR_ID, 2) == PCI_NONE) {
break;
}
pci_scan_bus(f, type, func, extra);
}
}
static void find_isa_bridge(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra)
{
if (vendorid == 0x8086 && (deviceid == 0x7000 || deviceid == 0x7110)) {
*((uint32_t *)extra) = device;
}
}
static uint32_t pci_isa = 0;
static uint32_t pci_remaps[4] = { 0 };
void pci_remap()
{
pci_scan(&find_isa_bridge, -1, &pci_isa);
if (pci_isa) {
pr_default("PCI-to-ISA interrupt mappings by line:\n");
for (int i = 0; i < 4; ++i) {
pci_remaps[i] = pci_read_field(pci_isa, 0x60 + i, 1);
pr_default("\tLine %d: 0x%2x\n", i + 1, pci_remaps[i]);
}
uint32_t out = 0;
memcpy(&out, &pci_remaps, 4);
pci_write_field(pci_isa, 0x60, 4, out);
}
}
int pci_get_interrupt(uint32_t device)
{
if (pci_isa == 0) {
return pci_read_field(device, PCI_INTERRUPT_LINE, 1);
}
uint32_t irq_pin = pci_read_field(device, PCI_INTERRUPT_PIN, 1);
if (irq_pin == 0) {
pr_default("PCI device does not specific interrupt line\n");
return pci_read_field(device, PCI_INTERRUPT_LINE, 1);
}
int pirq = (irq_pin + pci_extract_slot(device) - 2) % 4;
uint32_t int_line = pci_read_field(device, PCI_INTERRUPT_LINE, 1);
pr_default("Slot is %d, irq pin is %d, so pirq is %d and that maps to %d?"
"int_line=%d\n",
pci_extract_slot(device), irq_pin, pirq, pci_remaps[pirq],
int_line);
if (pci_remaps[pirq] == 0x80) {
pr_default("Not mapped, remapping?\n");
pci_remaps[pirq] = int_line;
uint32_t out = 0;
memcpy(&out, &pci_remaps, 4);
pci_write_field(pci_isa, 0x60, 4, out);
return pci_read_field(device, PCI_INTERRUPT_LINE, 1);
}
return pci_remaps[pirq];
}
static void __scan_count(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra)
{
(void)device;
(void)vendorid;
(void)deviceid;
size_t *count = extra;
++(*count);
}
static void __scan_hit_list(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra)
{
(void)extra;
pr_default("%2x:%2x.%d\n", pci_extract_bus(device), pci_extract_slot(device), pci_extract_func(device), deviceid);
pr_default(" Vendor : [0x%06x] %s\n", vendorid, pci_vendor_lookup(vendorid));
pr_default(" Device : [0x%06x] %s\n", deviceid, pci_device_lookup(vendorid, deviceid));
pr_default(" Type : [0x%06x] %s\n", pci_find_type(device), pci_class_lookup(pci_find_type(device)));
pr_default(" Status : 0x%4x\n", pci_read_field(device, PCI_STATUS, 2));
pr_default(" Command : 0x%4x\n", pci_read_field(device, PCI_COMMAND, 2));
pr_default(" Revision : %2d\n", pci_read_field(device, PCI_REVISION_ID, 1));
pr_default(" Cache Line Size : %2d\n", pci_read_field(device, PCI_CACHE_LINE_SIZE, 1));
pr_default(" Latency Timer : %2d\n", pci_read_field(device, PCI_LATENCY_TIMER, 1));
pr_default(" Header Type : %2d\n", pci_read_field(device, PCI_HEADER_TYPE, 1));
pr_default(" BIST : %2d\n", pci_read_field(device, PCI_BIST, 1));
pr_default(" BAR0 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_0, 4));
pr_default(" BAR1 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_1, 4));
pr_default(" BAR2 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_2, 4));
pr_default(" BAR3 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_3, 4));
pr_default(" BAR4 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_4, 4));
pr_default(" BAR6 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_5, 4));
pr_default(" Cardbus CIS : 0x%08x\n", pci_read_field(device, PCI_CARDBUS_CIS, 4));
pr_default(" Subsystem V. ID : 0x%08x\n", pci_read_field(device, PCI_SUBSYSTEM_VENDOR_ID, 2));
pr_default(" Subsystem ID : 0x%08x\n", pci_read_field(device, PCI_SUBSYSTEM_ID, 2));
pr_default(" ROM Base Address: 0x%08x\n", pci_read_field(device, PCI_ROM_ADDRESS, 4));
pr_default(" PCI Cp. LinkList: 0x%08x\n", pci_read_field(device, PCI_CAPABILITY_LIST, 1));
pr_default(" Max Latency : 0x%08x\n", pci_read_field(device, PCI_MAX_LAT, 1));
pr_default(" Min Grant : 0x%08x\n", pci_read_field(device, PCI_MIN_GNT, 1));
pr_default(" Interrupt Pin : %2d\n", pci_read_field(device, PCI_INTERRUPT_PIN, 1));
pr_default(" Interrupt Line : %2d\n", pci_read_field(device, PCI_INTERRUPT_LINE, 1));
pr_default(" Interrupt Number: %2d\n", pci_get_interrupt(device));
pr_default("\n");
}
void pci_debug_scan()
{
pr_default("\n--------------------------------------------------\n");
pr_default("Counting PCI entities...\n");
size_t count = 0;
pci_scan(&__scan_count, -1, &count);
pr_default("Total PCI entities: %d\n", count);
pr_default("Scanning PCI entities...\n");
pci_scan(&__scan_hit_list, -1, NULL);
pr_default("Mapping PCI entities...\n");
pci_remap();
pr_default("--------------------------------------------------\n");
}
///! @endcond