/* * 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 the PCI bus. * * * * Authors: Miran Grca, * * Copyright 2023 Miran Grca. */ #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/dma.h> #include <86box/pci.h> #include <86box/keyboard.h> #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; 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) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # 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]; } static void pci_clear_slot(int card) { pci_card_to_slot_mapping[pci_cards[card].bus][pci_cards[card].id] = PCI_CARD_INVALID; pci_cards[card].id = 0xff; pci_cards[card].type = 0xff; for (uint8_t i = 0; i < 4; i++) pci_cards[card].irq_routing[i] = 0; pci_cards[card].read = NULL; pci_cards[card].write = NULL; pci_cards[card].priv = NULL; } /* Relocate a PCI device to a new slot, required for the configurable IDSEL's of ALi M1543(c). */ void pci_relocate_slot(int type, int new_slot) { int card = -1; int old_slot; uint8_t mapping; if ((new_slot < 0) || (new_slot > 31)) return; for (uint8_t i = 0; i < PCI_CARDS_NUM; i++) { if ((pci_cards[i].bus == 0) && (pci_cards[i].type == type)) { card = i; break; } } if (card == -1) return; old_slot = pci_cards[card].id; pci_cards[card].id = new_slot; mapping = pci_card_to_slot_mapping[0][old_slot]; pci_card_to_slot_mapping[0][old_slot] = PCI_CARD_INVALID; pci_card_to_slot_mapping[0][new_slot] = mapping; } /* 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; } 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); } static void pci_reg_write(uint16_t port, uint8_t val) { 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); } static void pci_reset_regs(void) { pci_index = pci_card = pci_func = pci_bus = pci_key = 0; pci_enable = 0x00000000; pci_flags &= ~(FLAG_CONFIG_IO_ON | FLAG_CONFIG_M1_IO_ON); } 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; picintc(1 << i); } } pci_pic_reset(); } 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; 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); break; case 0xc100 ... 0xcfff: if ((pci_flags & FLAG_MECHANISM_2) && (pci_flags & FLAG_CONFIG_IO_ON)) pci_reg_write(port, val); 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; } } } 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; } } } static uint8_t pci_reg_read(uint16_t port) { uint8_t slot = 0; 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; 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; 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; 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); break; default: break; } pci_log("PCI: [RB] Mechanism #%i port %04X = %02X\n", ((port >= 0xcfc) && (port <= 0xcff)) ? 1 : 2, port, ret); return ret; } uint16_t pci_readw(uint16_t port, UNUSED(void *priv)) { 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; } } return ret; } uint32_t pci_readl(uint16_t port, UNUSED(void *priv)) { 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_IO_ON) ret |= PCI_ENABLED; pci_log("PCI: [RL] Mechanism #1 port 0CF8 = %08X\n", ret); return ret; } 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; } } return ret; } 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) { pci_card_t *dev = &pci_cards[last_pci_card]; 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); 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++; } } static void pci_clear_card(int pci_card) { pci_card_desc_t *dev; if (next_pci_card < PCI_CARDS_NUM) { dev = &pci_card_descs[next_pci_card]; memset(dev, 0x00, sizeof(pci_card_desc_t)); } } static uint8_t pci_register_card(int pci_card) { 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]; if (last_pci_card) { /* First, find the next available slot. */ i = pci_find_slot(dev->type, 0xff); 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); *(dev->slot) = i; ret = i; } } pci_clear_card(pci_card); } return ret; } /* Add an instance of the PCI bridge. */ uint8_t pci_add_bridge(uint8_t (*read)(int func, int addr, void *priv), void (*write)(int func, int addr, uint8_t val, void *priv), void *priv) { pci_card_t *card; card = &pci_cards[last_normal_pci_card_id]; card->read = read; card->write = write; card->priv = priv; return last_normal_pci_card_id; } /* Register the cards that have been added into slots. */ void pci_register_cards(void) { uint8_t i; uint8_t normal; #ifdef ENABLE_PCI_LOG uint8_t type; uint8_t *slot; #endif next_normal_pci_card = 0; if (next_pci_card > 0) { for (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; } static void pci_slots_clear(void) { uint8_t i; last_pci_card = last_normal_pci_card = 0; last_normal_pci_card = 0; 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++) pci_clear_slot(i); 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; } while (i++ < 0xff); pci_bus_number_to_index_mapping[0] = 0; /* always map bus 0 to index 0 */ } void pci_init(int flags) { int c; pci_base = 0xc000; pci_size = 0x1000; pci_slots_clear(); pci_reset_hard(); pci_trc_reg = 0; pci_flags = flags; if (pci_flags & FLAG_NO_IRQ_STEERING) { pic_elcr_io_handler(0); pic_elcr_set_enabled(0); } else { pic_elcr_io_handler(1); pic_elcr_set_enabled(1); } 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; } 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++) { pci_irqs[c] = PCI_IRQ_DISABLED; pci_irq_level[c] = (pci_flags & FLAG_NO_IRQ_STEERING) ? 0 : 1; } for (c = 0; c < PCI_MIRQS_NUM; c++) { pci_mirqs[c].enabled = 0; pci_mirqs[c].irq_line = PCI_IRQ_DISABLED; } pic_set_pci_flag(1); }