From 69b910399a3c40620a5213adaeb14a37366d97ac Mon Sep 17 00:00:00 2001 From: bellard Date: Tue, 18 May 2004 23:05:28 +0000 Subject: [PATCH] PCI support git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@818 c046a42c-6fe2-441c-8c8c-71466251a162 --- Changelog | 3 + Makefile.target | 2 +- hw/ide.c | 110 ++++++++++++++- hw/ne2000.c | 152 +++++++++++++++++++- hw/pc.c | 37 +++-- hw/pci.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/pci2isa.c | 107 ++++++++++++++ hw/ppc_prep.c | 6 +- vl.c | 23 ++- vl.h | 65 ++++++++- 10 files changed, 842 insertions(+), 30 deletions(-) create mode 100644 hw/pci.c create mode 100644 hw/pci2isa.c diff --git a/Changelog b/Changelog index 6f8b3591e7..c55362c283 100644 --- a/Changelog +++ b/Changelog @@ -8,6 +8,9 @@ version 0.5.6: - int13 CDROM BIOS fix (aka Solaris x86 install CD fix) - int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix) - BSR/BSF "undefined behaviour" fix + - vmdk2raw: convert VMware disk images to raw images + - PCI support + - NE2K PCI support version 0.5.5: diff --git a/Makefile.target b/Makefile.target index 84f9df8a57..949af323a3 100644 --- a/Makefile.target +++ b/Makefile.target @@ -232,7 +232,7 @@ ifeq ($(ARCH),alpha) endif # must use static linking to avoid leaving stuff in virtual address space -VL_OBJS=vl.o osdep.o block.o monitor.o +VL_OBJS=vl.o osdep.o block.o monitor.o pci.o pci2isa.o ifeq ($(TARGET_ARCH), i386) # Hardware support diff --git a/hw/ide.c b/hw/ide.c index 6f0db4aaa6..b2d902f20a 100644 --- a/hw/ide.c +++ b/hw/ide.c @@ -1433,18 +1433,14 @@ static void ide_guess_geometry(IDEState *s) } } -void ide_init(int iobase, int iobase2, int irq, - BlockDriverState *hd0, BlockDriverState *hd1) +static void ide_init2(IDEState *ide_state, int irq, + BlockDriverState *hd0, BlockDriverState *hd1) { - IDEState *s, *ide_state; + IDEState *s; static int drive_serial = 1; int i, cylinders, heads, secs; int64_t nb_sectors; - ide_state = qemu_mallocz(sizeof(IDEState) * 2); - if (!ide_state) - return; - for(i = 0; i < 2; i++) { s = ide_state + i; if (i == 0) @@ -1483,6 +1479,22 @@ void ide_init(int iobase, int iobase2, int irq, s->irq = irq; ide_reset(s); } +} + +/***********************************************************/ +/* ISA IDE definitions */ + +void isa_ide_init(int iobase, int iobase2, int irq, + BlockDriverState *hd0, BlockDriverState *hd1) +{ + IDEState *ide_state; + + ide_state = qemu_mallocz(sizeof(IDEState) * 2); + if (!ide_state) + return; + + ide_init2(ide_state, irq, hd0, hd1); + register_ioport_write(iobase, 8, 1, ide_ioport_write, ide_state); register_ioport_read(iobase, 8, 1, ide_ioport_read, ide_state); if (iobase2) { @@ -1496,3 +1508,87 @@ void ide_init(int iobase, int iobase2, int irq, register_ioport_write(iobase, 4, 4, ide_data_writel, ide_state); register_ioport_read(iobase, 4, 4, ide_data_readl, ide_state); } + +/***********************************************************/ +/* PCI IDE definitions */ + +typedef struct PCIIDEState { + PCIDevice dev; + IDEState ide_if[4]; +} PCIIDEState; + +static uint32_t ide_read_config(PCIDevice *d, + uint32_t address, int len) +{ + uint32_t val; + val = 0; + memcpy(&val, d->config + address, len); + return val; +} + +static void ide_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + memcpy(d->config + address, &val, len); +} + +static void ide_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCIIDEState *d = (PCIIDEState *)pci_dev; + IDEState *ide_state; + + if (region_num <= 3) { + ide_state = &d->ide_if[(region_num >> 1) * 2]; + if (region_num & 1) { + register_ioport_read(addr + 2, 1, 1, ide_status_read, ide_state); + register_ioport_write(addr + 2, 1, 1, ide_cmd_write, ide_state); + } else { + register_ioport_write(addr, 8, 1, ide_ioport_write, ide_state); + register_ioport_read(addr, 8, 1, ide_ioport_read, ide_state); + + /* data ports */ + register_ioport_write(addr, 2, 2, ide_data_writew, ide_state); + register_ioport_read(addr, 2, 2, ide_data_readw, ide_state); + register_ioport_write(addr, 4, 4, ide_data_writel, ide_state); + register_ioport_read(addr, 4, 4, ide_data_readl, ide_state); + } + } +} + +/* hd_table must contain 4 block drivers */ +void pci_ide_init(BlockDriverState **hd_table) +{ + PCIIDEState *d; + uint8_t *pci_conf; + + d = (PCIIDEState *)pci_register_device("IDE", sizeof(PCIIDEState), + 0, -1, + ide_read_config, + ide_write_config); + pci_conf = d->dev.config; + pci_conf[0x00] = 0x86; // Intel + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x00; // fake + pci_conf[0x03] = 0x01; // fake + pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE + pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage + pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic + + pci_conf[0x2c] = 0x86; // subsys vendor + pci_conf[0x2d] = 0x80; // subsys vendor + pci_conf[0x2e] = 0x00; // fake + pci_conf[0x2f] = 0x01; // fake + + pci_register_io_region((PCIDevice *)d, 0, 0x8, + PCI_ADDRESS_SPACE_IO, ide_map); + pci_register_io_region((PCIDevice *)d, 1, 0x4, + PCI_ADDRESS_SPACE_IO, ide_map); + pci_register_io_region((PCIDevice *)d, 2, 0x8, + PCI_ADDRESS_SPACE_IO, ide_map); + pci_register_io_region((PCIDevice *)d, 3, 0x4, + PCI_ADDRESS_SPACE_IO, ide_map); + + ide_init2(&d->ide_if[0], 14, hd_table[0], hd_table[1]); + ide_init2(&d->ide_if[2], 15, hd_table[2], hd_table[3]); +} diff --git a/hw/ne2000.c b/hw/ne2000.c index 5997e8865e..8ecbf55fc1 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -368,7 +368,7 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) } static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr, - uint32_t val) + uint32_t val) { if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { @@ -382,8 +382,17 @@ static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr, addr &= ~1; /* XXX: check exact behaviour if not even */ if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { - s->mem[addr] = val; - s->mem[addr + 1] = val >> 8; + *(uint16_t *)(s->mem + addr) = cpu_to_le16(val); + } +} + +static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, + uint32_t val) +{ + addr &= ~3; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + *(uint32_t *)(s->mem + addr) = cpu_to_le32(val); } } @@ -402,12 +411,23 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) addr &= ~1; /* XXX: check exact behaviour if not even */ if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { - return s->mem[addr] | (s->mem[addr + 1] << 8); + return le16_to_cpu(*(uint16_t *)(s->mem + addr)); } else { return 0xffff; } } +static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) +{ + addr &= ~3; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return le32_to_cpu(*(uint32_t *)(s->mem + addr)); + } else { + return 0xffffffff; + } +} + static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val) { NE2000State *s = opaque; @@ -468,6 +488,53 @@ static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr) return ret; } +static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + NE2000State *s = opaque; + +#ifdef DEBUG_NE2000 + printf("NE2000: asic writel val=0x%04x\n", val); +#endif + if (s->rcnt == 0) + return; + /* 32 bit access */ + ne2000_mem_writel(s, s->rsar, val); + s->rsar += 4; + s->rcnt -= 4; + /* wrap */ + if (s->rsar == s->stop) + s->rsar = s->start; + if (s->rcnt == 0) { + /* signal end of transfert */ + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } +} + +static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr) +{ + NE2000State *s = opaque; + int ret; + + /* 32 bit access */ + ret = ne2000_mem_readl(s, s->rsar); + s->rsar += 4; + s->rcnt -= 4; + + /* wrap */ + if (s->rsar == s->stop) + s->rsar = s->start; + if (s->rcnt == 0) { + /* signal end of transfert */ + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } +#ifdef DEBUG_NE2000 + printf("NE2000: asic readl val=0x%04x\n", ret); +#endif + return ret; +} + static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val) { /* nothing to do (end of reset pulse) */ @@ -480,7 +547,7 @@ static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr) return 0; } -void ne2000_init(int base, int irq, NetDriverState *nd) +void isa_ne2000_init(int base, int irq, NetDriverState *nd) { NE2000State *s; @@ -505,3 +572,78 @@ void ne2000_init(int base, int irq, NetDriverState *nd) qemu_add_read_packet(nd, ne2000_can_receive, ne2000_receive, s); } + +/***********************************************************/ +/* PCI NE2000 definitions */ + +typedef struct PCINE2000State { + PCIDevice dev; + NE2000State ne2000; +} PCINE2000State; + +static uint32_t ne2000_read_config(PCIDevice *d, + uint32_t address, int len) +{ + uint32_t val; + val = 0; + memcpy(&val, d->config + address, len); + return val; +} + +static void ne2000_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + memcpy(d->config + address, &val, len); +} + +static void ne2000_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCINE2000State *d = (PCINE2000State *)pci_dev; + NE2000State *s = &d->ne2000; + + register_ioport_write(addr, 16, 1, ne2000_ioport_write, s); + register_ioport_read(addr, 16, 1, ne2000_ioport_read, s); + + register_ioport_write(addr + 0x10, 1, 1, ne2000_asic_ioport_write, s); + register_ioport_read(addr + 0x10, 1, 1, ne2000_asic_ioport_read, s); + register_ioport_write(addr + 0x10, 2, 2, ne2000_asic_ioport_write, s); + register_ioport_read(addr + 0x10, 2, 2, ne2000_asic_ioport_read, s); + register_ioport_write(addr + 0x10, 4, 4, ne2000_asic_ioport_writel, s); + register_ioport_read(addr + 0x10, 4, 4, ne2000_asic_ioport_readl, s); + + register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s); + register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s); +} + +void pci_ne2000_init(NetDriverState *nd) +{ + PCINE2000State *d; + NE2000State *s; + uint8_t *pci_conf; + + d = (PCINE2000State *)pci_register_device("NE2000", sizeof(PCINE2000State), + 0, -1, + ne2000_read_config, + ne2000_write_config); + pci_conf = d->dev.config; + pci_conf[0x00] = 0xec; // Realtek 8029 + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0x29; + pci_conf[0x03] = 0x80; + pci_conf[0x0a] = 0x00; // ethernet network controller + pci_conf[0x0b] = 0x02; + pci_conf[0x0e] = 0x00; // header_type + + /* XXX: do that in the BIOS */ + pci_conf[0x3c] = 11; // interrupt line + pci_conf[0x3d] = 1; // interrupt pin + + pci_register_io_region((PCIDevice *)d, 0, 0x100, + PCI_ADDRESS_SPACE_IO, ne2000_map); + s = &d->ne2000; + s->irq = 11; + s->nd = nd; + ne2000_reset(s); + qemu_add_read_packet(nd, ne2000_can_receive, ne2000_receive, s); +} diff --git a/hw/pc.c b/hw/pc.c index 239777756f..1139781a0c 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -380,6 +380,11 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x210, 0x01); } + if (pci_enabled) { + i440fx_init(); + piix3_init(); + } + /* init basic PC hardware */ register_ioport_write(0x80, 1, 1, ioport80_write, NULL); @@ -401,17 +406,25 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, fd = serial_open_device(); serial_init(0x3f8, 4, fd); - nb_nics1 = nb_nics; - if (nb_nics1 > NE2000_NB_MAX) - nb_nics1 = NE2000_NB_MAX; - for(i = 0; i < nb_nics1; i++) { - ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]); + if (pci_enabled) { + for(i = 0; i < nb_nics; i++) { + pci_ne2000_init(&nd_table[i]); + } + pci_ide_init(bs_table); + } else { + nb_nics1 = nb_nics; + if (nb_nics1 > NE2000_NB_MAX) + nb_nics1 = NE2000_NB_MAX; + for(i = 0; i < nb_nics1; i++) { + isa_ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]); + } + + for(i = 0; i < 2; i++) { + isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], + bs_table[2 * i], bs_table[2 * i + 1]); + } } - for(i = 0; i < 2; i++) { - ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], - bs_table[2 * i], bs_table[2 * i + 1]); - } kbd_init(); DMA_init(); @@ -426,4 +439,10 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); cmos_init(ram_size, boot_device); + + /* must be done after all PCI devices are instanciated */ + /* XXX: should be done in the Bochs BIOS */ + if (pci_enabled) { + pci_bios_init(); + } } diff --git a/hw/pci.c b/hw/pci.c new file mode 100644 index 0000000000..2c5f30b15b --- /dev/null +++ b/hw/pci.c @@ -0,0 +1,367 @@ +/* + * QEMU PCI bus manager + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_PCI + +typedef struct PCIBridge { + uint32_t config_reg; + PCIDevice **pci_bus[256]; +} PCIBridge; + +static PCIBridge pci_bridge; +target_phys_addr_t pci_mem_base; + +/* -1 for devfn means auto assign */ +PCIDevice *pci_register_device(const char *name, int instance_size, + int bus_num, int devfn, + PCIConfigReadFunc *config_read, + PCIConfigWriteFunc *config_write) +{ + PCIBridge *s = &pci_bridge; + PCIDevice *pci_dev, **bus; + + if (!s->pci_bus[bus_num]) { + s->pci_bus[bus_num] = qemu_mallocz(256 * sizeof(PCIDevice *)); + if (!s->pci_bus[bus_num]) + return NULL; + } + bus = s->pci_bus[bus_num]; + if (devfn < 0) { + for(devfn = 0 ; devfn < 256; devfn += 8) { + if (!bus[devfn]) + goto found; + } + return NULL; + found: ; + } + pci_dev = qemu_mallocz(instance_size); + if (!pci_dev) + return NULL; + pci_dev->bus_num = bus_num; + pci_dev->devfn = devfn; + pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); + pci_dev->config_read = config_read; + pci_dev->config_write = config_write; + bus[devfn] = pci_dev; + return pci_dev; +} + +void pci_register_io_region(PCIDevice *pci_dev, int region_num, + uint32_t size, int type, + PCIMapIORegionFunc *map_func) +{ + PCIIORegion *r; + + if ((unsigned int)region_num >= 6) + return; + r = &pci_dev->io_regions[region_num]; + r->addr = -1; + r->size = size; + r->type = type; + r->map_func = map_func; +} + +static void pci_config_writel(void* opaque, uint32_t addr, uint32_t val) +{ + PCIBridge *s = opaque; + s->config_reg = val; +} + +static uint32_t pci_config_readl(void* opaque, uint32_t addr) +{ + PCIBridge *s = opaque; + return s->config_reg; +} + +static void unmap_region(PCIIORegion *r) +{ + if (r->addr == -1) + return; +#ifdef DEBUG_PCI + printf("unmap addr=%08x size=%08x\n", r->addr, r->size); +#endif + if (r->type & PCI_ADDRESS_SPACE_IO) { + isa_unassign_ioport(r->addr, r->size); + } else { + cpu_register_physical_memory(r->addr + pci_mem_base, r->size, + IO_MEM_UNASSIGNED); + } +} + +static void pci_data_write(void *opaque, uint32_t addr, + uint32_t val, int len) +{ + PCIBridge *s = opaque; + PCIDevice **bus, *pci_dev; + int config_addr, reg; + +#if defined(DEBUG_PCI) && 0 + printf("pci_data_write: addr=%08x val=%08x len=%d\n", + s->config_reg, val, len); +#endif + if (!(s->config_reg & (1 << 31))) { + return; + } + if ((s->config_reg & 0x3) != 0) { + return; + } + bus = s->pci_bus[(s->config_reg >> 16) & 0xff]; + if (!bus) + return; + pci_dev = bus[(s->config_reg >> 8) & 0xff]; + if (!pci_dev) + return; + config_addr = (s->config_reg & 0xfc) | (addr & 3); + +#if defined(DEBUG_PCI) + printf("pci_config_write: %s: addr=%02x val=%08x len=%d\n", + pci_dev->name, config_addr, val, len); +#endif + if (len == 4 && (config_addr >= 0x10 && config_addr < 0x10 + 4 * 6)) { + PCIIORegion *r; + reg = (config_addr - 0x10) >> 2; + r = &pci_dev->io_regions[reg]; + if (r->size == 0) + goto default_config; + if (val != 0xffffffff && val != 0) { + /* XXX: the memory assignment should be global to handle + overlaps, but it is not needed at this stage */ + /* first unmap the old region */ + unmap_region(r); + /* change the address */ + if (r->type & PCI_ADDRESS_SPACE_IO) + r->addr = val & ~0x3; + else + r->addr = val & ~0xf; +#ifdef DEBUG_PCI + printf("map addr=%08x size=%08x type=%d\n", + r->addr, r->size, r->type); +#endif + r->map_func(pci_dev, reg, r->addr, r->size, r->type); + } + /* now compute the stored value */ + val &= ~(r->size - 1); + val |= r->type; + *(uint32_t *)(pci_dev->config + 0x10 + reg * 4) = cpu_to_le32(val); + } else { + default_config: + pci_dev->config_write(pci_dev, config_addr, val, len); + } +} + +static uint32_t pci_data_read(void *opaque, uint32_t addr, + int len) +{ + PCIBridge *s = opaque; + PCIDevice **bus, *pci_dev; + int config_addr; + uint32_t val; + + if (!(s->config_reg & (1 << 31))) + goto fail; + if ((s->config_reg & 0x3) != 0) + goto fail; + bus = s->pci_bus[(s->config_reg >> 16) & 0xff]; + if (!bus) + goto fail; + pci_dev = bus[(s->config_reg >> 8) & 0xff]; + if (!pci_dev) { + fail: + val = 0; + goto the_end; + } + config_addr = (s->config_reg & 0xfc) | (addr & 3); + val = pci_dev->config_read(pci_dev, config_addr, len); +#if defined(DEBUG_PCI) + printf("pci_config_read: %s: addr=%02x val=%08x len=%d\n", + pci_dev->name, config_addr, val, len); +#endif + the_end: +#if defined(DEBUG_PCI) && 0 + printf("pci_data_read: addr=%08x val=%08x len=%d\n", + s->config_reg, val, len); +#endif + return val; +} + +static void pci_data_writeb(void* opaque, uint32_t addr, uint32_t val) +{ + pci_data_write(opaque, addr, val, 1); +} + +static void pci_data_writew(void* opaque, uint32_t addr, uint32_t val) +{ + pci_data_write(opaque, addr, val, 2); +} + +static void pci_data_writel(void* opaque, uint32_t addr, uint32_t val) +{ + pci_data_write(opaque, addr, val, 4); +} + +static uint32_t pci_data_readb(void* opaque, uint32_t addr) +{ + return pci_data_read(opaque, addr, 1); +} + +static uint32_t pci_data_readw(void* opaque, uint32_t addr) +{ + return pci_data_read(opaque, addr, 2); +} + +static uint32_t pci_data_readl(void* opaque, uint32_t addr) +{ + return pci_data_read(opaque, addr, 4); +} + +/* i440FX PCI bridge */ + +static uint32_t i440_read_config(PCIDevice *d, + uint32_t address, int len) +{ + uint32_t val; + val = 0; + memcpy(&val, d->config + address, len); + return val; +} + +static void i440_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + memcpy(d->config + address, &val, len); +} + +void i440fx_init(void) +{ + PCIBridge *s = &pci_bridge; + PCIDevice *d; + + register_ioport_write(0xcf8, 4, 4, pci_config_writel, s); + register_ioport_read(0xcf8, 4, 4, pci_config_readl, s); + + register_ioport_write(0xcfc, 4, 1, pci_data_writeb, s); + register_ioport_write(0xcfc, 4, 2, pci_data_writew, s); + register_ioport_write(0xcfc, 4, 4, pci_data_writel, s); + register_ioport_read(0xcfc, 4, 1, pci_data_readb, s); + register_ioport_read(0xcfc, 4, 2, pci_data_readw, s); + register_ioport_read(0xcfc, 4, 4, pci_data_readl, s); + + d = pci_register_device("i440FX", sizeof(PCIDevice), 0, 0, + i440_read_config, i440_write_config); + + d->config[0x00] = 0x86; // vendor_id + d->config[0x01] = 0x80; + d->config[0x02] = 0x37; // device_id + d->config[0x03] = 0x12; + d->config[0x08] = 0x02; // revision + d->config[0x0a] = 0x04; // class_sub = pci2pci + d->config[0x0b] = 0x06; // class_base = PCI_bridge + d->config[0x0c] = 0x01; // line_size in 32 bit words + d->config[0x0e] = 0x01; // header_type +} + +/* NOTE: the following should be done by the BIOS */ + +static uint32_t pci_bios_io_addr; +static uint32_t pci_bios_mem_addr; + +static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) +{ + PCIBridge *s = &pci_bridge; + PCIIORegion *r; + + s->config_reg = 0x80000000 | (d->bus_num << 16) | + (d->devfn << 8) | (0x10 + region_num * 4); + pci_data_write(s, 0, addr, 4); + r = &d->io_regions[region_num]; + + /* enable memory mappings */ + if (r->type & PCI_ADDRESS_SPACE_IO) + d->config[0x04] |= 1; + else + d->config[0x04] |= 2; +} + + +static void pci_bios_init_device(PCIDevice *d) +{ + int class; + PCIIORegion *r; + uint32_t *paddr; + int i; + + class = d->config[0x0a] | (d->config[0x0b] << 8); + switch(class) { + case 0x0101: + /* IDE: we map it as in ISA mode */ + pci_set_io_region_addr(d, 0, 0x1f0); + pci_set_io_region_addr(d, 1, 0x3f4); + pci_set_io_region_addr(d, 2, 0x170); + pci_set_io_region_addr(d, 3, 0x374); + break; + default: + /* default memory mappings */ + for(i = 0; i < 6; i++) { + r = &d->io_regions[i]; + if (r->size) { + if (r->type & PCI_ADDRESS_SPACE_IO) + paddr = &pci_bios_io_addr; + else + paddr = &pci_bios_mem_addr; + *paddr = (*paddr + r->size - 1) & ~(r->size - 1); + pci_set_io_region_addr(d, i, *paddr); + *paddr += r->size; + } + } + break; + } +} + +/* + * This function initializes the PCI devices as a normal PCI BIOS + * would do. It is provided just in case the BIOS has no support for + * PCI. + */ +void pci_bios_init(void) +{ + PCIBridge *s = &pci_bridge; + PCIDevice **bus; + int bus_num, devfn; + + pci_bios_io_addr = 0xc000; + pci_bios_mem_addr = 0xf0000000; + + for(bus_num = 0; bus_num < 256; bus_num++) { + bus = s->pci_bus[bus_num]; + if (bus) { + for(devfn = 0; devfn < 256; devfn++) { + if (bus[devfn]) + pci_bios_init_device(bus[devfn]); + } + } + } +} + + diff --git a/hw/pci2isa.c b/hw/pci2isa.c new file mode 100644 index 0000000000..939a90db45 --- /dev/null +++ b/hw/pci2isa.c @@ -0,0 +1,107 @@ +/* + * QEMU PCI to ISA bridge + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_PCI + +typedef struct PIIX3State { + PCIDevice dev; + uint8_t elcr1; + uint8_t elcr2; +} PIIX3State; + +static void piix3_reset(PIIX3State *d) +{ + uint8_t *pci_conf = d->dev.config; + + pci_conf[0x04] = 0x07; // master, memory and I/O + pci_conf[0x05] = 0x00; + pci_conf[0x06] = 0x00; + pci_conf[0x07] = 0x02; // PCI_status_devsel_medium + pci_conf[0x4c] = 0x4d; + pci_conf[0x4e] = 0x03; + pci_conf[0x4f] = 0x00; + pci_conf[0x60] = 0x80; + pci_conf[0x69] = 0x02; + pci_conf[0x70] = 0x80; + pci_conf[0x76] = 0x0c; + pci_conf[0x77] = 0x0c; + pci_conf[0x78] = 0x02; + pci_conf[0x79] = 0x00; + pci_conf[0x80] = 0x00; + pci_conf[0x82] = 0x00; + pci_conf[0xa0] = 0x08; + pci_conf[0xa0] = 0x08; + pci_conf[0xa2] = 0x00; + pci_conf[0xa3] = 0x00; + pci_conf[0xa4] = 0x00; + pci_conf[0xa5] = 0x00; + pci_conf[0xa6] = 0x00; + pci_conf[0xa7] = 0x00; + pci_conf[0xa8] = 0x0f; + pci_conf[0xaa] = 0x00; + pci_conf[0xab] = 0x00; + pci_conf[0xac] = 0x00; + pci_conf[0xae] = 0x00; + + d->elcr1 = 0x00; + d->elcr2 = 0x00; +} + +static uint32_t piix3_read_config(PCIDevice *d, + uint32_t address, int len) +{ + uint32_t val; + val = 0; + memcpy(&val, d->config + address, len); + return val; +} + +static void piix3_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + memcpy(d->config + address, &val, len); +} + +void piix3_init(void) +{ + PIIX3State *d; + uint8_t *pci_conf; + + d = (PIIX3State *)pci_register_device("PIIX3", sizeof(PIIX3State), + 0, -1, + piix3_read_config, + piix3_write_config); + pci_conf = d->dev.config; + + pci_conf[0x00] = 0x86; // Intel + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x00; // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) + pci_conf[0x03] = 0x70; + pci_conf[0x0a] = 0x01; // class_sub = PCI_ISA + pci_conf[0x0b] = 0x06; // class_base = PCI_bridge + pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic + + piix3_reset(d); +} diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 8499ba3d1e..02c7f0d456 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -962,13 +962,13 @@ void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, if (nb_nics1 > NE2000_NB_MAX) nb_nics1 = NE2000_NB_MAX; for(i = 0; i < nb_nics1; i++) { - ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]); + isa_ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]); } #endif for(i = 0; i < 2; i++) { - ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], - bs_table[2 * i], bs_table[2 * i + 1]); + isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], + bs_table[2 * i], bs_table[2 * i + 1]); } kbd_init(); AUD_init(); diff --git a/vl.c b/vl.c index 45836fea54..baa09e58ad 100644 --- a/vl.c +++ b/vl.c @@ -121,6 +121,7 @@ SerialState *serial_console; QEMUTimer *gui_timer; int vm_running; int audio_enabled = 0; +int pci_enabled = 0; /***********************************************************/ /* x86 ISA bus support */ @@ -238,6 +239,21 @@ int register_ioport_write(int start, int length, int size, return 0; } +void isa_unassign_ioport(int start, int length) +{ + int i; + + for(i = start; i < start + length; i++) { + ioport_read_table[0][i] = default_ioport_readb; + ioport_read_table[1][i] = default_ioport_readw; + ioport_read_table[2][i] = default_ioport_readl; + + ioport_write_table[0][i] = default_ioport_writeb; + ioport_write_table[1][i] = default_ioport_writew; + ioport_write_table[2][i] = default_ioport_writel; + } +} + void pstrcpy(char *buf, int buf_size, const char *str) { int c; @@ -1973,6 +1989,7 @@ enum { QEMU_OPTION_hdachs, QEMU_OPTION_L, QEMU_OPTION_no_code_copy, + QEMU_OPTION_pci, }; typedef struct QEMUOption { @@ -1999,7 +2016,7 @@ const QEMUOption qemu_options[] = { { "nics", HAS_ARG, QEMU_OPTION_nics}, { "macaddr", HAS_ARG, QEMU_OPTION_macaddr}, - { "n", HAS_ARG, QEMU_OPTION_d }, + { "n", HAS_ARG, QEMU_OPTION_n }, { "tun-fd", HAS_ARG, QEMU_OPTION_tun_fd }, #ifdef CONFIG_SLIRP { "user-net", 0, QEMU_OPTION_user_net }, @@ -2017,6 +2034,7 @@ const QEMUOption qemu_options[] = { { "hdachs", HAS_ARG, QEMU_OPTION_hdachs }, { "L", HAS_ARG, QEMU_OPTION_L }, { "no-code-copy", 0, QEMU_OPTION_no_code_copy }, + { "pci", 0, QEMU_OPTION_pci }, { NULL }, }; @@ -2286,6 +2304,9 @@ int main(int argc, char **argv) case QEMU_OPTION_S: start_emulation = 0; break; + case QEMU_OPTION_pci: + pci_enabled = 1; + break; } } } diff --git a/vl.h b/vl.h index b640ad4e72..0cac4abd26 100644 --- a/vl.h +++ b/vl.h @@ -218,7 +218,7 @@ typedef void QEMUTimerCB(void *opaque); /* The real time clock should be used only for stuff which does not change the virtual machine state, as it is run even if the virtual - machine is stopped. The real time clock has a frequency or 1000 + machine is stopped. The real time clock has a frequency of 1000 Hz. */ extern QEMUClock *rt_clock; @@ -360,6 +360,61 @@ int register_ioport_read(int start, int length, int size, IOPortReadFunc *func, void *opaque); int register_ioport_write(int start, int length, int size, IOPortWriteFunc *func, void *opaque); +void isa_unassign_ioport(int start, int length); + +/* PCI bus */ + +extern int pci_enabled; + +extern target_phys_addr_t pci_mem_base; + +typedef struct PCIDevice PCIDevice; + +typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, + uint32_t address, uint32_t data, int len); +typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, + uint32_t address, int len); +typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type); + +#define PCI_ADDRESS_SPACE_MEM 0x00 +#define PCI_ADDRESS_SPACE_IO 0x01 +#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08 + +typedef struct PCIIORegion { + uint32_t addr; + uint32_t size; + uint8_t type; + PCIMapIORegionFunc *map_func; +} PCIIORegion; + +struct PCIDevice { + /* PCI config space */ + uint8_t config[256]; + + /* the following fields are read only */ + int bus_num; + int devfn; + char name[64]; + PCIIORegion io_regions[6]; + + /* do not access the following fields */ + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; +}; + +PCIDevice *pci_register_device(const char *name, int instance_size, + int bus_num, int devfn, + PCIConfigReadFunc *config_read, + PCIConfigWriteFunc *config_write); + +void pci_register_io_region(PCIDevice *pci_dev, int region_num, + uint32_t size, int type, + PCIMapIORegionFunc *map_func); + +void i440fx_init(void); +void piix3_init(void); +void pci_bios_init(void); /* vga.c */ @@ -397,8 +452,9 @@ void sdl_display_init(DisplayState *ds); extern BlockDriverState *bs_table[MAX_DISKS]; -void ide_init(int iobase, int iobase2, int irq, - BlockDriverState *hd0, BlockDriverState *hd1); +void isa_ide_init(int iobase, int iobase2, int irq, + BlockDriverState *hd0, BlockDriverState *hd1); +void pci_ide_init(BlockDriverState **hd_table); /* oss.c */ typedef enum { @@ -446,7 +502,8 @@ int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num); /* ne2000.c */ -void ne2000_init(int base, int irq, NetDriverState *nd); +void isa_ne2000_init(int base, int irq, NetDriverState *nd); +void pci_ne2000_init(NetDriverState *nd); /* pckbd.c */