📄 pci.c
字号:
/* * 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#define PCI_VENDOR_ID 0x00 /* 16 bits */#define PCI_DEVICE_ID 0x02 /* 16 bits */#define PCI_COMMAND 0x04 /* 16 bits */#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */#define PCI_CLASS_DEVICE 0x0a /* Device class */#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */#define PCI_MIN_GNT 0x3e /* 8 bits */#define PCI_MAX_LAT 0x3f /* 8 bits *//* just used for simpler irq handling. */#define PCI_DEVICES_MAX 64#define PCI_IRQ_WORDS ((PCI_DEVICES_MAX + 31) / 32)struct PCIBus { int bus_num; int devfn_min; void (*set_irq)(PCIDevice *pci_dev, int irq_num, int level); uint32_t config_reg; /* XXX: suppress */ /* low level pic */ SetIRQFunc *low_set_irq; void *irq_opaque; PCIDevice *devices[256];};target_phys_addr_t pci_mem_base;static int pci_irq_index;static uint32_t pci_irq_levels[4][PCI_IRQ_WORDS];static PCIBus *first_bus;static PCIBus *pci_register_bus(void){ PCIBus *bus; bus = qemu_mallocz(sizeof(PCIBus)); first_bus = bus; return bus;}void generic_pci_save(QEMUFile* f, void *opaque){ PCIDevice* s=(PCIDevice*)opaque; qemu_put_buffer(f, s->config, 256);}int generic_pci_load(QEMUFile* f, void *opaque, int version_id){ PCIDevice* s=(PCIDevice*)opaque; if (version_id != 1) return -EINVAL; qemu_get_buffer(f, s->config, 256); return 0;}/* -1 for devfn means auto assign */PCIDevice *pci_register_device(PCIBus *bus, const char *name, int instance_size, int devfn, PCIConfigReadFunc *config_read, PCIConfigWriteFunc *config_write){ PCIDevice *pci_dev; if (pci_irq_index >= PCI_DEVICES_MAX) return NULL; if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) { if (!bus->devices[devfn]) goto found; } return NULL; found: ; } pci_dev = qemu_mallocz(instance_size); if (!pci_dev) return NULL; pci_dev->bus = bus; pci_dev->devfn = devfn; pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); if (!config_read) config_read = pci_default_read_config; if (!config_write) config_write = pci_default_write_config; pci_dev->config_read = config_read; pci_dev->config_write = config_write; pci_dev->irq_index = pci_irq_index++; bus->devices[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; uint32_t addr; if ((unsigned int)region_num >= PCI_NUM_REGIONS) return; r = &pci_dev->io_regions[region_num]; r->addr = -1; r->size = size; r->type = type; r->map_func = map_func; if (region_num == PCI_ROM_SLOT) { addr = 0x30; } else { addr = 0x10 + region_num * 4; } *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);}static void pci_addr_writel(void* opaque, uint32_t addr, uint32_t val){ PCIBus *s = opaque; s->config_reg = val;}static uint32_t pci_addr_readl(void* opaque, uint32_t addr){ PCIBus *s = opaque; return s->config_reg;}static void pci_update_mappings(PCIDevice *d){ PCIIORegion *r; int cmd, i; uint32_t last_addr, new_addr, config_ofs; cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND)); for(i = 0; i < PCI_NUM_REGIONS; i++) { r = &d->io_regions[i]; if (i == PCI_ROM_SLOT) { config_ofs = 0x30; } else { config_ofs = 0x10 + i * 4; } if (r->size != 0) { if (r->type & PCI_ADDRESS_SPACE_IO) { if (cmd & PCI_COMMAND_IO) { new_addr = le32_to_cpu(*(uint32_t *)(d->config + config_ofs)); new_addr = new_addr & ~(r->size - 1); last_addr = new_addr + r->size - 1; /* NOTE: we have only 64K ioports on PC */ if (last_addr <= new_addr || new_addr == 0 || last_addr >= 0x10000) { new_addr = -1; } } else { new_addr = -1; } } else { if (cmd & PCI_COMMAND_MEMORY) { new_addr = le32_to_cpu(*(uint32_t *)(d->config + config_ofs)); /* the ROM slot has a specific enable bit */ if (i == PCI_ROM_SLOT && !(new_addr & 1)) goto no_mem_map; new_addr = new_addr & ~(r->size - 1); last_addr = new_addr + r->size - 1; /* NOTE: we do not support wrapping */ /* XXX: as we cannot support really dynamic mappings, we handle specific values as invalid mappings. */ if (last_addr <= new_addr || new_addr == 0 || last_addr == -1) { new_addr = -1; } } else { no_mem_map: new_addr = -1; } } /* now do the real mapping */ if (new_addr != r->addr) { if (r->addr != -1) { if (r->type & PCI_ADDRESS_SPACE_IO) { int class; /* NOTE: specific hack for IDE in PC case: only one byte must be mapped. */ class = d->config[0x0a] | (d->config[0x0b] << 8); if (class == 0x0101 && r->size == 4) { isa_unassign_ioport(r->addr + 2, 1); } else { isa_unassign_ioport(r->addr, r->size); } } else { cpu_register_physical_memory(r->addr + pci_mem_base, r->size, IO_MEM_UNASSIGNED); } } r->addr = new_addr; if (r->addr != -1) { r->map_func(d, i, r->addr, r->size, r->type); } } } }}uint32_t pci_default_read_config(PCIDevice *d, uint32_t address, int len){ uint32_t val; switch(len) { case 1: val = d->config[address]; break; case 2: val = le16_to_cpu(*(uint16_t *)(d->config + address)); break; default: case 4: val = le32_to_cpu(*(uint32_t *)(d->config + address)); break; } return val;}void pci_default_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len){ int can_write, i; uint32_t end, addr; if (len == 4 && ((address >= 0x10 && address < 0x10 + 4 * 6) || (address >= 0x30 && address < 0x34))) { PCIIORegion *r; int reg; if ( address >= 0x30 ) { reg = PCI_ROM_SLOT; }else{ reg = (address - 0x10) >> 2; } r = &d->io_regions[reg]; if (r->size == 0) goto default_config; /* compute the stored value */ if (reg == PCI_ROM_SLOT) { /* keep ROM enable bit */ val &= (~(r->size - 1)) | 1; } else { val &= ~(r->size - 1); val |= r->type; } *(uint32_t *)(d->config + address) = cpu_to_le32(val); pci_update_mappings(d); return; } default_config: /* not efficient, but simple */ addr = address; for(i = 0; i < len; i++) { /* default read/write accesses */ switch(d->config[0x0e]) { case 0x00: case 0x80: switch(addr) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0e: case 0x10 ... 0x27: /* base */ case 0x30 ... 0x33: /* rom */ case 0x3d: can_write = 0; break; default: can_write = 1; break; } break; default: case 0x01: switch(addr) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0e: case 0x38 ... 0x3b: /* rom */ case 0x3d: can_write = 0; break; default: can_write = 1; break; } break; } if (can_write) { d->config[addr] = val; } addr++; val >>= 8; } end = address + len; if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) { /* if the command register is modified, we must modify the mappings */ pci_update_mappings(d); }}static void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len){ PCIBus *s = opaque; PCIDevice *pci_dev; int config_addr, bus_num; #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; } bus_num = (s->config_reg >> 16) & 0xff; if (bus_num != 0) return; pci_dev = s->devices[(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 pci_dev->config_write(pci_dev, config_addr, val, len);}static uint32_t pci_data_read(void *opaque, uint32_t addr, int len){ PCIBus *s = opaque; PCIDevice *pci_dev; int config_addr, bus_num; uint32_t val; if (!(s->config_reg & (1 << 31))) goto fail; bus_num = (s->config_reg >> 16) & 0xff; if (bus_num != 0) goto fail; pci_dev = s->devices[(s->config_reg >> 8) & 0xff]; if (!pci_dev) { fail: switch(len) { case 1: val = 0xff; break; case 2: val = 0xffff; break; default: case 4: val = 0xffffffff; break; } 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 void piix3_set_irq(PCIDevice *pci_dev, int irq_num, int level);PCIBus *i440fx_init(void){ PCIBus *s; PCIDevice *d; s = pci_register_bus(); s->set_irq = piix3_set_irq; register_ioport_write(0xcf8, 4, 4, pci_addr_writel, s); register_ioport_read(0xcf8, 4, 4, pci_addr_readl, s);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -