Files
86Box/src/pci.c

948 lines
26 KiB
C
Raw Normal View History

2018-10-21 20:27:57 +02:00
/*
* 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.
2018-10-21 20:27:57 +02:00
*
* This file is part of the 86Box distribution.
2018-10-21 20:27:57 +02:00
*
* Implementation the PCI bus.
2018-10-21 20:27:57 +02:00
*
2020-03-25 00:46:02 +02:00
*
2018-10-21 20:27:57 +02:00
*
* Authors: Miran Grca, <mgrca8@gmail.com>
2018-10-21 20:27:57 +02:00
*
* Copyright 2023 Miran Grca.
2018-10-21 20:27:57 +02:00
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <wchar.h>
2017-12-10 02:53:10 -05:00
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/machine.h>
2020-02-29 19:12:23 +01:00
#include "cpu.h"
#include <86box/io.h>
#include <86box/pic.h>
#include <86box/mem.h>
#include <86box/device.h>
#include <86box/dma.h>
#include <86box/pci.h>
#include <86box/keyboard.h>
2023-06-28 13:46:28 -04:00
#include <86box/plat_unused.h>
#define PCI_ENABLED 0x80000000
typedef struct pci_card_t {
uint8_t bus;
uint8_t id;
uint8_t type;
uint8_t irq_routing[PCI_INT_PINS_NUM];
void * priv;
void (*write)(int func, int addr, uint8_t val, void *priv);
uint8_t (*read)(int func, int addr, void *priv);
} pci_card_t;
typedef struct pci_card_desc_t {
uint8_t type;
void * priv;
void (*write)(int func, int addr, uint8_t val, void *priv);
uint8_t (*read)(int func, int addr, void *priv);
uint8_t *slot;
} pci_card_desc_t;
2023-06-28 13:46:28 -04:00
typedef struct pci_mirq_t {
uint8_t enabled;
uint8_t irq_line;
} pci_mirq_t;
int pci_burst_time;
int agp_burst_time;
int pci_nonburst_time;
int agp_nonburst_time;
int pci_flags;
uint32_t pci_base = 0xc000;
uint32_t pci_size = 0x1000;
static pci_card_t pci_cards[PCI_CARDS_NUM];
static pci_card_desc_t pci_card_descs[PCI_CARDS_NUM];
static uint8_t pci_pmc = 0;
static uint8_t last_pci_card = 0;
static uint8_t last_normal_pci_card = 0;
static uint8_t last_normal_pci_card_id = 0;
static uint8_t last_pci_bus = 1;
static uint8_t next_pci_card = 0;
static uint8_t normal_pci_cards = 0;
static uint8_t next_normal_pci_card = 0;
static uint8_t pci_card_to_slot_mapping[256][PCI_CARDS_NUM];
static uint8_t pci_bus_number_to_index_mapping[256];
static uint8_t pci_irqs[PCI_IRQS_NUM];
static uint8_t pci_irq_level[PCI_IRQS_NUM];
static uint64_t pci_irq_hold[PCI_IRQS_NUM];
static pci_mirq_t pci_mirqs[PCI_MIRQS_NUM];
static int pci_index;
static int pci_func;
static int pci_card;
static int pci_bus;
static int pci_key;
static int pci_trc_reg = 0;
static uint32 pci_enable = 0x00000000;
static void pci_reset_regs(void);
#ifdef ENABLE_PCI_LOG
int pci_do_log = ENABLE_PCI_LOG;
static void
pci_log(const char *fmt, ...)
{
va_list ap;
if (pci_do_log) {
2022-09-18 17:11:43 -04:00
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
2022-09-18 17:11:43 -04:00
# define pci_log(fmt, ...)
#endif
void
pci_set_irq_routing(int pci_int, int irq)
{
pci_irqs[pci_int - 1] = irq;
}
void
pci_set_irq_level(int pci_int, int level)
{
pci_irq_level[pci_int - 1] = !!level;
}
void
pci_enable_mirq(int mirq)
{
pci_mirqs[mirq].enabled = 1;
}
void
pci_set_mirq_routing(int mirq, int irq)
{
pci_mirqs[mirq].irq_line = irq;
}
/* PCI raise IRQ: the first parameter is slot if < PCI_MIRQ_BASE, MIRQ if >= PCI_MIRQ_BASE
and < PCI_DIRQ_BASE, and direct IRQ line if >= PCI_DIRQ_BASE (RichardG's
hack that may no longer be needed). */
void
pci_irq(uint8_t slot, uint8_t pci_int, int level, int set, uint8_t *irq_state)
{
uint8_t irq_routing = 0;
uint8_t pci_int_index = pci_int - PCI_INTA;
uint8_t irq_line = 0;
uint8_t is_vfio = 0;
/* The fast path out an invalid PCI card. */
if (slot == PCI_CARD_INVALID)
return;
switch (slot) {
default:
return;
case 0x00 ... PCI_CARD_MAX:
/* PCI card. */
if (!last_pci_card)
return;
if (pci_flags & FLAG_NO_IRQ_STEERING)
irq_line = pci_cards[slot].read(0, 0x3c, pci_cards[slot].priv);
else {
irq_routing = pci_cards[slot].irq_routing[pci_int_index];
switch (irq_routing) {
default:
case 0x00:
return;
case 0x01 ... PCI_IRQS_NUM:
is_vfio = pci_cards[slot].type & PCI_CARD_VFIO;
irq_routing = (irq_routing - PCI_INTA) & PCI_IRQ_MAX;
irq_line = pci_irqs[irq_routing];
/* Ignore what was provided to us as a parameter and override it with whatever
the chipset is set to. */
level = !!pci_irq_level[irq_routing];
if (level && is_vfio)
level--;
break;
/* Sometimes, PCI devices are mapped to direct IRQ's. */
case (PCI_DIRQ_BASE | 0x00) ... (PCI_DIRQ_BASE | PCI_DIRQ_MAX):
/* Direct IRQ line, always edge-triggered. */
irq_line = slot & PCI_IRQ_MAX;
break;
}
}
break;
case (PCI_MIRQ_BASE | 0x00) ... (PCI_MIRQ_BASE | PCI_MIRQ_MAX):
/* MIRQ */
slot &= PCI_MIRQ_MAX;
if (!pci_mirqs[slot].enabled)
return;
irq_line = pci_mirqs[slot].irq_line;
break;
case (PCI_DIRQ_BASE | 0x00) ... (PCI_DIRQ_BASE | PCI_DIRQ_MAX):
/* Direct IRQ line (RichardG's ACPI workaround, may no longer be needed). */
irq_line = slot & PCI_IRQ_MAX;
break;
}
if (irq_line > PCI_IRQ_MAX)
return;
picint_common(1 << irq_line, level, set, irq_state);
}
uint8_t
pci_get_int(uint8_t slot, uint8_t pci_int)
{
return pci_cards[slot].irq_routing[pci_int - PCI_INTA];
}
2022-07-23 01:15:59 +02:00
static void
pci_clear_slot(int card)
{
pci_card_to_slot_mapping[pci_cards[card].bus][pci_cards[card].id] = PCI_CARD_INVALID;
2022-07-23 01:15:59 +02:00
2022-09-18 17:11:43 -04:00
pci_cards[card].id = 0xff;
2022-07-23 01:15:59 +02:00
pci_cards[card].type = 0xff;
2023-05-11 03:02:36 -04:00
for (uint8_t i = 0; i < 4; i++)
2022-09-18 17:11:43 -04:00
pci_cards[card].irq_routing[i] = 0;
2022-07-23 01:15:59 +02:00
2022-09-18 17:11:43 -04:00
pci_cards[card].read = NULL;
2022-07-23 01:15:59 +02:00
pci_cards[card].write = NULL;
2022-09-18 17:11:43 -04:00
pci_cards[card].priv = NULL;
2022-07-23 01:15:59 +02:00
}
/* Relocate a PCI device to a new slot, required for the configurable
IDSEL's of ALi M1543(c). */
2022-07-23 01:15:59 +02:00
void
pci_relocate_slot(int type, int new_slot)
{
2023-05-11 03:02:36 -04:00
int card = -1;
2022-09-18 17:11:43 -04:00
int old_slot;
2022-07-23 01:15:59 +02:00
if ((new_slot < 0) || (new_slot > 31))
2022-09-18 17:11:43 -04:00
return;
2022-07-23 01:15:59 +02:00
for (uint8_t i = 0; i < PCI_CARDS_NUM; i++) {
2022-09-18 17:11:43 -04:00
if ((pci_cards[i].bus == 0) && (pci_cards[i].type == type)) {
card = i;
break;
}
2022-07-23 01:15:59 +02:00
}
if (card == -1)
2022-09-18 17:11:43 -04:00
return;
2022-07-23 01:15:59 +02:00
2022-09-18 17:11:43 -04:00
old_slot = pci_cards[card].id;
pci_cards[card].id = new_slot;
if (pci_card_to_slot_mapping[0][old_slot] == card)
pci_card_to_slot_mapping[0][old_slot] = PCI_CARD_INVALID;
if (pci_card_to_slot_mapping[0][new_slot] == PCI_CARD_INVALID)
pci_card_to_slot_mapping[0][new_slot] = card;
2022-07-23 01:15:59 +02:00
}
/* Write PCI enable/disable key, split for the ALi M1435. */
void
pci_key_write(uint8_t val)
{
pci_key = val & 0xf0;
if (pci_key)
pci_flags |= FLAG_CONFIG_IO_ON;
else
pci_flags &= ~FLAG_CONFIG_IO_ON;
}
2018-10-21 20:27:57 +02:00
static void
pci_io_handlers(int set)
{
io_handler(set, 0x0cf8, 4, pci_read, pci_readw, pci_readl, pci_write, pci_writew, pci_writel, NULL);
if (pci_flags & FLAG_MECHANISM_1)
io_handler(set, 0x0cfc, 4, pci_read, pci_readw, pci_readl, pci_write, pci_writew, pci_writel, NULL);
if (pci_flags & FLAG_MECHANISM_2) {
if (set && pci_key)
pci_flags |= FLAG_CONFIG_IO_ON;
else
pci_flags &= ~FLAG_CONFIG_IO_ON;
}
}
/* Set PMC (ie. change PCI configuration mechanism), 0 = #2, 1 = #1. */
void
pci_set_pmc(uint8_t pmc)
{
pci_log("pci_set_pmc(%02X)\n", pmc);
pci_io_handlers(0);
pci_flags &= ~FLAG_MECHANISM_MASK;
pci_flags |= (FLAG_MECHANISM_1 + !(pmc & 0x01));
pci_io_handlers(1);
pci_pmc = (pmc & 0x01);
}
2018-10-21 20:27:57 +02:00
static void
pci_reg_write(uint16_t port, uint8_t val)
{
2018-10-21 20:27:57 +02:00
uint8_t slot = 0;
if (port >= 0xc000) {
pci_card = (port >> 8) & 0xf;
pci_index = port & 0xfc;
}
slot = pci_card_to_slot_mapping[pci_bus_number_to_index_mapping[pci_bus]][pci_card];
if (slot != PCI_CARD_INVALID) {
if (pci_cards[slot].write)
pci_cards[slot].write(pci_func, pci_index | (port & 0x03), val, pci_cards[slot].priv);
}
pci_log("PCI: [WB] Mechanism #%i, slot %02X, %s card %02X:%02X, function %02X, index %02X = %02X\n",
(port >= 0xc000) ? 2 : 1, slot,
(slot == PCI_CARD_INVALID) ? "non-existent" : (pci_cards[slot].write ? "used" : "unused"),
pci_card, pci_bus, pci_func, pci_index | (port & 0x03), val);
}
2022-09-18 17:11:43 -04:00
static void
pci_reset_regs(void)
{
pci_index = pci_card = pci_func = pci_bus = pci_key = 0;
pci_enable = 0x00000000;
2020-09-19 00:56:12 -03:00
pci_flags &= ~(FLAG_CONFIG_IO_ON | FLAG_CONFIG_M1_IO_ON);
}
2023-06-28 13:46:28 -04:00
void
pci_pic_reset(void)
{
pic_reset();
pic_set_pci_flag(last_pci_card > 0);
}
static void
pci_reset_hard(void)
{
pci_reset_regs();
for (uint8_t i = 0; i < PCI_IRQS_NUM; i++) {
if (pci_irq_hold[i]) {
pci_irq_hold[i] = 0;
2022-09-18 17:11:43 -04:00
picintc(1 << i);
}
}
pci_pic_reset();
}
2023-06-28 13:46:28 -04:00
void
pci_reset(void)
{
if (pci_flags & FLAG_MECHANISM_SWITCH) {
pci_log("pci_reset(): Switchable configuration mechanism\n");
pci_set_pmc(0x00);
}
pci_reset_hard();
}
static void
pci_trc_reset(uint8_t val)
{
if (val & 2) {
dma_reset();
dma_set_at(1);
device_reset_all(DEVICE_ALL);
cpu_alt_reset = 0;
pci_reset();
mem_a20_alt = 0;
mem_a20_recalc();
flushmmucache();
}
resetx86();
}
void
pci_write(uint16_t port, uint8_t val, UNUSED(void *priv))
{
pci_log("PCI: [WB] Mechanism #%i port %04X = %02X\n", ((port >= 0xcfc) && (port <= 0xcff)) ? 1 : 2, port, val);
switch (port) {
case 0xcf8:
if (pci_flags & FLAG_MECHANISM_2) {
pci_func = (val >> 1) & 7;
pci_key_write(val);
pci_log("PCI: Mechanism #2 CF8: %sllocating ports %04X-%04X...\n", (pci_flags & FLAG_CONFIG_IO_ON) ? "A" : "Dea",
pci_base, pci_base + pci_size - 1);
}
break;
case 0xcf9:
if (!(pci_trc_reg & 4) && (val & 4))
pci_trc_reset(val);
pci_trc_reg = val & 0xfd;
if (val & 2)
pci_trc_reg &= 0xfb;
break;
case 0xcfa:
if (pci_flags & FLAG_MECHANISM_2)
pci_bus = val;
break;
case 0xcfb:
if (pci_flags & FLAG_MECHANISM_SWITCH)
pci_set_pmc(val);
break;
2022-09-18 17:11:43 -04:00
case 0xcfc:
case 0xcfd:
case 0xcfe:
case 0xcff:
if ((pci_flags & FLAG_MECHANISM_1) && (pci_flags & FLAG_CONFIG_M1_IO_ON))
pci_reg_write(port, val);
break;
case 0xc000 ... 0xc0ff:
if ((pci_flags & FLAG_MECHANISM_2) && (pci_flags & (FLAG_CONFIG_IO_ON | FLAG_CONFIG_DEV0_IO_ON)))
pci_reg_write(port, val);
2022-09-18 17:11:43 -04:00
break;
2023-06-28 13:46:28 -04:00
case 0xc100 ... 0xcfff:
if ((pci_flags & FLAG_MECHANISM_2) && (pci_flags & FLAG_CONFIG_IO_ON))
pci_reg_write(port, val);
2023-06-28 13:46:28 -04:00
break;
2023-08-21 20:25:33 -04:00
default:
break;
}
}
void
pci_writew(uint16_t port, uint16_t val, UNUSED(void *priv))
{
if (port & 0x0001) {
/* Non-aligned access, split into two byte accesses. */
pci_write(port, val & 0xff, priv);
pci_write(port + 1, val >> 8, priv);
} else {
/* Aligned access, still split because we cheat. */
switch (port) {
case 0xcfc:
case 0xcfe:
case 0xc000 ... 0xcffe:
pci_write(port, val & 0xff, priv);
pci_write(port + 1, val >> 8, priv);
break;
2023-08-21 20:25:33 -04:00
default:
break;
}
}
}
void
pci_writel(uint16_t port, uint32_t val, UNUSED(void *priv))
{
if (port & 0x0003) {
/* Non-aligned access, split into two word accesses. */
pci_writew(port, val & 0xffff, priv);
pci_writew(port + 2, val >> 16, priv);
} else {
/* Aligned access. */
switch (port) {
case 0xcf8:
/* No split here, actual 32-bit access. */
if (pci_flags & FLAG_MECHANISM_1) {
pci_log("PCI: [WL] Mechanism #1 port 0CF8 = %08X\n", val);
pci_index = val & 0xff;
pci_func = (val >> 8) & 7;
pci_card = (val >> 11) & 31;
pci_bus = (val >> 16) & 0xff;
pci_enable = (val & PCI_ENABLED);
if (pci_enable)
pci_flags |= FLAG_CONFIG_M1_IO_ON;
else
pci_flags &= ~FLAG_CONFIG_M1_IO_ON;
break;
}
break;
case 0xcfc:
case 0xc000 ... 0xcffc:
/* Still split because we cheat. */
pci_writew(port, val & 0xffff, priv);
pci_writew(port + 2, val >> 16, priv);
break;
2023-08-21 20:25:33 -04:00
default:
break;
}
}
}
2018-10-21 20:27:57 +02:00
static uint8_t
pci_reg_read(uint16_t port)
{
2018-10-21 20:27:57 +02:00
uint8_t slot = 0;
2022-09-18 17:11:43 -04:00
uint8_t ret = 0xff;
if (port >= 0xc000) {
pci_card = (port >> 8) & 0xf;
pci_index = port & 0xfc;
}
slot = pci_card_to_slot_mapping[pci_bus_number_to_index_mapping[pci_bus]][pci_card];
if (slot != PCI_CARD_INVALID) {
if (pci_cards[slot].read)
ret = pci_cards[slot].read(pci_func, pci_index | (port & 0x03), pci_cards[slot].priv);
}
pci_log("PCI: [RB] Mechanism #%i, slot %02X, %s card %02X:%02X, function %02X, index %02X = %02X\n",
(port >= 0xc000) ? 2 : 1, slot,
(slot == PCI_CARD_INVALID) ? "non-existent" : (pci_cards[slot].read ? "used" : "unused"),
pci_card, pci_bus, pci_func, pci_index | (port & 0x03), ret);
return ret;
}
uint8_t
pci_read(uint16_t port, UNUSED(void *priv))
{
uint8_t ret = 0xff;
2018-10-21 20:27:57 +02:00
switch (port) {
case 0xcf8:
if (pci_flags & FLAG_MECHANISM_2)
ret = pci_key | (pci_func << 1);
break;
case 0xcf9:
ret = pci_trc_reg & 0xfb;
break;
case 0xcfa:
if (pci_flags & FLAG_MECHANISM_2)
ret = pci_bus;
break;
case 0xcfb:
if (pci_flags & FLAG_MECHANISM_SWITCH)
ret = pci_pmc;
break;
2022-09-18 17:11:43 -04:00
case 0xcfc:
case 0xcfd:
case 0xcfe:
case 0xcff:
if ((pci_flags & FLAG_MECHANISM_1) && (pci_flags & FLAG_CONFIG_M1_IO_ON))
ret = pci_reg_read(port);
break;
2022-09-18 17:11:43 -04:00
case 0xc000 ... 0xc0ff:
if ((pci_flags & FLAG_MECHANISM_2) && (pci_flags & (FLAG_CONFIG_IO_ON | FLAG_CONFIG_DEV0_IO_ON)))
ret = pci_reg_read(port);
break;
case 0xc100 ... 0xcfff:
if ((pci_flags & FLAG_MECHANISM_2) && (pci_flags & FLAG_CONFIG_IO_ON))
ret = pci_reg_read(port);
2023-06-28 13:46:28 -04:00
break;
default:
break;
2018-10-21 20:27:57 +02:00
}
pci_log("PCI: [RB] Mechanism #%i port %04X = %02X\n", ((port >= 0xcfc) && (port <= 0xcff)) ? 1 : 2, port, ret);
2020-09-19 00:56:12 -03:00
return ret;
}
uint16_t
2023-06-28 13:46:28 -04:00
pci_readw(uint16_t port, UNUSED(void *priv))
{
2022-09-18 17:11:43 -04:00
uint16_t ret = 0xffff;
if (port & 0x0001) {
/* Non-aligned access, split into two byte accesses. */
ret = pci_read(port, priv);
ret |= ((uint16_t) pci_read(port + 1, priv)) << 8;
} else {
/* Aligned access, still split because we cheat. */
switch (port) {
case 0xcfc:
case 0xcfe:
case 0xc000 ... 0xcffe:
ret = pci_read(port, priv);
ret |= ((uint16_t) pci_read(port + 1, priv)) << 8;
break;
2023-08-21 20:25:33 -04:00
default:
break;
}
}
return ret;
}
uint32_t
2023-06-28 13:46:28 -04:00
pci_readl(uint16_t port, UNUSED(void *priv))
{
2022-09-18 17:11:43 -04:00
uint32_t ret = 0xffffffff;
if (port & 0x0003) {
/* Non-aligned access, split into two word accesses. */
ret = pci_readw(port, priv);
ret |= ((uint32_t) pci_readw(port + 2, priv)) << 16;
} else {
/* Aligned access. */
switch (port) {
case 0xcf8:
/* No split here, actual 32-bit access. */
if (pci_flags & FLAG_MECHANISM_1) {
ret = pci_index | (pci_func << 8) | (pci_card << 11) | (pci_bus << 16);
if (pci_flags & FLAG_CONFIG_M1_IO_ON)
ret |= PCI_ENABLED;
pci_log("PCI: [RL] Mechanism #1 port 0CF8 = %08X\n", ret);
return ret;
2022-09-18 17:11:43 -04:00
}
break;
case 0xcfc:
case 0xc000 ... 0xcffc:
/* Still split because we cheat. */
ret = pci_readw(port, priv);
ret |= ((uint32_t) pci_readw(port + 2, priv)) << 16;
break;
}
2021-04-06 07:30:11 +02:00
}
return ret;
2021-04-06 07:30:11 +02:00
}
uint8_t
pci_register_bus(void)
{
return last_pci_bus++;
}
void
pci_remap_bus(uint8_t bus_index, uint8_t bus_number)
{
uint8_t i = 1;
do {
if (pci_bus_number_to_index_mapping[i] == bus_index)
pci_bus_number_to_index_mapping[i] = PCI_BUS_INVALID;
} while (i++ < 0xff);
if ((bus_number > 0) && (bus_number < 0xff))
pci_bus_number_to_index_mapping[bus_number] = bus_index;
}
void
pci_register_bus_slot(int bus, int card, int type, int inta, int intb, int intc, int intd)
2021-04-06 07:30:11 +02:00
{
pci_card_t *dev = &pci_cards[last_pci_card];
2021-04-06 07:30:11 +02:00
dev->bus = bus;
dev->id = card;
dev->type = type;
dev->irq_routing[0] = inta;
dev->irq_routing[1] = intb;
dev->irq_routing[2] = intc;
dev->irq_routing[3] = intd;
dev->read = NULL;
dev->write = NULL;
dev->priv = NULL;
pci_card_to_slot_mapping[bus][card] = last_pci_card;
pci_log("pci_register_slot(): pci_cards[%i].bus = %02X; .id = %02X\n", last_pci_card, bus, card);
2018-10-21 20:27:57 +02:00
if (type == PCI_CARD_NORMAL) {
last_normal_pci_card++;
/* This is needed to know at what position to add the bridge. */
last_normal_pci_card_id = last_pci_card;
}
last_pci_card++;
}
static uint8_t
pci_find_slot(uint8_t add_type, uint8_t ignore_slot)
{
const pci_card_t *dev;
/* Is the device being added with a strict slot type matching requirement? */
uint8_t strict = (add_type & PCI_ADD_STRICT);
/* The actual type of the device being added, with the strip flag, if any,
masked. */
uint8_t masked_add_type = (add_type & PCI_ADD_MASK);
/* Is the device being added normal, ie. without the possibility of ever
being used as an on-board device? */
uint8_t normal_add_type = (masked_add_type >= PCI_CARD_NORMAL);
uint8_t match;
uint8_t normal;
uint8_t empty;
uint8_t process;
uint8_t ret = PCI_CARD_INVALID;
/* Iterate i until we have either exhausted all the slot or the value of
ret has changed to something other than PCI_CARD_INVALID. */
for (uint8_t i = 0; (ret == PCI_CARD_INVALID) && (i < last_pci_card); i++) {
dev = &pci_cards[i];
/* Is the slot we are looking at of the exact same type as the device being
added? */
match = (dev->type == masked_add_type);
/* Is the slot we are looking at a normal slot (ie. not an on-board chip)? */
normal = (dev->type == PCI_CARD_NORMAL);
/* Is the slot we are looking at empty? */
empty = !dev->read && !dev->write;
/* Should we process this slot, ie. were we told to ignore it, if any at all? */
process = (ignore_slot == PCI_IGNORE_NO_SLOT) || (i != ignore_slot);
/* This condition is now refactored and made to be easily human-readable. */
if (empty && process && (match || (!strict && normal && normal_add_type)))
ret = i;
}
return ret;
}
/* Add a PCI card. */
void
pci_add_card(uint8_t add_type, uint8_t (*read)(int func, int addr, void *priv),
void (*write)(int func, int addr, uint8_t val, void *priv), void *priv, uint8_t *slot)
{
pci_card_desc_t *dev;
pci_log("pci_add_card(): PCI card #%02i: type = %i\n", next_pci_card, add_type);
if (next_pci_card < PCI_CARDS_NUM) {
dev = &pci_card_descs[next_pci_card];
dev->type = add_type;
dev->read = read;
dev->write = write;
dev->priv = priv;
dev->slot = slot;
*(dev->slot) = PCI_CARD_INVALID;
next_pci_card++;
if (add_type == PCI_ADD_NORMAL)
normal_pci_cards++;
2018-10-21 20:27:57 +02:00
}
}
static void
2023-08-21 20:25:33 -04:00
pci_clear_card(UNUSED(int pci_card))
{
pci_card_desc_t *dev;
2018-10-21 20:27:57 +02:00
if (next_pci_card < PCI_CARDS_NUM) {
dev = &pci_card_descs[next_pci_card];
memset(dev, 0x00, sizeof(pci_card_desc_t));
2018-10-21 20:27:57 +02:00
}
}
static uint8_t
pci_register_card(int pci_card)
2018-10-21 20:27:57 +02:00
{
pci_card_desc_t *dev;
pci_card_t *card;
uint8_t i;
uint8_t ret = PCI_CARD_INVALID;
if (pci_card < PCI_CARDS_NUM) {
dev = &pci_card_descs[pci_card];
2022-09-18 17:11:43 -04:00
if (last_pci_card) {
/* First, find the next available slot. */
i = pci_find_slot(dev->type, 0xff);
2022-09-18 17:11:43 -04:00
if (i != PCI_CARD_INVALID) {
card = &pci_cards[i];
card->read = dev->read;
card->write = dev->write;
card->priv = dev->priv;
card->type |= (dev->type & PCI_CARD_VFIO);
2022-09-18 17:11:43 -04:00
*(dev->slot) = i;
ret = i;
}
2022-09-18 17:11:43 -04:00
}
2018-10-21 20:27:57 +02:00
pci_clear_card(pci_card);
2018-10-21 20:27:57 +02:00
}
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
return ret;
}
/* Add an instance of the PCI bridge. */
void
pci_add_bridge(uint8_t agp, uint8_t (*read)(int func, int addr, void *priv), void (*write)(int func, int addr, uint8_t val, void *priv), void *priv, uint8_t *slot)
2020-09-19 00:56:12 -03:00
{
pci_card_t *card;
uint8_t bridge_slot = agp ? pci_find_slot(PCI_ADD_AGPBRIDGE, 0xff) : last_normal_pci_card_id;
2020-09-19 00:56:12 -03:00
if (bridge_slot != PCI_CARD_INVALID) {
card = &pci_cards[bridge_slot];
card->read = read;
card->write = write;
card->priv = priv;
}
*slot = bridge_slot;
}
/* Register the cards that have been added into slots. */
2021-12-19 23:41:23 +01:00
void
pci_register_cards(void)
2021-12-19 23:41:23 +01:00
{
uint8_t normal;
#ifdef ENABLE_PCI_LOG
uint8_t type;
uint8_t *slot;
#endif
2021-12-19 23:41:23 +01:00
next_normal_pci_card = 0;
if (next_pci_card > 0) {
2023-08-21 20:25:33 -04:00
for (uint8_t i = 0; i < next_pci_card; i++) {
#ifdef ENABLE_PCI_LOG
type = pci_card_descs[i].type;
slot = pci_card_descs[i].slot;
#endif
normal = (pci_card_descs[i].type == PCI_CARD_NORMAL);
/* If this is a normal card, increase the next normal card index. */
if (normal)
next_normal_pci_card++;
/* If this is a normal card and the next one is going to be beyond the last slot,
add the bridge. */
if (normal && (next_normal_pci_card >= last_normal_pci_card) &&
(normal_pci_cards > last_normal_pci_card) && !(pci_flags & FLAG_NO_BRIDGES))
device_add_inst(&dec21150_device, last_pci_bus);
pci_register_card(i);
pci_log("pci_register_cards(): PCI card #%02i: type = %02X, pci device = %02X:%02X\n",
i, type, pci_cards[*slot].bus, pci_cards[*slot].id);
}
}
next_pci_card = 0;
normal_pci_cards = 0;
next_normal_pci_card = 0;
}
2018-10-21 20:27:57 +02:00
static void
pci_slots_clear(void)
{
2023-05-11 03:02:36 -04:00
uint8_t i;
2023-06-09 23:46:54 -04:00
2020-09-19 00:56:12 -03:00
last_pci_card = last_normal_pci_card = 0;
last_normal_pci_card = 0;
2022-09-18 17:11:43 -04:00
last_pci_bus = 1;
next_pci_card = 0;
normal_pci_cards = 0;
next_normal_pci_card = 0;
for (i = 0; i < PCI_CARDS_NUM; i++)
2022-09-18 17:11:43 -04:00
pci_clear_slot(i);
2020-09-19 00:56:12 -03:00
i = 0;
do {
for (uint8_t j = 0; j < PCI_CARDS_NUM; j++)
pci_card_to_slot_mapping[i][j] = PCI_CARD_INVALID;
pci_bus_number_to_index_mapping[i] = PCI_BUS_INVALID;
2020-09-19 00:56:12 -03:00
} while (i++ < 0xff);
pci_bus_number_to_index_mapping[0] = 0; /* always map bus 0 to index 0 */
}
void
pci_init(int flags)
{
2018-10-21 20:27:57 +02:00
int c;
pci_base = 0xc000;
pci_size = 0x1000;
2018-10-21 20:27:57 +02:00
pci_slots_clear();
pci_reset_hard();
pci_trc_reg = 0;
pci_flags = flags;
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
if (pci_flags & FLAG_NO_IRQ_STEERING) {
2022-09-18 17:11:43 -04:00
pic_elcr_io_handler(0);
pic_elcr_set_enabled(0);
} else {
2022-09-18 17:11:43 -04:00
pic_elcr_io_handler(1);
pic_elcr_set_enabled(1);
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
}
2018-10-21 20:27:57 +02:00
pci_pmc = (pci_flags & FLAG_MECHANISM_1) ? 0x01 : 0x00;
if ((pci_flags & FLAG_MECHANISM_2) && (pci_flags & FLAG_CONFIG_DEV0_IO_ON)) {
pci_log("PCI: Always expose device 0\n");
pci_base = 0xc100;
pci_size = 0x0f00;
2018-10-21 20:27:57 +02:00
}
if (pci_flags & FLAG_MECHANISM_SWITCH) {
pci_log("PCI: Switchable configuration mechanism\n");
pci_set_pmc(pci_pmc);
} else
pci_io_handlers(1);
for (c = 0; c < PCI_IRQS_NUM; c++) {
2022-09-18 17:11:43 -04:00
pci_irqs[c] = PCI_IRQ_DISABLED;
pci_irq_level[c] = (pci_flags & FLAG_NO_IRQ_STEERING) ? 0 : 1;
}
2018-10-21 20:27:57 +02:00
for (c = 0; c < PCI_MIRQS_NUM; c++) {
2022-09-18 17:11:43 -04:00
pci_mirqs[c].enabled = 0;
pci_mirqs[c].irq_line = PCI_IRQ_DISABLED;
2018-10-21 20:27:57 +02:00
}
pic_set_pci_flag(1);
}