/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * Implementation of PCI-PCI and host-AGP bridges. * * * * Authors: RichardG, * * Copyright 2020 RichardG. */ #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/machine.h> #include "cpu.h" #include <86box/io.h> #include <86box/pic.h> #include <86box/mem.h> #include <86box/device.h> #include <86box/pci.h> #define PCI_BRIDGE_DEC_21150 0x10110022 #define AGP_BRIDGE_ALI_M5243 0x10b95243 #define AGP_BRIDGE_ALI_M5247 0x10b95247 #define AGP_BRIDGE_INTEL_440LX 0x80867181 #define AGP_BRIDGE_INTEL_440BX 0x80867191 #define AGP_BRIDGE_INTEL_440GX 0x808671a1 #define AGP_BRIDGE_VIA_597 0x11068597 #define AGP_BRIDGE_VIA_598 0x11068598 #define AGP_BRIDGE_VIA_691 0x11068691 #define AGP_BRIDGE_VIA_8601 0x11068601 #define AGP_BRIDGE_ALI(x) (((x) >> 16) == 0x10b9) #define AGP_BRIDGE_INTEL(x) (((x) >> 16) == 0x8086) #define AGP_BRIDGE_VIA(x) (((x) >> 16) == 0x1106) #define AGP_BRIDGE(x) ((x) >= AGP_BRIDGE_ALI_M5243) typedef struct pci_bridge_t { uint32_t local; uint8_t type; uint8_t ctl; uint8_t regs[256]; uint8_t bus_index; uint8_t slot; } pci_bridge_t; #ifdef ENABLE_PCI_BRIDGE_LOG int pci_bridge_do_log = ENABLE_PCI_BRIDGE_LOG; static void pci_bridge_log(const char *fmt, ...) { va_list ap; if (pci_bridge_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define pci_bridge_log(fmt, ...) #endif void pci_bridge_set_ctl(void *priv, uint8_t ctl) { pci_bridge_t *dev = (pci_bridge_t *) priv; dev->ctl = ctl; } static void pci_bridge_write(int func, int addr, uint8_t val, void *priv) { pci_bridge_t *dev = (pci_bridge_t *) priv; pci_bridge_log("PCI Bridge %d: write(%d, %02X, %02X)\n", dev->bus_index, func, addr, val); if (func > 0) return; if ((dev->local == AGP_BRIDGE_ALI_M5247) && (addr >= 0x40)) return; switch (addr) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x06: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0e: case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x1e: case 0x34: case 0x3d: case 0x67: case 0xdc: case 0xdd: case 0xde: case 0xdf: return; case 0x04: if (AGP_BRIDGE_INTEL(dev->local)) { if (dev->local == AGP_BRIDGE_INTEL_440BX) val &= 0x1f; } else if (dev->local == AGP_BRIDGE_ALI_M5243) val |= 0x02; else if (dev->local == AGP_BRIDGE_ALI_M5247) val &= 0xc3; else val &= 0x67; break; case 0x05: if (AGP_BRIDGE_INTEL(dev->local)) val &= 0x01; else if (AGP_BRIDGE_ALI(dev->local)) val &= 0x01; else val &= 0x03; break; case 0x07: if (dev->local == AGP_BRIDGE_INTEL_440LX) dev->regs[addr] &= ~(val & 0x40); else if (dev->local == AGP_BRIDGE_ALI_M5243) dev->regs[addr] &= ~(val & 0xf8); else if (dev->local == AGP_BRIDGE_ALI_M5247) dev->regs[addr] &= ~(val & 0xc0); return; case 0x0c: case 0x18: /* Parent bus number (0x18) is always 0 on AGP bridges. */ if (AGP_BRIDGE(dev->local)) return; break; case 0x0d: if (AGP_BRIDGE_VIA(dev->local)) return; else if (AGP_BRIDGE_INTEL(dev->local)) val &= 0xf8; else if (AGP_BRIDGE_ALI(dev->local)) val &= 0xf8; break; case 0x19: /* Set our bus number. */ pci_bridge_log("PCI Bridge %d: remapping from bus %02X to %02X\n", dev->bus_index, dev->regs[addr], val); pci_remap_bus(dev->bus_index, val); break; case 0x1f: if (AGP_BRIDGE_INTEL(dev->local)) { if (dev->local == AGP_BRIDGE_INTEL_440LX) dev->regs[addr] &= ~(val & 0xf1); else if ((dev->local == AGP_BRIDGE_INTEL_440BX) || (dev->local == AGP_BRIDGE_INTEL_440GX)) dev->regs[addr] &= ~(val & 0xf0); } else if (AGP_BRIDGE_ALI(dev->local)) dev->regs[addr] &= ~(val & 0xf0); return; case 0x1c: case 0x1d: case 0x20: case 0x22: case 0x24: case 0x26: val &= 0xf0; break; case 0x3c: if (!(dev->ctl & 0x80)) return; break; case 0x3e: if (AGP_BRIDGE_VIA(dev->local)) val &= 0x0c; else if (dev->local == AGP_BRIDGE_ALI_M5247) val &= 0x0f; else if (dev->local == AGP_BRIDGE_ALI_M5243) return; else if (AGP_BRIDGE(dev->local)) { if ((dev->local == AGP_BRIDGE_INTEL_440BX) || (dev->local == AGP_BRIDGE_INTEL_440GX)) val &= 0xed; else val &= 0x0f; } else if (dev->local == PCI_BRIDGE_DEC_21150) val &= 0xef; break; case 0x3f: if (dev->local == AGP_BRIDGE_INTEL_440LX) { dev->regs[addr] = ((dev->regs[addr] & 0x04) | (val & 0x02)) & ~(val & 0x04); return; } else if (dev->local == AGP_BRIDGE_ALI_M5247) return; else if (dev->local == AGP_BRIDGE_ALI_M5243) val &= 0x06; else if (AGP_BRIDGE(dev->local)) return; else if (dev->local == PCI_BRIDGE_DEC_21150) val &= 0x0f; break; case 0x40: if (dev->local == PCI_BRIDGE_DEC_21150) val &= 0x32; break; case 0x41: if (AGP_BRIDGE_VIA(dev->local)) val &= 0x7e; else if (dev->local == PCI_BRIDGE_DEC_21150) val &= 0x07; break; case 0x42: if (AGP_BRIDGE_VIA(dev->local)) val &= 0xfe; break; case 0x43: if (dev->local == PCI_BRIDGE_DEC_21150) val &= 0x03; break; case 0x64: if (dev->local == PCI_BRIDGE_DEC_21150) val &= 0x7e; break; case 0x69: if (dev->local == PCI_BRIDGE_DEC_21150) val &= 0x3f; break; case 0x86: if (AGP_BRIDGE_ALI(dev->local)) val &= 0x3f; break; case 0x87: if (AGP_BRIDGE_ALI(dev->local)) val &= 0x60; break; case 0x88: if (AGP_BRIDGE_ALI(dev->local)) val &= 0x8c; break; case 0x8b: if (AGP_BRIDGE_ALI(dev->local)) val &= 0x0f; break; case 0x8c: if (AGP_BRIDGE_ALI(dev->local)) val &= 0x83; break; case 0x8d: if (AGP_BRIDGE_ALI(dev->local)) return; break; case 0xe0: case 0xe1: if (AGP_BRIDGE_ALI(dev->local)) { if (!(dev->ctl & 0x20)) return; } else return; break; case 0xe2: if (AGP_BRIDGE_ALI(dev->local)) { if (dev->ctl & 0x20) val &= 0x3f; else return; } else return; break; case 0xe3: if (AGP_BRIDGE_ALI(dev->local)) { if (dev->ctl & 0x20) val &= 0xfe; else return; } else return; break; case 0xe4: if (AGP_BRIDGE_ALI(dev->local)) { if (dev->ctl & 0x20) val &= 0x03; else return; } break; case 0xe5: if (AGP_BRIDGE_ALI(dev->local)) { if (!(dev->ctl & 0x20)) return; } break; case 0xe6: if (AGP_BRIDGE_ALI(dev->local)) { if (dev->ctl & 0x20) val &= 0xc0; else return; } break; case 0xe7: if (AGP_BRIDGE_ALI(dev->local)) { if (!(dev->ctl & 0x20)) return; } break; default: break; } dev->regs[addr] = val; } static uint8_t pci_bridge_read(int func, int addr, void *priv) { const pci_bridge_t *dev = (pci_bridge_t *) priv; uint8_t ret; if (func > 0) ret = 0xff; else ret = dev->regs[addr]; pci_bridge_log("PCI Bridge %d: read(%d, %02X) = %02X\n", dev->bus_index, func, addr, ret); return ret; } static void pci_bridge_reset(void *priv) { pci_bridge_t *dev = (pci_bridge_t *) priv; pci_bridge_log("PCI Bridge %d: reset()\n", dev->bus_index); memset(dev->regs, 0, sizeof(dev->regs)); /* IDs */ dev->regs[0x00] = dev->local >> 16; dev->regs[0x01] = dev->local >> 24; dev->regs[0x02] = dev->local; dev->regs[0x03] = dev->local >> 8; /* command and status */ switch (dev->local) { case PCI_BRIDGE_DEC_21150: dev->regs[0x06] = 0x80; dev->regs[0x07] = 0x02; break; case AGP_BRIDGE_ALI_M5243: dev->regs[0x04] = 0x06; dev->regs[0x07] = 0x04; dev->regs[0x0d] = 0x20; dev->regs[0x19] = 0x01; dev->regs[0x1b] = 0x20; dev->regs[0x34] = 0xe0; dev->regs[0x89] = 0x20; dev->regs[0x8a] = 0xa0; dev->regs[0x8e] = 0x20; dev->regs[0x8f] = 0x20; dev->regs[0xe0] = 0x01; pci_remap_bus(dev->bus_index, 0x01); break; case AGP_BRIDGE_ALI_M5247: dev->regs[0x04] = 0x03; dev->regs[0x08] = 0x01; break; case AGP_BRIDGE_INTEL_440LX: dev->regs[0x06] = 0xa0; dev->regs[0x07] = 0x02; dev->regs[0x08] = 0x03; break; case AGP_BRIDGE_INTEL_440BX: case AGP_BRIDGE_INTEL_440GX: dev->regs[0x06] = 0x20; dev->regs[0x07] = dev->regs[0x08] = 0x02; break; case AGP_BRIDGE_VIA_597: case AGP_BRIDGE_VIA_598: case AGP_BRIDGE_VIA_691: case AGP_BRIDGE_VIA_8601: dev->regs[0x04] = 0x07; dev->regs[0x06] = 0x20; dev->regs[0x07] = 0x02; break; default: break; } /* class */ dev->regs[0x0a] = 0x04; /* PCI-PCI bridge */ dev->regs[0x0b] = 0x06; /* bridge device */ dev->regs[0x0e] = 0x01; /* bridge header */ /* IO BARs */ if (AGP_BRIDGE(dev->local)) dev->regs[0x1c] = 0xf0; else dev->regs[0x1c] = dev->regs[0x1d] = 0x01; if (dev->local == AGP_BRIDGE_ALI_M5247) dev->regs[0x1e] = 0x20; else if (!AGP_BRIDGE_VIA(dev->local)) { dev->regs[0x1e] = AGP_BRIDGE(dev->local) ? 0xa0 : 0x80; dev->regs[0x1f] = 0x02; } /* prefetchable memory limits */ if (AGP_BRIDGE(dev->local)) { dev->regs[0x20] = dev->regs[0x24] = 0xf0; dev->regs[0x21] = dev->regs[0x25] = 0xff; } else { dev->regs[0x24] = dev->regs[0x26] = 0x01; } /* power management */ if (dev->local == PCI_BRIDGE_DEC_21150) { dev->regs[0x34] = 0xdc; dev->regs[0x43] = 0x02; dev->regs[0xdc] = dev->regs[0xde] = 0x01; } } static void * pci_bridge_init(const device_t *info) { uint8_t interrupts[4]; uint8_t interrupt_count; uint8_t interrupt_mask; uint8_t slot_count; pci_bridge_t *dev = (pci_bridge_t *) malloc(sizeof(pci_bridge_t)); memset(dev, 0, sizeof(pci_bridge_t)); dev->local = info->local; dev->bus_index = pci_register_bus(); pci_bridge_log("PCI Bridge %d: init()\n", dev->bus_index); pci_bridge_reset(dev); pci_add_bridge(AGP_BRIDGE(dev->local), pci_bridge_read, pci_bridge_write, dev, &dev->slot); interrupt_count = sizeof(interrupts); interrupt_mask = interrupt_count - 1; if (dev->slot < 32) { for (uint8_t i = 0; i < interrupt_count; i++) interrupts[i] = pci_get_int(dev->slot, PCI_INTA + i); } pci_bridge_log("PCI Bridge %d: upstream bus %02X slot %02X interrupts %02X %02X %02X %02X\n", dev->bus_index, (dev->slot >> 5) & 0xff, dev->slot & 31, interrupts[0], interrupts[1], interrupts[2], interrupts[3]); if (info->local == PCI_BRIDGE_DEC_21150) slot_count = 9; /* 9 bus masters */ else slot_count = 1; /* AGP bridges always have 1 slot */ for (uint8_t i = 0; i < slot_count; i++) { /* Interrupts for bridge slots are assigned in round-robin: ABCD, BCDA, CDAB and so on. */ pci_bridge_log("PCI Bridge %d: downstream slot %02X interrupts %02X %02X %02X %02X\n", dev->bus_index, i, interrupts[i & interrupt_mask], interrupts[(i + 1) & interrupt_mask], interrupts[(i + 2) & interrupt_mask], interrupts[(i + 3) & interrupt_mask]); pci_register_bus_slot(dev->bus_index, i, AGP_BRIDGE(dev->local) ? PCI_CARD_AGP : PCI_CARD_NORMAL, interrupts[i & interrupt_mask], interrupts[(i + 1) & interrupt_mask], interrupts[(i + 2) & interrupt_mask], interrupts[(i + 3) & interrupt_mask]); } return dev; } /* PCI bridges */ const device_t dec21150_device = { .name = "DEC 21150 PCI Bridge", .internal_name = "dec21150", .flags = DEVICE_PCI, .local = PCI_BRIDGE_DEC_21150, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; /* AGP bridges */ const device_t ali5243_agp_device = { .name = "ALi M5243 AGP Bridge", .internal_name = "ali5243_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_ALI_M5243, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; /* AGP bridges */ const device_t ali5247_agp_device = { .name = "ALi M5247 AGP Bridge", .internal_name = "ali5247_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_ALI_M5247, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t i440lx_agp_device = { .name = "Intel 82443LX/EX AGP Bridge", .internal_name = "i440lx_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_INTEL_440LX, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t i440bx_agp_device = { .name = "Intel 82443BX/ZX AGP Bridge", .internal_name = "i440bx_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_INTEL_440BX, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t i440gx_agp_device = { .name = "Intel 82443GX AGP Bridge", .internal_name = "i440gx_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_INTEL_440GX, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t via_vp3_agp_device = { .name = "VIA Apollo VP3 AGP Bridge", .internal_name = "via_vp3_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_VIA_597, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t via_mvp3_agp_device = { .name = "VIA Apollo MVP3 AGP Bridge", .internal_name = "via_mvp3_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_VIA_598, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t via_apro_agp_device = { .name = "VIA Apollo Pro AGP Bridge", .internal_name = "via_apro_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_VIA_691, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t via_vt8601_agp_device = { .name = "VIA Apollo ProMedia AGP Bridge", .internal_name = "via_vt8601_agp", .flags = DEVICE_PCI, .local = AGP_BRIDGE_VIA_8601, .init = pci_bridge_init, .close = NULL, .reset = pci_bridge_reset, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL };