📄 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_PCIstruct PCIBus { int bus_num; int devfn_min; pci_set_irq_fn set_irq; pci_map_irq_fn map_irq; uint32_t config_reg; /* XXX: suppress */ /* low level pic */ SetIRQFunc *low_set_irq; void *irq_opaque; PCIDevice *devices[256]; PCIDevice *parent_dev; PCIBus *next; /* The bus IRQ state is the logical OR of the connected devices. Keep a count of the number of devices with raised IRQs. */ int irq_count[];};static void pci_update_mappings(PCIDevice *d);target_phys_addr_t pci_mem_base;static int pci_irq_index;static PCIBus *first_bus;PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *pic, int devfn_min, int nirq){ PCIBus *bus; bus = qemu_mallocz(sizeof(PCIBus) + (nirq * sizeof(int))); bus->set_irq = set_irq; bus->map_irq = map_irq; bus->irq_opaque = pic; bus->devfn_min = devfn_min; first_bus = bus; return bus;}PCIBus *pci_register_secondary_bus(PCIDevice *dev, pci_map_irq_fn map_irq){ PCIBus *bus; bus = qemu_mallocz(sizeof(PCIBus)); bus->map_irq = map_irq; bus->parent_dev = dev; bus->next = dev->bus->next; dev->bus->next = bus; return bus;}int pci_bus_num(PCIBus *s){ return s->bus_num;}void pci_device_save(PCIDevice *s, QEMUFile *f){ uint8_t irq_state = 0; int i; qemu_put_be32(f, 2); /* PCI device version */ qemu_put_buffer(f, s->config, 256); for (i = 0; i < 4; i++) irq_state |= !!s->irq_state[i] << i; qemu_put_buffer(f, &irq_state, 1);}int pci_device_load(PCIDevice *s, QEMUFile *f){ uint32_t version_id; version_id = qemu_get_be32(f); if (version_id != 1 && version_id != 2) return -EINVAL; qemu_get_buffer(f, s->config, 256); pci_update_mappings(s); if (version_id == 2) { uint8_t irq_state; int i; qemu_get_buffer(f, &irq_state, 1); for (i = 0; i < 4; i++) pci_set_irq(s, i, (irq_state >> i) & 1); } 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] && !( devfn >= PHP_DEVFN_START && devfn < PHP_DEVFN_END ) ) 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); memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state)); 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_hide_device(PCIDevice *pci_dev){ PCIBus *bus = pci_dev->bus; bus->devices[pci_dev->devfn] = NULL;}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);}target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr){ return addr + pci_mem_base;}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(pci_to_cpu_addr(r->addr), 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) { default: case 4: if (address <= 0xfc) { val = le32_to_cpu(*(uint32_t *)(d->config + address)); break; } /* fall through */ case 2: if (address <= 0xfe) { val = le16_to_cpu(*(uint16_t *)(d->config + address)); break; } /* fall through */ case 1: val = 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 0x2c ... 0x2f: /* subsystem vendor id, subsystem id */ case 0x30 ... 0x33: /* rom */ case 0x3d: can_write = 0; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -