PCI support

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@818 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2004-05-18 23:05:28 +00:00
parent 158156d13d
commit 69b910399a
10 changed files with 842 additions and 30 deletions

View file

@ -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:

View file

@ -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

110
hw/ide.c
View file

@ -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]);
}

View file

@ -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);
}

37
hw/pc.c
View file

@ -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();
}
}

367
hw/pci.c Normal file
View file

@ -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]);
}
}
}
}

107
hw/pci2isa.c Normal file
View file

@ -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);
}

View file

@ -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();

23
vl.c
View file

@ -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;
}
}
}

65
vl.h
View file

@ -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 */