diff options
Diffstat (limited to 'hw')
54 files changed, 2330 insertions, 296 deletions
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 2a28978cba..39c5d7103c 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -198,7 +198,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker); diff --git a/hw/block/nvme.c b/hw/block/nvme.c index aa1ed986d2..ce079aefdd 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -476,7 +476,8 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) switch (dw10) { case NVME_NUMBER_OF_QUEUES: - req->cqe.result = cpu_to_le32(n->num_queues); + req->cqe.result = + cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); break; default: return NVME_INVALID_FIELD | NVME_DNR; @@ -490,7 +491,8 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) switch (dw10) { case NVME_NUMBER_OF_QUEUES: - req->cqe.result = cpu_to_le32(n->num_queues); + req->cqe.result = + cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); break; default: return NVME_INVALID_FIELD | NVME_DNR; @@ -813,7 +815,7 @@ static int nvme_init(PCIDevice *pci_dev) NVME_CAP_SET_CSS(n->bar.cap, 1); NVME_CAP_SET_MPSMAX(n->bar.cap, 4); - n->bar.vs = 0x00010001; + n->bar.vs = 0x00010100; n->bar.intmc = n->bar.intms = 0; for (i = 0; i < n->num_namespaces; i++) { diff --git a/hw/core/loader.c b/hw/core/loader.c index f2b34da240..fcd4705d78 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -724,12 +724,22 @@ static void rom_insert(Rom *rom) QTAILQ_INSERT_TAIL(&roms, rom, next); } +static void fw_cfg_resized(const char *id, uint64_t length, void *host) +{ + if (fw_cfg) { + fw_cfg_modify_file(fw_cfg, id + strlen("/rom@"), host, length); + } +} + static void *rom_set_mr(Rom *rom, Object *owner, const char *name) { void *data; rom->mr = g_malloc(sizeof(*rom->mr)); - memory_region_init_ram(rom->mr, owner, name, rom->datasize, &error_abort); + memory_region_init_resizeable_ram(rom->mr, owner, name, + rom->datasize, rom->romsize, + fw_cfg_resized, + &error_abort); memory_region_set_readonly(rom->mr, true); vmstate_register_ram_global(rom->mr); @@ -824,7 +834,7 @@ err: } ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len, - hwaddr addr, const char *fw_file_name, + size_t max_len, hwaddr addr, const char *fw_file_name, FWCfgReadCallback fw_callback, void *callback_opaque) { Rom *rom; @@ -833,7 +843,7 @@ ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len, rom = g_malloc0(sizeof(*rom)); rom->name = g_strdup(name); rom->addr = addr; - rom->romsize = len; + rom->romsize = max_len ? max_len : len; rom->datasize = len; rom->data = g_malloc0(rom->datasize); memcpy(rom->data, blob, len); @@ -853,7 +863,7 @@ ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len, fw_cfg_add_file_callback(fw_cfg, fw_file_name, fw_callback, callback_opaque, - data, rom->romsize); + data, rom->datasize); } return ret; } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index a4d0c0c8bf..6a2e9c52bc 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -68,6 +68,9 @@ #define ACPI_BUILD_TABLE_SIZE 0x20000 +/* Reserve RAM space for tables: add another order of magnitude. */ +#define ACPI_BUILD_TABLE_MAX_SIZE 0x200000 + /* #define DEBUG_ACPI_BUILD */ #ifdef DEBUG_ACPI_BUILD #define ACPI_BUILD_DPRINTF(fmt, ...) \ @@ -1718,6 +1721,11 @@ static void acpi_build_update(void *build_opaque, uint32_t offset) acpi_build(build_state->guest_info, &tables); assert(acpi_data_len(tables.table_data) == build_state->table_size); + + /* Make sure RAM size is correct - in case it got changed by migration */ + qemu_ram_resize(build_state->table_ram, build_state->table_size, + &error_abort); + memcpy(qemu_get_ram_ptr(build_state->table_ram), tables.table_data->data, build_state->table_size); @@ -1734,10 +1742,10 @@ static void acpi_build_reset(void *build_opaque) } static ram_addr_t acpi_add_rom_blob(AcpiBuildState *build_state, GArray *blob, - const char *name) + const char *name, uint64_t max_size) { - return rom_add_blob(name, blob->data, acpi_data_len(blob), -1, name, - acpi_build_update, build_state); + return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1, + name, acpi_build_update, build_state); } static const VMStateDescription vmstate_acpi_build = { @@ -1781,11 +1789,12 @@ void acpi_setup(PcGuestInfo *guest_info) /* Now expose it all to Guest */ build_state->table_ram = acpi_add_rom_blob(build_state, tables.table_data, - ACPI_BUILD_TABLE_FILE); + ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_MAX_SIZE); assert(build_state->table_ram != RAM_ADDR_MAX); build_state->table_size = acpi_data_len(tables.table_data); - acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader"); + acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader", 0); fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index c63b7e556e..a71e6e014f 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -621,20 +621,107 @@ static void cmd_request_sense(IDEState *s, uint8_t *buf) static void cmd_inquiry(IDEState *s, uint8_t *buf) { + uint8_t page_code = buf[2]; int max_len = buf[4]; - buf[0] = 0x05; /* CD-ROM */ - buf[1] = 0x80; /* removable */ - buf[2] = 0x00; /* ISO */ - buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ - buf[4] = 31; /* additional length */ - buf[5] = 0; /* reserved */ - buf[6] = 0; /* reserved */ - buf[7] = 0; /* reserved */ - padstr8(buf + 8, 8, "QEMU"); - padstr8(buf + 16, 16, "QEMU DVD-ROM"); - padstr8(buf + 32, 4, s->version); - ide_atapi_cmd_reply(s, 36, max_len); + unsigned idx = 0; + unsigned size_idx; + unsigned preamble_len; + + /* If the EVPD (Enable Vital Product Data) bit is set in byte 1, + * we are being asked for a specific page of info indicated by byte 2. */ + if (buf[1] & 0x01) { + preamble_len = 4; + size_idx = 3; + + buf[idx++] = 0x05; /* CD-ROM */ + buf[idx++] = page_code; /* Page Code */ + buf[idx++] = 0x00; /* reserved */ + idx++; /* length (set later) */ + + switch (page_code) { + case 0x00: + /* Supported Pages: List of supported VPD responses. */ + buf[idx++] = 0x00; /* 0x00: Supported Pages, and: */ + buf[idx++] = 0x83; /* 0x83: Device Identification. */ + break; + + case 0x83: + /* Device Identification. Each entry is optional, but the entries + * included here are modeled after libata's VPD responses. + * If the response is given, at least one entry must be present. */ + + /* Entry 1: Serial */ + if (idx + 24 > max_len) { + /* Not enough room for even the first entry: */ + /* 4 byte header + 20 byte string */ + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, + ASC_DATA_PHASE_ERROR); + return; + } + buf[idx++] = 0x02; /* Ascii */ + buf[idx++] = 0x00; /* Vendor Specific */ + buf[idx++] = 0x00; + buf[idx++] = 20; /* Remaining length */ + padstr8(buf + idx, 20, s->drive_serial_str); + idx += 20; + + /* Entry 2: Drive Model and Serial */ + if (idx + 72 > max_len) { + /* 4 (header) + 8 (vendor) + 60 (model & serial) */ + goto out; + } + buf[idx++] = 0x02; /* Ascii */ + buf[idx++] = 0x01; /* T10 Vendor */ + buf[idx++] = 0x00; + buf[idx++] = 68; + padstr8(buf + idx, 8, "ATA"); /* Generic T10 vendor */ + idx += 8; + padstr8(buf + idx, 40, s->drive_model_str); + idx += 40; + padstr8(buf + idx, 20, s->drive_serial_str); + idx += 20; + + /* Entry 3: WWN */ + if (s->wwn && (idx + 12 <= max_len)) { + /* 4 byte header + 8 byte wwn */ + buf[idx++] = 0x01; /* Binary */ + buf[idx++] = 0x03; /* NAA */ + buf[idx++] = 0x00; + buf[idx++] = 0x08; + stq_be_p(&buf[idx], s->wwn); + idx += 8; + } + break; + + default: + /* SPC-3, revision 23 sec. 6.4 */ + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + return; + } + } else { + preamble_len = 5; + size_idx = 4; + + buf[0] = 0x05; /* CD-ROM */ + buf[1] = 0x80; /* removable */ + buf[2] = 0x00; /* ISO */ + buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ + /* buf[size_idx] set below. */ + buf[5] = 0; /* reserved */ + buf[6] = 0; /* reserved */ + buf[7] = 0; /* reserved */ + padstr8(buf + 8, 8, "QEMU"); + padstr8(buf + 16, 16, "QEMU DVD-ROM"); + padstr8(buf + 32, 4, s->version); + idx = 36; + } + + out: + buf[size_idx] = idx - preamble_len; + ide_atapi_cmd_reply(s, idx, max_len); + return; } static void cmd_get_configuration(IDEState *s, uint8_t *buf) diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 8a3eca40d2..c998003bf3 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -296,6 +296,7 @@ typedef struct IDEDMAOps IDEDMAOps; #define ASC_INCOMPATIBLE_FORMAT 0x30 #define ASC_MEDIUM_NOT_PRESENT 0x3a #define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 +#define ASC_DATA_PHASE_ERROR 0x4b #define ASC_MEDIA_REMOVAL_PREVENTED 0x53 #define CFA_NO_ERROR 0x00 diff --git a/hw/lm32/lm32_hwsetup.h b/hw/lm32/lm32_hwsetup.h index 9fd5e697a7..838754d5d8 100644 --- a/hw/lm32/lm32_hwsetup.h +++ b/hw/lm32/lm32_hwsetup.h @@ -73,7 +73,8 @@ static inline void hwsetup_free(HWSetup *hw) static inline void hwsetup_create_rom(HWSetup *hw, hwaddr base) { - rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, base, NULL, NULL, NULL); + rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, + TARGET_PAGE_SIZE, base, NULL, NULL, NULL); } static inline void hwsetup_add_u8(HWSetup *hw, uint8_t u) diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index d780ba0fcb..0407dee6da 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -218,13 +218,6 @@ static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf, return size; } -static void aw_emac_cleanup(NetClientState *nc) -{ - AwEmacState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static void aw_emac_reset(DeviceState *dev) { AwEmacState *s = AW_EMAC(dev); @@ -433,7 +426,6 @@ static NetClientInfo net_aw_emac_info = { .size = sizeof(NICState), .can_receive = aw_emac_can_receive, .receive = aw_emac_receive, - .cleanup = aw_emac_cleanup, .link_status_changed = aw_emac_set_link, }; diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index de26609c9d..55b629387c 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1209,14 +1209,6 @@ static const MemoryRegionOps gem_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void gem_cleanup(NetClientState *nc) -{ - GemState *s = qemu_get_nic_opaque(nc); - - DB_PRINT("\n"); - s->nic = NULL; -} - static void gem_set_link(NetClientState *nc) { DB_PRINT("\n"); @@ -1228,7 +1220,6 @@ static NetClientInfo net_gem_info = { .size = sizeof(NICState), .can_receive = gem_can_receive, .receive = gem_receive, - .cleanup = gem_cleanup, .link_status_changed = gem_set_link, }; diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 7eab7ad0cc..7ce13d2b46 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -859,22 +859,11 @@ static void nic_reset(void *opaque) dp8393x_update_irq(s); } -static void nic_cleanup(NetClientState *nc) -{ - dp8393xState *s = qemu_get_nic_opaque(nc); - - timer_del(s->watchdog); - timer_free(s->watchdog); - - g_free(s); -} - static NetClientInfo net_dp83932_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = nic_can_receive, .receive = nic_receive, - .cleanup = nic_cleanup, }; void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 89c5788b1c..a207e21bcf 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1503,14 +1503,6 @@ e1000_mmio_setup(E1000State *d) } static void -e1000_cleanup(NetClientState *nc) -{ - E1000State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - -static void pci_e1000_uninit(PCIDevice *dev) { E1000State *d = E1000(dev); @@ -1528,7 +1520,6 @@ static NetClientInfo net_e1000_info = { .can_receive = e1000_can_receive, .receive = e1000_receive, .receive_iov = e1000_receive_iov, - .cleanup = e1000_cleanup, .link_status_changed = e1000_set_link_status, }; diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 4877bfd4d3..7a4f9f8a08 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1832,13 +1832,6 @@ static const VMStateDescription vmstate_eepro100 = { } }; -static void nic_cleanup(NetClientState *nc) -{ - EEPRO100State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static void pci_nic_uninit(PCIDevice *pci_dev) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); @@ -1853,7 +1846,6 @@ static NetClientInfo net_eepro100_info = { .size = sizeof(NICState), .can_receive = nic_can_receive, .receive = nic_receive, - .cleanup = nic_cleanup, }; static int e100_nic_init(PCIDevice *pci_dev) diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 6a3c86db48..4773dea927 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -581,24 +581,11 @@ static const MemoryRegionOps eth_ops = { } }; -static void eth_cleanup(NetClientState *nc) -{ - ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); - - /* Disconnect the client. */ - eth->dma_out->client.push = NULL; - eth->dma_out->client.opaque = NULL; - eth->dma_in->client.opaque = NULL; - eth->dma_in->client.pull = NULL; - g_free(eth); -} - static NetClientInfo net_etraxfs_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = eth_can_receive, .receive = eth_receive, - .cleanup = eth_cleanup, .link_status_changed = eth_set_link, }; diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index d4b4429446..2fbbc6ccc0 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -338,11 +338,6 @@ static void etsec_reset(DeviceState *d) MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; } -static void etsec_cleanup(NetClientState *nc) -{ - /* qemu_log("eTSEC cleanup\n"); */ -} - static int etsec_can_receive(NetClientState *nc) { eTSEC *etsec = qemu_get_nic_opaque(nc); @@ -377,7 +372,6 @@ static NetClientInfo net_etsec_info = { .size = sizeof(NICState), .can_receive = etsec_can_receive, .receive = etsec_receive, - .cleanup = etsec_cleanup, .link_status_changed = etsec_set_link_status, }; diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index e528290b41..f169c383df 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1309,19 +1309,11 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void lan9118_cleanup(NetClientState *nc) -{ - lan9118_state *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_lan9118_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = lan9118_can_receive, .receive = lan9118_receive, - .cleanup = lan9118_cleanup, .link_status_changed = lan9118_set_link, }; diff --git a/hw/net/lance.c b/hw/net/lance.c index a1c49f1b97..ff7e789563 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -91,20 +91,12 @@ static const MemoryRegionOps lance_mem_ops = { }, }; -static void lance_cleanup(NetClientState *nc) -{ - PCNetState *d = qemu_get_nic_opaque(nc); - - pcnet_common_cleanup(d); -} - static NetClientInfo net_lance_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = pcnet_can_receive, .receive = pcnet_receive, .link_status_changed = pcnet_set_link_status, - .cleanup = lance_cleanup, }; static const VMStateDescription vmstate_lance = { diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 22cd7cf870..0255612f10 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -439,19 +439,11 @@ static const MemoryRegionOps mcf_fec_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void mcf_fec_cleanup(NetClientState *nc) -{ - mcf_fec_state *s = qemu_get_nic_opaque(nc); - - g_free(s); -} - static NetClientInfo net_mcf_fec_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = mcf_fec_can_receive, .receive = mcf_fec_receive, - .cleanup = mcf_fec_cleanup, }; void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c index c6326728eb..f06afaa581 100644 --- a/hw/net/milkymist-minimac2.c +++ b/hw/net/milkymist-minimac2.c @@ -425,13 +425,6 @@ static int minimac2_can_rx(NetClientState *nc) return 0; } -static void minimac2_cleanup(NetClientState *nc) -{ - MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static void milkymist_minimac2_reset(DeviceState *d) { MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(d); @@ -454,7 +447,6 @@ static NetClientInfo net_milkymist_minimac2_info = { .size = sizeof(NICState), .can_receive = minimac2_can_rx, .receive = minimac2_rx, - .cleanup = minimac2_cleanup, }; static int milkymist_minimac2_init(SysBusDevice *sbd) diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index b26c369178..c813e0caa8 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -211,19 +211,11 @@ static const VMStateDescription vmstate_mipsnet = { } }; -static void mipsnet_cleanup(NetClientState *nc) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_mipsnet_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = mipsnet_can_receive, .receive = mipsnet_receive, - .cleanup = mipsnet_cleanup, }; static const MemoryRegionOps mipsnet_ioport_ops = { diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 82e2ba17c1..17e7199f70 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -41,19 +41,11 @@ typedef struct ISANE2000State { NE2000State ne2000; } ISANE2000State; -static void isa_ne2000_cleanup(NetClientState *nc) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_ne2000_isa_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = ne2000_can_receive, .receive = ne2000_receive, - .cleanup = isa_ne2000_cleanup, }; static const VMStateDescription vmstate_isa_ne2000 = { diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 3ab2d03696..4dea70178d 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -702,19 +702,11 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) memory_region_init_io(&s->io, OBJECT(dev), &ne2000_ops, s, "ne2000", size); } -static void ne2000_cleanup(NetClientState *nc) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_ne2000_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = ne2000_can_receive, .receive = ne2000_receive, - .cleanup = ne2000_cleanup, }; static int pci_ne2000_init(PCIDevice *pci_dev) diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 4a443049dd..3642046efa 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -472,16 +472,11 @@ static ssize_t open_eth_receive(NetClientState *nc, return size; } -static void open_eth_cleanup(NetClientState *nc) -{ -} - static NetClientInfo net_open_eth_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = open_eth_can_receive, .receive = open_eth_receive, - .cleanup = open_eth_cleanup, .link_status_changed = open_eth_set_link_status, }; diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index fb5f5d6237..b86bc0d79b 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -271,13 +271,6 @@ static void pci_physical_memory_read(void *dma_opaque, hwaddr addr, pci_dma_read(dma_opaque, addr, buf, len); } -static void pci_pcnet_cleanup(NetClientState *nc) -{ - PCNetState *d = qemu_get_nic_opaque(nc); - - pcnet_common_cleanup(d); -} - static void pci_pcnet_uninit(PCIDevice *dev) { PCIPCNetState *d = PCI_PCNET(dev); @@ -294,7 +287,6 @@ static NetClientInfo net_pci_pcnet_info = { .can_receive = pcnet_can_receive, .receive = pcnet_receive, .link_status_changed = pcnet_set_link_status, - .cleanup = pci_pcnet_cleanup, }; static int pci_pcnet_init(PCIDevice *pci_dev) diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index f409b9293d..8a1c8f17b0 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -1724,11 +1724,6 @@ const VMStateDescription vmstate_pcnet = { } }; -void pcnet_common_cleanup(PCNetState *d) -{ - d->nic = NULL; -} - int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) { int i; diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index f8e8a6f6ba..3f12fe3c13 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -63,7 +63,6 @@ uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap); int pcnet_can_receive(NetClientState *nc); ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_); void pcnet_set_link_status(NetClientState *nc); -void pcnet_common_cleanup(PCNetState *d); int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info); extern const VMStateDescription vmstate_pcnet; #endif diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 5f0197c9df..6fa9e0aa15 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3455,13 +3455,6 @@ static void rtl8139_timer(void *opaque) rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); } -static void rtl8139_cleanup(NetClientState *nc) -{ - RTL8139State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static void pci_rtl8139_uninit(PCIDevice *dev) { RTL8139State *s = RTL8139(dev); @@ -3494,7 +3487,6 @@ static NetClientInfo net_rtl8139_info = { .size = sizeof(NICState), .can_receive = rtl8139_can_receive, .receive = rtl8139_receive, - .cleanup = rtl8139_cleanup, .link_status_changed = rtl8139_set_link_status, }; diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index d1dca8f4e2..74e06e6c77 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -736,19 +736,11 @@ static const MemoryRegionOps smc91c111_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void smc91c111_cleanup(NetClientState *nc) -{ - smc91c111_state *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_smc91c111_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = smc91c111_can_receive, .receive = smc91c111_receive, - .cleanup = smc91c111_cleanup, }; static int smc91c111_init1(SysBusDevice *sbd) diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 2c8b038227..c255d925a7 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -187,19 +187,11 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, return size; } -static void spapr_vlan_cleanup(NetClientState *nc) -{ - VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); - - dev->nic = NULL; -} - static NetClientInfo net_spapr_vlan_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = spapr_vlan_can_receive, .receive = spapr_vlan_receive, - .cleanup = spapr_vlan_cleanup, }; static void spapr_vlan_reset(VIOsPAPRDevice *sdev) diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index c07e5137c2..278a6545c3 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -451,19 +451,11 @@ static void stellaris_enet_reset(stellaris_enet_state *s) s->tx_fifo_len = 0; } -static void stellaris_enet_cleanup(NetClientState *nc) -{ - stellaris_enet_state *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_stellaris_enet_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = stellaris_enet_can_receive, .receive = stellaris_enet_receive, - .cleanup = stellaris_enet_cleanup, }; static int stellaris_enet_init(SysBusDevice *sbd) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index e574bd4322..45da34ad61 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1522,19 +1522,11 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, return 0; } -static void virtio_net_cleanup(NetClientState *nc) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - - n->nic = NULL; -} - static NetClientInfo net_virtio_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = virtio_net_can_receive, .receive = virtio_net_receive, - .cleanup = virtio_net_cleanup, .link_status_changed = virtio_net_set_link_status, .query_rx_filter = virtio_net_query_rxfilter, }; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 8eea58989b..a83d2a11ff 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1912,12 +1912,6 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) return bytes_indicated; } -static void vmxnet3_cleanup(NetClientState *nc) -{ - VMXNET3State *s = qemu_get_nic_opaque(nc); - s->nic = NULL; -} - static void vmxnet3_set_link_status(NetClientState *nc) { VMXNET3State *s = qemu_get_nic_opaque(nc); @@ -1937,7 +1931,6 @@ static NetClientInfo net_vmxnet3_info = { .size = sizeof(NICState), .can_receive = vmxnet3_can_receive, .receive = vmxnet3_receive, - .cleanup = vmxnet3_cleanup, .link_status_changed = vmxnet3_set_link_status, }; diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 63918ae1a0..19ecfc4ccf 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -370,11 +370,16 @@ static int net_connect(struct XenDevice *xendev) netdev->xendev.dom, netdev->tx_ring_ref, PROT_READ | PROT_WRITE); + if (!netdev->txs) { + return -1; + } netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, netdev->rx_ring_ref, PROT_READ | PROT_WRITE); - if (!netdev->txs || !netdev->rxs) { + if (!netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; return -1; } BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); @@ -405,10 +410,6 @@ static void net_disconnect(struct XenDevice *xendev) xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); netdev->rxs = NULL; } - if (netdev->nic) { - qemu_del_nic(netdev->nic); - netdev->nic = NULL; - } } static void net_event(struct XenDevice *xendev) @@ -422,7 +423,12 @@ static int net_free(struct XenDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + if (netdev->nic) { + qemu_del_nic(netdev->nic); + netdev->nic = NULL; + } g_free(netdev->mac); + netdev->mac = NULL; return 0; } diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index aeffcb58b7..b068f3a0d6 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -368,19 +368,11 @@ out: return ret; } -static void eth_cleanup(NetClientState *nc) -{ - XgmacState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_xgmac_enet_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = eth_can_rx, .receive = eth_rx, - .cleanup = eth_cleanup, }; static int xgmac_enet_init(SysBusDevice *sbd) diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index cd952d2514..21efedfc3e 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -857,14 +857,6 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) return size; } -static void eth_cleanup(NetClientState *nc) -{ - /* FIXME. */ - XilinxAXIEnet *s = qemu_get_nic_opaque(nc); - g_free(s->rxmem); - g_free(s); -} - static size_t xilinx_axienet_control_stream_push(StreamSlave *obj, uint8_t *buf, size_t len) { @@ -936,7 +928,6 @@ static NetClientInfo net_xilinx_enet_info = { .size = sizeof(NICState), .can_receive = eth_can_rx, .receive = eth_rx, - .cleanup = eth_cleanup, }; static void xilinx_enet_realize(DeviceState *dev, Error **errp) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 1b177b3dae..9536f64584 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -212,19 +212,11 @@ static void xilinx_ethlite_reset(DeviceState *dev) s->rxbuf = 0; } -static void eth_cleanup(NetClientState *nc) -{ - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_xilinx_ethlite_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = eth_can_rx, .receive = eth_rx, - .cleanup = eth_cleanup, }; static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 1b4c0f0023..574f8b2efb 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -62,11 +62,19 @@ #define PPCE500_PCI_NR_POBS 5 #define PPCE500_PCI_NR_PIBS 3 +#define PIWAR_EN 0x80000000 /* Enable */ +#define PIWAR_PF 0x20000000 /* prefetch */ +#define PIWAR_TGI_LOCAL 0x00f00000 /* target - local memory */ +#define PIWAR_READ_SNOOP 0x00050000 +#define PIWAR_WRITE_SNOOP 0x00005000 +#define PIWAR_SZ_MASK 0x0000003f + struct pci_outbound { uint32_t potar; uint32_t potear; uint32_t powbar; uint32_t powar; + MemoryRegion mem; }; struct pci_inbound { @@ -74,6 +82,7 @@ struct pci_inbound { uint32_t piwbar; uint32_t piwbear; uint32_t piwar; + MemoryRegion mem; }; #define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost" @@ -91,10 +100,13 @@ struct PPCE500PCIState { uint32_t irq_num[PCI_NUM_PINS]; uint32_t first_slot; uint32_t first_pin_irq; + AddressSpace bm_as; + MemoryRegion bm; /* mmio maps */ MemoryRegion container; MemoryRegion iomem; MemoryRegion pio; + MemoryRegion busmem; }; #define TYPE_PPC_E500_PCI_BRIDGE "e500-host-bridge" @@ -181,6 +193,71 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr, return value; } +/* DMA mapping */ +static void e500_update_piw(PPCE500PCIState *pci, int idx) +{ + uint64_t tar = ((uint64_t)pci->pib[idx].pitar) << 12; + uint64_t wbar = ((uint64_t)pci->pib[idx].piwbar) << 12; + uint64_t war = pci->pib[idx].piwar; + uint64_t size = 2ULL << (war & PIWAR_SZ_MASK); + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *mem = &pci->pib[idx].mem; + MemoryRegion *bm = &pci->bm; + char *name; + + if (memory_region_is_mapped(mem)) { + /* Before we modify anything, unmap and destroy the region */ + memory_region_del_subregion(bm, mem); + object_unparent(OBJECT(mem)); + } + + if (!(war & PIWAR_EN)) { + /* Not enabled, nothing to do */ + return; + } + + name = g_strdup_printf("PCI Inbound Window %d", idx); + memory_region_init_alias(mem, OBJECT(pci), name, address_space_mem, tar, + size); + memory_region_add_subregion_overlap(bm, wbar, mem, -1); + g_free(name); + + pci_debug("%s: Added window of size=%#lx from PCI=%#lx to CPU=%#lx\n", + __func__, size, wbar, tar); +} + +/* BAR mapping */ +static void e500_update_pow(PPCE500PCIState *pci, int idx) +{ + uint64_t tar = ((uint64_t)pci->pob[idx].potar) << 12; + uint64_t wbar = ((uint64_t)pci->pob[idx].powbar) << 12; + uint64_t war = pci->pob[idx].powar; + uint64_t size = 2ULL << (war & PIWAR_SZ_MASK); + MemoryRegion *mem = &pci->pob[idx].mem; + MemoryRegion *address_space_mem = get_system_memory(); + char *name; + + if (memory_region_is_mapped(mem)) { + /* Before we modify anything, unmap and destroy the region */ + memory_region_del_subregion(address_space_mem, mem); + object_unparent(OBJECT(mem)); + } + + if (!(war & PIWAR_EN)) { + /* Not enabled, nothing to do */ + return; + } + + name = g_strdup_printf("PCI Outbound Window %d", idx); + memory_region_init_alias(mem, OBJECT(pci), name, &pci->busmem, tar, + size); + memory_region_add_subregion(address_space_mem, wbar, mem); + g_free(name); + + pci_debug("%s: Added window of size=%#lx from CPU=%#lx to PCI=%#lx\n", + __func__, size, wbar, tar); +} + static void pci_reg_write4(void *opaque, hwaddr addr, uint64_t value, unsigned size) { @@ -199,18 +276,22 @@ static void pci_reg_write4(void *opaque, hwaddr addr, case PPCE500_PCI_OW3: case PPCE500_PCI_OW4: idx = (addr >> 5) & 0x7; - switch (addr & 0xC) { + switch (addr & 0x1F) { case PCI_POTAR: pci->pob[idx].potar = value; + e500_update_pow(pci, idx); break; case PCI_POTEAR: pci->pob[idx].potear = value; + e500_update_pow(pci, idx); break; case PCI_POWBAR: pci->pob[idx].powbar = value; + e500_update_pow(pci, idx); break; case PCI_POWAR: pci->pob[idx].powar = value; + e500_update_pow(pci, idx); break; default: break; @@ -221,18 +302,22 @@ static void pci_reg_write4(void *opaque, hwaddr addr, case PPCE500_PCI_IW2: case PPCE500_PCI_IW1: idx = ((addr >> 5) & 0x3) - 1; - switch (addr & 0xC) { + switch (addr & 0x1F) { case PCI_PITAR: pci->pib[idx].pitar = value; + e500_update_piw(pci, idx); break; case PCI_PIWBAR: pci->pib[idx].piwbar = value; + e500_update_piw(pci, idx); break; case PCI_PIWBEAR: pci->pib[idx].piwbear = value; + e500_update_piw(pci, idx); break; case PCI_PIWAR: pci->pib[idx].piwar = value; + e500_update_piw(pci, idx); break; default: break; @@ -349,13 +434,20 @@ static int e500_pcihost_bridge_initfn(PCIDevice *d) return 0; } +static AddressSpace *e500_pcihost_set_iommu(PCIBus *bus, void *opaque, + int devfn) +{ + PPCE500PCIState *s = opaque; + + return &s->bm_as; +} + static int e500_pcihost_initfn(SysBusDevice *dev) { PCIHostState *h; PPCE500PCIState *s; PCIBus *b; int i; - MemoryRegion *address_space_mem = get_system_memory(); h = PCI_HOST_BRIDGE(dev); s = PPC_E500_PCI_HOST_BRIDGE(dev); @@ -369,12 +461,22 @@ static int e500_pcihost_initfn(SysBusDevice *dev) } memory_region_init(&s->pio, OBJECT(s), "pci-pio", PCIE500_PCI_IOLEN); + memory_region_init(&s->busmem, OBJECT(s), "pci bus memory", UINT64_MAX); + + /* PIO lives at the bottom of our bus space */ + memory_region_add_subregion_overlap(&s->busmem, 0, &s->pio, -2); b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq, - mpc85xx_pci_map_irq, s, address_space_mem, - &s->pio, PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS); + mpc85xx_pci_map_irq, s, &s->busmem, &s->pio, + PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS); h->bus = b; + /* Set up PCI view of memory */ + memory_region_init(&s->bm, OBJECT(s), "bm-e500", UINT64_MAX); + memory_region_add_subregion(&s->bm, 0x0, &s->busmem); + address_space_init(&s->bm_as, &s->bm, "pci-bm"); + pci_setup_iommu(b, e500_pcihost_set_iommu, s); + pci_create_simple(b, 0, "e500-host-bridge"); memory_region_init(&s->container, OBJECT(h), "pci-container", PCIE500_ALL_SIZE); @@ -388,7 +490,6 @@ static int e500_pcihost_initfn(SysBusDevice *dev) memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem); memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem); sysbus_init_mmio(dev, &s->container); - sysbus_init_mmio(dev, &s->pio); pci_bus_set_route_irq_fn(b, e500_route_intx_pin_to_irq); return 0; diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 2832fc0da4..7e17d180c6 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -51,21 +51,16 @@ #define RAM_SIZES_ALIGN (64UL << 20) /* TODO: parameterize */ -#define MPC8544_CCSRBAR_BASE 0xE0000000ULL #define MPC8544_CCSRBAR_SIZE 0x00100000ULL #define MPC8544_MPIC_REGS_OFFSET 0x40000ULL #define MPC8544_MSI_REGS_OFFSET 0x41600ULL #define MPC8544_SERIAL0_REGS_OFFSET 0x4500ULL #define MPC8544_SERIAL1_REGS_OFFSET 0x4600ULL #define MPC8544_PCI_REGS_OFFSET 0x8000ULL -#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + \ - MPC8544_PCI_REGS_OFFSET) #define MPC8544_PCI_REGS_SIZE 0x1000ULL -#define MPC8544_PCI_IO 0xE1000000ULL #define MPC8544_UTIL_OFFSET 0xe0000ULL -#define MPC8544_SPIN_BASE 0xEF000000ULL #define MPC8XXX_GPIO_OFFSET 0x000FF000ULL -#define MPC8XXX_GPIO_IRQ 43 +#define MPC8XXX_GPIO_IRQ 47 struct boot_info { @@ -293,12 +288,12 @@ static int ppce500_load_device_tree(MachineState *machine, int len; uint32_t pci_ranges[14] = { - 0x2000000, 0x0, 0xc0000000, - 0x0, 0xc0000000, + 0x2000000, 0x0, params->pci_mmio_bus_base, + params->pci_mmio_base >> 32, params->pci_mmio_base, 0x0, 0x20000000, 0x1000000, 0x0, 0x0, - 0x0, 0xe1000000, + params->pci_pio_base >> 32, params->pci_pio_base, 0x0, 0x10000, }; QemuOpts *machine_opts = qemu_get_machine_opts(); @@ -389,7 +384,7 @@ static int ppce500_load_device_tree(MachineState *machine, CPUState *cpu; PowerPCCPU *pcpu; char cpu_name[128]; - uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); + uint64_t cpu_release_addr = params->spin_base + (i * 0x20); cpu = qemu_get_cpu(i); if (cpu == NULL) { @@ -426,7 +421,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ - snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); + snprintf(soc, sizeof(soc), "/soc@%"PRIx64, params->ccsrbar_base); qemu_fdt_add_subnode(fdt, soc); qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, @@ -434,7 +429,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1); qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1); qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0, - MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, + params->ccsrbar_base >> 32, params->ccsrbar_base, MPC8544_CCSRBAR_SIZE); /* XXX should contain a reasonable value */ qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); @@ -493,7 +488,8 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph); qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); - snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE); + snprintf(pci, sizeof(pci), "/pci@%llx", + params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); qemu_fdt_add_subnode(fdt, pci); qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); @@ -512,8 +508,10 @@ static int ppce500_load_device_tree(MachineState *machine, } qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph); qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); - qemu_fdt_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, - MPC8544_PCI_REGS_BASE, 0, 0x1000); + qemu_fdt_setprop_cells(fdt, pci, "reg", + (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET) >> 32, + (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET), + 0, 0x1000); qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666); qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1); qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2); @@ -841,7 +839,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; - env->mpic_iack = MPC8544_CCSRBAR_BASE + + env->mpic_iack = params->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); @@ -875,7 +873,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) qdev_init_nofail(dev); ccsr = CCSR(dev); ccsr_addr_space = &ccsr->ccsr_space; - memory_region_add_subregion(address_space_mem, MPC8544_CCSRBAR_BASE, + memory_region_add_subregion(address_space_mem, params->ccsrbar_base, ccsr_addr_space); mpic = ppce500_init_mpic(params, ccsr_addr_space, irqs); @@ -917,8 +915,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) if (!pci_bus) printf("couldn't create PCI controller!\n"); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, MPC8544_PCI_IO); - if (pci_bus) { /* Register network interfaces. */ for (i = 0; i < nb_nics; i++) { @@ -927,7 +923,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* Register spinning region */ - sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); + sysbus_create_simple("e500-spin", params->spin_base, NULL); if (cur_base < (32 * 1024 * 1024)) { /* u-boot occupies memory up to 32MB, so load blobs above */ diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 9f61ab2b1c..ef224ea5e6 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -17,6 +17,11 @@ typedef struct PPCE500Params { hwaddr platform_bus_size; int platform_bus_first_irq; int platform_bus_num_irqs; + hwaddr ccsrbar_base; + hwaddr pci_pio_base; + hwaddr pci_mmio_base; + hwaddr pci_mmio_bus_base; + hwaddr spin_base; } PPCE500Params; void ppce500_init(MachineState *machine, PPCE500Params *params); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index d50ae000ee..14b14eaa7d 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -41,6 +41,11 @@ static void e500plat_init(MachineState *machine) .platform_bus_size = (128ULL * 1024 * 1024), .platform_bus_first_irq = 5, .platform_bus_num_irqs = 10, + .ccsrbar_base = 0xFE0000000ULL, + .pci_pio_base = 0xFE1000000ULL, + .pci_mmio_base = 0xC00000000ULL, + .pci_mmio_bus_base = 0xE0000000ULL, + .spin_base = 0xFEF000000ULL, }; /* Older KVM versions don't support EPR which breaks guests when we announce diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index b99f74af75..3a3b141e43 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -15,6 +15,7 @@ #include "hw/boards.h" #include "sysemu/device_tree.h" #include "hw/ppc/openpic.h" +#include "qemu/error-report.h" static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) { @@ -33,8 +34,18 @@ static void mpc8544ds_init(MachineState *machine) .pci_nr_slots = 2, .fixup_devtree = mpc8544ds_fixup_devtree, .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, + .ccsrbar_base = 0xE0000000ULL, + .pci_mmio_base = 0xC0000000ULL, + .pci_mmio_bus_base = 0xC0000000ULL, + .pci_pio_base = 0xE1000000ULL, + .spin_base = 0xEF000000ULL, }; + if (machine->ram_size > 0xc0000000) { + error_report("The MPC8544DS board only supports up to 3GB of RAM"); + exit(1); + } + ppce500_init(machine, ¶ms); } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index bec82cd7a9..5ce565d5ec 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -844,7 +844,7 @@ static void timebase_pre_save(void *opaque) return; } - tb->time_of_the_day_ns = get_clock_realtime(); + tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); /* * tb_offset is only expected to be changed by migration so * there is no need to update it from KVM here @@ -873,7 +873,7 @@ static int timebase_post_load(void *opaque, int version_id) * We try to adjust timebase by downtime if host clocks are not * too much out of sync (1 second for now). */ - host_ns = get_clock_realtime(); + host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns); migration_duration_ns = MIN(NSEC_PER_SEC, ns_diff); migration_duration_tb = muldiv64(migration_duration_ns, freq, NSEC_PER_SEC); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 53c4116ed3..b560459e83 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -819,9 +819,16 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu) } } +#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) +#define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID) +#define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY) +#define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) +#define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) + static void spapr_reset_htab(sPAPREnvironment *spapr) { long shift; + int index; /* allocate hash page table. For now we always make this 16mb, * later we should probably make it scale to the size of guest @@ -833,6 +840,11 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* Kernel handles htab, we don't need to allocate one */ spapr->htab_shift = shift; kvmppc_kern_htab = true; + + /* Tell readers to update their file descriptor */ + if (spapr->htab_fd >= 0) { + spapr->htab_fd_stale = true; + } } else { if (!spapr->htab) { /* Allocate an htab if we don't yet have one */ @@ -841,6 +853,10 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* And clear it */ memset(spapr->htab, 0, HTAB_SIZE(spapr)); + + for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { + DIRTY_HPTE(HPTE(spapr->htab, index)); + } } /* Update the RMA size if necessary */ @@ -867,6 +883,28 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) return 0; } +/* + * A guest reset will cause spapr->htab_fd to become stale if being used. + * Reopen the file descriptor to make sure the whole HTAB is properly read. + */ +static int spapr_check_htab_fd(sPAPREnvironment *spapr) +{ + int rc = 0; + + if (spapr->htab_fd_stale) { + close(spapr->htab_fd); + spapr->htab_fd = kvmppc_get_htab_fd(false); + if (spapr->htab_fd < 0) { + error_report("Unable to open fd for reading hash table from KVM: " + "%s", strerror(errno)); + rc = -1; + } + spapr->htab_fd_stale = false; + } + + return rc; +} + static void ppc_spapr_reset(void) { PowerPCCPU *first_ppc_cpu; @@ -986,11 +1024,6 @@ static const VMStateDescription vmstate_spapr = { }, }; -#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) -#define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID) -#define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY) -#define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) - static int htab_save_setup(QEMUFile *f, void *opaque) { sPAPREnvironment *spapr = opaque; @@ -1005,6 +1038,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque) assert(kvm_enabled()); spapr->htab_fd = kvmppc_get_htab_fd(false); + spapr->htab_fd_stale = false; if (spapr->htab_fd < 0) { fprintf(stderr, "Unable to open fd for reading hash table from KVM: %s\n", strerror(errno)); @@ -1037,7 +1071,7 @@ static void htab_save_first_pass(QEMUFile *f, sPAPREnvironment *spapr, /* Consume valid HPTEs */ chunkstart = index; - while ((index < htabslots) + while ((index < htabslots) && (index - chunkstart < USHRT_MAX) && HPTE_VALID(HPTE(spapr->htab, index))) { index++; CLEAN_HPTE(HPTE(spapr->htab, index)); @@ -1089,7 +1123,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr, chunkstart = index; /* Consume valid dirty HPTEs */ - while ((index < htabslots) + while ((index < htabslots) && (index - chunkstart < USHRT_MAX) && HPTE_DIRTY(HPTE(spapr->htab, index)) && HPTE_VALID(HPTE(spapr->htab, index))) { CLEAN_HPTE(HPTE(spapr->htab, index)); @@ -1099,7 +1133,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr, invalidstart = index; /* Consume invalid dirty HPTEs */ - while ((index < htabslots) + while ((index < htabslots) && (index - invalidstart < USHRT_MAX) && HPTE_DIRTY(HPTE(spapr->htab, index)) && !HPTE_VALID(HPTE(spapr->htab, index))) { CLEAN_HPTE(HPTE(spapr->htab, index)); @@ -1157,6 +1191,11 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) if (!spapr->htab) { assert(kvm_enabled()); + rc = spapr_check_htab_fd(spapr); + if (rc < 0) { + return rc; + } + rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); if (rc < 0) { @@ -1188,6 +1227,11 @@ static int htab_save_complete(QEMUFile *f, void *opaque) assert(kvm_enabled()); + rc = spapr_check_htab_fd(spapr); + if (rc < 0) { + return rc; + } + rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, -1); if (rc < 0) { return rc; @@ -1438,7 +1482,7 @@ static void ppc_spapr_init(MachineState *machine) } if (spapr->rtas_size > RTAS_MAX_SIZE) { hw_error("RTAS too big ! 0x%zx bytes (max is 0x%x)\n", - spapr->rtas_size, RTAS_MAX_SIZE); + (size_t)spapr->rtas_size, RTAS_MAX_SIZE); exit(1); } g_free(filename); diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 6c91d8edd8..da474740c0 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -173,9 +173,9 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, return tcet; } -static void spapr_tce_table_finalize(Object *obj) +static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) { - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(obj); + sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); QLIST_REMOVE(tcet, list); @@ -420,6 +420,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->init = spapr_tce_table_realize; dc->reset = spapr_tce_reset; + dc->unrealize = spapr_tce_table_unrealize; QLIST_INIT(&spapr_tce_tables); @@ -435,7 +436,6 @@ static TypeInfo spapr_tce_table_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(sPAPRTCETable), .class_init = spapr_tce_table_class_init, - .instance_finalize = spapr_tce_table_finalize, }; static void register_types(void) diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 1ba6c3ab70..27cd75a932 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -8,3 +8,4 @@ obj-y += ipl.o obj-y += css.o obj-y += s390-virtio-ccw.o obj-y += virtio-ccw.o +obj-y += s390-pci-bus.o s390-pci-inst.o diff --git a/hw/s390x/css.c b/hw/s390x/css.c index b67c039a70..d0c5ddeece 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1299,6 +1299,11 @@ void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) /* TODO */ } +void css_generate_css_crws(uint8_t cssid) +{ + css_queue_crw(CRW_RSC_CSS, 0, 0, cssid); +} + int css_enable_mcsse(void) { trace_css_enable_facility("mcsse"); diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 33104ac58e..7e53148700 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -101,6 +101,7 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add); void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); +void css_generate_css_crws(uint8_t cssid); void css_adapter_interrupt(uint8_t isc); #define CSS_IO_ADAPTER_VIRTIO 1 diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c new file mode 100644 index 0000000000..1201b8d57c --- /dev/null +++ b/hw/s390x/s390-pci-bus.c @@ -0,0 +1,591 @@ +/* + * s390 PCI BUS + * + * Copyright 2014 IBM Corp. + * Author(s): Frank Blaschka <frank.blaschka@de.ibm.com> + * Hong Bo Li <lihbbj@cn.ibm.com> + * Yi Min Zhao <zyimin@cn.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "s390-pci-bus.h" +#include <hw/pci/pci_bus.h> +#include <hw/pci/msi.h> +#include <qemu/error-report.h> + +/* #define DEBUG_S390PCI_BUS */ +#ifdef DEBUG_S390PCI_BUS +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +int chsc_sei_nt2_get_event(void *res) +{ + ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res; + PciCcdfAvail *accdf; + PciCcdfErr *eccdf; + int rc = 1; + SeiContainer *sei_cont; + S390pciState *s = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + + if (!s) { + return rc; + } + + sei_cont = QTAILQ_FIRST(&s->pending_sei); + if (sei_cont) { + QTAILQ_REMOVE(&s->pending_sei, sei_cont, link); + nt2_res->nt = 2; + nt2_res->cc = sei_cont->cc; + switch (sei_cont->cc) { + case 1: /* error event */ + eccdf = (PciCcdfErr *)nt2_res->ccdf; + eccdf->fid = cpu_to_be32(sei_cont->fid); + eccdf->fh = cpu_to_be32(sei_cont->fh); + eccdf->e = cpu_to_be32(sei_cont->e); + eccdf->faddr = cpu_to_be64(sei_cont->faddr); + eccdf->pec = cpu_to_be16(sei_cont->pec); + break; + case 2: /* availability event */ + accdf = (PciCcdfAvail *)nt2_res->ccdf; + accdf->fid = cpu_to_be32(sei_cont->fid); + accdf->fh = cpu_to_be32(sei_cont->fh); + accdf->pec = cpu_to_be16(sei_cont->pec); + break; + default: + abort(); + } + g_free(sei_cont); + rc = 0; + } + + return rc; +} + +int chsc_sei_nt2_have_event(void) +{ + S390pciState *s = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + + if (!s) { + return 0; + } + + return !QTAILQ_EMPTY(&s->pending_sei); +} + +S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) +{ + S390PCIBusDevice *pbdev; + int i; + S390pciState *s = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + + if (!s) { + return NULL; + } + + for (i = 0; i < PCI_SLOT_MAX; i++) { + pbdev = &s->pbdev[i]; + if ((pbdev->fh != 0) && (pbdev->fid == fid)) { + return pbdev; + } + } + + return NULL; +} + +void s390_pci_sclp_configure(int configure, SCCB *sccb) +{ + PciCfgSccb *psccb = (PciCfgSccb *)sccb; + S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); + uint16_t rc; + + if (pbdev) { + if ((configure == 1 && pbdev->configured == true) || + (configure == 0 && pbdev->configured == false)) { + rc = SCLP_RC_NO_ACTION_REQUIRED; + } else { + pbdev->configured = !pbdev->configured; + rc = SCLP_RC_NORMAL_COMPLETION; + } + } else { + DPRINTF("sclp config %d no dev found\n", configure); + rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + } + + psccb->header.response_code = cpu_to_be16(rc); + return; +} + +static uint32_t s390_pci_get_pfid(PCIDevice *pdev) +{ + return PCI_SLOT(pdev->devfn); +} + +static uint32_t s390_pci_get_pfh(PCIDevice *pdev) +{ + return PCI_SLOT(pdev->devfn) | FH_VIRT; +} + +S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) +{ + S390PCIBusDevice *pbdev; + int i; + int j = 0; + S390pciState *s = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + + if (!s) { + return NULL; + } + + for (i = 0; i < PCI_SLOT_MAX; i++) { + pbdev = &s->pbdev[i]; + + if (pbdev->fh == 0) { + continue; + } + + if (j == idx) { + return pbdev; + } + j++; + } + + return NULL; +} + +S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) +{ + S390PCIBusDevice *pbdev; + int i; + S390pciState *s = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + + if (!s) { + return NULL; + } + + for (i = 0; i < PCI_SLOT_MAX; i++) { + pbdev = &s->pbdev[i]; + if (pbdev->fh == fh) { + return pbdev; + } + } + + return NULL; +} + +static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh, + uint32_t fid, uint64_t faddr, uint32_t e) +{ + SeiContainer *sei_cont = g_malloc0(sizeof(SeiContainer)); + S390pciState *s = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + + if (!s) { + return; + } + + sei_cont->fh = fh; + sei_cont->fid = fid; + sei_cont->cc = cc; + sei_cont->pec = pec; + sei_cont->faddr = faddr; + sei_cont->e = e; + + QTAILQ_INSERT_TAIL(&s->pending_sei, sei_cont, link); + css_generate_css_crws(0); +} + +static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh, + uint32_t fid) +{ + s390_pci_generate_event(2, pec, fh, fid, 0, 0); +} + +static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, + uint32_t fid, uint64_t faddr, + uint32_t e) +{ + s390_pci_generate_event(1, pec, fh, fid, faddr, e); +} + +static void s390_pci_set_irq(void *opaque, int irq, int level) +{ + /* nothing to do */ +} + +static int s390_pci_map_irq(PCIDevice *pci_dev, int irq_num) +{ + /* nothing to do */ + return 0; +} + +static uint64_t s390_pci_get_table_origin(uint64_t iota) +{ + return iota & ~ZPCI_IOTA_RTTO_FLAG; +} + +static unsigned int calc_rtx(dma_addr_t ptr) +{ + return ((unsigned long) ptr >> ZPCI_RT_SHIFT) & ZPCI_INDEX_MASK; +} + +static unsigned int calc_sx(dma_addr_t ptr) +{ + return ((unsigned long) ptr >> ZPCI_ST_SHIFT) & ZPCI_INDEX_MASK; +} + +static unsigned int calc_px(dma_addr_t ptr) +{ + return ((unsigned long) ptr >> PAGE_SHIFT) & ZPCI_PT_MASK; +} + +static uint64_t get_rt_sto(uint64_t entry) +{ + return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_RTX) + ? (entry & ZPCI_RTE_ADDR_MASK) + : 0; +} + +static uint64_t get_st_pto(uint64_t entry) +{ + return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_SX) + ? (entry & ZPCI_STE_ADDR_MASK) + : 0; +} + +static uint64_t s390_guest_io_table_walk(uint64_t guest_iota, + uint64_t guest_dma_address) +{ + uint64_t sto_a, pto_a, px_a; + uint64_t sto, pto, pte; + uint32_t rtx, sx, px; + + rtx = calc_rtx(guest_dma_address); + sx = calc_sx(guest_dma_address); + px = calc_px(guest_dma_address); + + sto_a = guest_iota + rtx * sizeof(uint64_t); + sto = ldq_phys(&address_space_memory, sto_a); + sto = get_rt_sto(sto); + if (!sto) { + pte = 0; + goto out; + } + + pto_a = sto + sx * sizeof(uint64_t); + pto = ldq_phys(&address_space_memory, pto_a); + pto = get_st_pto(pto); + if (!pto) { + pte = 0; + goto out; + } + + px_a = pto + px * sizeof(uint64_t); + pte = ldq_phys(&address_space_memory, px_a); + +out: + return pte; +} + +static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, + bool is_write) +{ + uint64_t pte; + uint32_t flags; + S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, mr); + S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev) + ->qbus.parent); + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = 0, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + + DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr); + + /* s390 does not have an APIC mapped to main storage so we use + * a separate AddressSpace only for msix notifications + */ + if (addr == ZPCI_MSI_ADDR) { + ret.target_as = &s->msix_notify_as; + ret.iova = addr; + ret.translated_addr = addr; + ret.addr_mask = 0xfff; + ret.perm = IOMMU_RW; + return ret; + } + + if (!pbdev->g_iota) { + pbdev->error_state = true; + pbdev->lgstg_blocked = true; + s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, + addr, 0); + return ret; + } + + if (addr < pbdev->pba || addr > pbdev->pal) { + pbdev->error_state = true; + pbdev->lgstg_blocked = true; + s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, + addr, 0); + return ret; + } + + pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota), + addr); + + if (!pte) { + pbdev->error_state = true; + pbdev->lgstg_blocked = true; + s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, + addr, ERR_EVENT_Q_BIT); + return ret; + } + + flags = pte & ZPCI_PTE_FLAG_MASK; + ret.iova = addr; + ret.translated_addr = pte & ZPCI_PTE_ADDR_MASK; + ret.addr_mask = 0xfff; + + if (flags & ZPCI_PTE_INVALID) { + ret.perm = IOMMU_NONE; + } else { + ret.perm = IOMMU_RW; + } + + return ret; +} + +static const MemoryRegionIOMMUOps s390_iommu_ops = { + .translate = s390_translate_iommu, +}; + +static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + S390pciState *s = opaque; + + return &s->pbdev[PCI_SLOT(devfn)].as; +} + +static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) +{ + uint8_t ind_old, ind_new; + hwaddr len = 1; + uint8_t *ind_addr; + + ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); + if (!ind_addr) { + s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0); + return -1; + } + do { + ind_old = *ind_addr; + ind_new = ind_old | to_be_set; + } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); + cpu_physical_memory_unmap(ind_addr, len, 1, len); + + return ind_old; +} + +static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + S390PCIBusDevice *pbdev; + uint32_t io_int_word; + uint32_t fid = data >> ZPCI_MSI_VEC_BITS; + uint32_t vec = data & ZPCI_MSI_VEC_MASK; + uint64_t ind_bit; + uint32_t sum_bit; + uint32_t e = 0; + + DPRINTF("write_msix data 0x%" PRIx64 " fid %d vec 0x%x\n", data, fid, vec); + + pbdev = s390_pci_find_dev_by_fid(fid); + if (!pbdev) { + e |= (vec << ERR_EVENT_MVN_OFFSET); + s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e); + return; + } + + ind_bit = pbdev->routes.adapter.ind_offset; + sum_bit = pbdev->routes.adapter.summary_offset; + + set_ind_atomic(pbdev->routes.adapter.ind_addr + (ind_bit + vec) / 8, + 0x80 >> ((ind_bit + vec) % 8)); + if (!set_ind_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8, + 0x80 >> (sum_bit % 8))) { + io_int_word = (pbdev->isc << 27) | IO_INT_WORD_AI; + s390_io_interrupt(0, 0, 0, io_int_word); + } + + return; +} + +static uint64_t s390_msi_ctrl_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0xffffffff; +} + +static const MemoryRegionOps s390_msi_ctrl_ops = { + .write = s390_msi_ctrl_write, + .read = s390_msi_ctrl_read, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void s390_pcihost_init_as(S390pciState *s) +{ + int i; + + for (i = 0; i < PCI_SLOT_MAX; i++) { + memory_region_init_iommu(&s->pbdev[i].mr, OBJECT(s), + &s390_iommu_ops, "iommu-s390", UINT64_MAX); + address_space_init(&s->pbdev[i].as, &s->pbdev[i].mr, "iommu-pci"); + } + + memory_region_init_io(&s->msix_notify_mr, OBJECT(s), + &s390_msi_ctrl_ops, s, "msix-s390", UINT64_MAX); + address_space_init(&s->msix_notify_as, &s->msix_notify_mr, "msix-pci"); +} + +static int s390_pcihost_init(SysBusDevice *dev) +{ + PCIBus *b; + BusState *bus; + PCIHostState *phb = PCI_HOST_BRIDGE(dev); + S390pciState *s = S390_PCI_HOST_BRIDGE(dev); + + DPRINTF("host_init\n"); + + b = pci_register_bus(DEVICE(dev), NULL, + s390_pci_set_irq, s390_pci_map_irq, NULL, + get_system_memory(), get_system_io(), 0, 64, + TYPE_PCI_BUS); + s390_pcihost_init_as(s); + pci_setup_iommu(b, s390_pci_dma_iommu, s); + + bus = BUS(b); + qbus_set_hotplug_handler(bus, DEVICE(dev), NULL); + phb->bus = b; + QTAILQ_INIT(&s->pending_sei); + return 0; +} + +static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev) +{ + uint8_t pos; + uint16_t ctrl; + uint32_t table, pba; + + pos = pci_find_capability(pbdev->pdev, PCI_CAP_ID_MSIX); + if (!pos) { + pbdev->msix.available = false; + return 0; + } + + ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_CAP_FLAGS, + pci_config_size(pbdev->pdev), sizeof(ctrl)); + table = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_TABLE, + pci_config_size(pbdev->pdev), sizeof(table)); + pba = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_PBA, + pci_config_size(pbdev->pdev), sizeof(pba)); + + pbdev->msix.table_bar = table & PCI_MSIX_FLAGS_BIRMASK; + pbdev->msix.table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + pbdev->msix.pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; + pbdev->msix.pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; + pbdev->msix.entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; + pbdev->msix.available = true; + return 0; +} + +static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + PCIDevice *pci_dev = PCI_DEVICE(dev); + S390PCIBusDevice *pbdev; + S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) + ->qbus.parent); + + pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + + pbdev->fid = s390_pci_get_pfid(pci_dev); + pbdev->pdev = pci_dev; + pbdev->configured = true; + pbdev->fh = s390_pci_get_pfh(pci_dev); + + s390_pcihost_setup_msix(pbdev); + + if (dev->hotplugged) { + s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, + pbdev->fh, pbdev->fid); + s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED, + pbdev->fh, pbdev->fid); + } + return; +} + +static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + PCIDevice *pci_dev = PCI_DEVICE(dev); + S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) + ->qbus.parent); + S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + + if (pbdev->configured) { + pbdev->configured = false; + s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, + pbdev->fh, pbdev->fid); + } + + s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, + pbdev->fh, pbdev->fid); + pbdev->fh = 0; + pbdev->fid = 0; + pbdev->pdev = NULL; + object_unparent(OBJECT(pci_dev)); +} + +static void s390_pcihost_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + + dc->cannot_instantiate_with_device_add_yet = true; + k->init = s390_pcihost_init; + hc->plug = s390_pcihost_hot_plug; + hc->unplug = s390_pcihost_hot_unplug; + msi_supported = true; +} + +static const TypeInfo s390_pcihost_info = { + .name = TYPE_S390_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(S390pciState), + .class_init = s390_pcihost_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } +}; + +static void s390_pci_register_types(void) +{ + type_register_static(&s390_pcihost_info); +} + +type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h new file mode 100644 index 0000000000..464a92eedf --- /dev/null +++ b/hw/s390x/s390-pci-bus.h @@ -0,0 +1,251 @@ +/* + * s390 PCI BUS definitions + * + * Copyright 2014 IBM Corp. + * Author(s): Frank Blaschka <frank.blaschka@de.ibm.com> + * Hong Bo Li <lihbbj@cn.ibm.com> + * Yi Min Zhao <zyimin@cn.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390_PCI_BUS_H +#define HW_S390_PCI_BUS_H + +#include <hw/pci/pci.h> +#include <hw/pci/pci_host.h> +#include "hw/s390x/sclp.h" +#include "hw/s390x/s390_flic.h" +#include "hw/s390x/css.h" + +#define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" +#define FH_VIRT 0x00ff0000 +#define ENABLE_BIT_OFFSET 31 +#define S390_PCIPT_ADAPTER 2 + +#define S390_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) + +#define HP_EVENT_TO_CONFIGURED 0x0301 +#define HP_EVENT_RESERVED_TO_STANDBY 0x0302 +#define HP_EVENT_CONFIGURED_TO_STBRES 0x0304 +#define HP_EVENT_STANDBY_TO_RESERVED 0x0308 + +#define ERR_EVENT_INVALAS 0x1 +#define ERR_EVENT_OORANGE 0x2 +#define ERR_EVENT_INVALTF 0x3 +#define ERR_EVENT_TPROTE 0x4 +#define ERR_EVENT_APROTE 0x5 +#define ERR_EVENT_KEYE 0x6 +#define ERR_EVENT_INVALTE 0x7 +#define ERR_EVENT_INVALTL 0x8 +#define ERR_EVENT_TT 0x9 +#define ERR_EVENT_INVALMS 0xa +#define ERR_EVENT_SERR 0xb +#define ERR_EVENT_NOMSI 0x10 +#define ERR_EVENT_INVALBV 0x11 +#define ERR_EVENT_AIBV 0x12 +#define ERR_EVENT_AIRERR 0x13 +#define ERR_EVENT_FMBA 0x2a +#define ERR_EVENT_FMBUP 0x2b +#define ERR_EVENT_FMBPRO 0x2c +#define ERR_EVENT_CCONF 0x30 +#define ERR_EVENT_SERVAC 0x3a +#define ERR_EVENT_PERMERR 0x3b + +#define ERR_EVENT_Q_BIT 0x2 +#define ERR_EVENT_MVN_OFFSET 16 + +#define ZPCI_MSI_VEC_BITS 11 +#define ZPCI_MSI_VEC_MASK 0x7ff + +#define ZPCI_MSI_ADDR 0xfe00000000000000ULL +#define ZPCI_SDMA_ADDR 0x100000000ULL +#define ZPCI_EDMA_ADDR 0x1ffffffffffffffULL + +#define PAGE_SHIFT 12 +#define PAGE_MASK (~(PAGE_SIZE-1)) +#define PAGE_DEFAULT_ACC 0 +#define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) + +/* I/O Translation Anchor (IOTA) */ +enum ZpciIoatDtype { + ZPCI_IOTA_STO = 0, + ZPCI_IOTA_RTTO = 1, + ZPCI_IOTA_RSTO = 2, + ZPCI_IOTA_RFTO = 3, + ZPCI_IOTA_PFAA = 4, + ZPCI_IOTA_IOPFAA = 5, + ZPCI_IOTA_IOPTO = 7 +}; + +#define ZPCI_IOTA_IOT_ENABLED 0x800ULL +#define ZPCI_IOTA_DT_ST (ZPCI_IOTA_STO << 2) +#define ZPCI_IOTA_DT_RT (ZPCI_IOTA_RTTO << 2) +#define ZPCI_IOTA_DT_RS (ZPCI_IOTA_RSTO << 2) +#define ZPCI_IOTA_DT_RF (ZPCI_IOTA_RFTO << 2) +#define ZPCI_IOTA_DT_PF (ZPCI_IOTA_PFAA << 2) +#define ZPCI_IOTA_FS_4K 0 +#define ZPCI_IOTA_FS_1M 1 +#define ZPCI_IOTA_FS_2G 2 +#define ZPCI_KEY (PAGE_DEFAULT_KEY << 5) + +#define ZPCI_IOTA_STO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_ST) +#define ZPCI_IOTA_RTTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RT) +#define ZPCI_IOTA_RSTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RS) +#define ZPCI_IOTA_RFTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RF) +#define ZPCI_IOTA_RFAA_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY |\ + ZPCI_IOTA_DT_PF | ZPCI_IOTA_FS_2G) + +/* I/O Region and segment tables */ +#define ZPCI_INDEX_MASK 0x7ffULL + +#define ZPCI_TABLE_TYPE_MASK 0xc +#define ZPCI_TABLE_TYPE_RFX 0xc +#define ZPCI_TABLE_TYPE_RSX 0x8 +#define ZPCI_TABLE_TYPE_RTX 0x4 +#define ZPCI_TABLE_TYPE_SX 0x0 + +#define ZPCI_TABLE_LEN_RFX 0x3 +#define ZPCI_TABLE_LEN_RSX 0x3 +#define ZPCI_TABLE_LEN_RTX 0x3 + +#define ZPCI_TABLE_OFFSET_MASK 0xc0 +#define ZPCI_TABLE_SIZE 0x4000 +#define ZPCI_TABLE_ALIGN ZPCI_TABLE_SIZE +#define ZPCI_TABLE_ENTRY_SIZE (sizeof(unsigned long)) +#define ZPCI_TABLE_ENTRIES (ZPCI_TABLE_SIZE / ZPCI_TABLE_ENTRY_SIZE) + +#define ZPCI_TABLE_BITS 11 +#define ZPCI_PT_BITS 8 +#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + PAGE_SHIFT) +#define ZPCI_RT_SHIFT (ZPCI_ST_SHIFT + ZPCI_TABLE_BITS) + +#define ZPCI_RTE_FLAG_MASK 0x3fffULL +#define ZPCI_RTE_ADDR_MASK (~ZPCI_RTE_FLAG_MASK) +#define ZPCI_STE_FLAG_MASK 0x7ffULL +#define ZPCI_STE_ADDR_MASK (~ZPCI_STE_FLAG_MASK) + +/* I/O Page tables */ +#define ZPCI_PTE_VALID_MASK 0x400 +#define ZPCI_PTE_INVALID 0x400 +#define ZPCI_PTE_VALID 0x000 +#define ZPCI_PT_SIZE 0x800 +#define ZPCI_PT_ALIGN ZPCI_PT_SIZE +#define ZPCI_PT_ENTRIES (ZPCI_PT_SIZE / ZPCI_TABLE_ENTRY_SIZE) +#define ZPCI_PT_MASK (ZPCI_PT_ENTRIES - 1) + +#define ZPCI_PTE_FLAG_MASK 0xfffULL +#define ZPCI_PTE_ADDR_MASK (~ZPCI_PTE_FLAG_MASK) + +/* Shared bits */ +#define ZPCI_TABLE_VALID 0x00 +#define ZPCI_TABLE_INVALID 0x20 +#define ZPCI_TABLE_PROTECTED 0x200 +#define ZPCI_TABLE_UNPROTECTED 0x000 + +#define ZPCI_TABLE_VALID_MASK 0x20 +#define ZPCI_TABLE_PROT_MASK 0x200 + +typedef struct SeiContainer { + QTAILQ_ENTRY(SeiContainer) link; + uint32_t fid; + uint32_t fh; + uint8_t cc; + uint16_t pec; + uint64_t faddr; + uint32_t e; +} SeiContainer; + +typedef struct PciCcdfErr { + uint32_t reserved1; + uint32_t fh; + uint32_t fid; + uint32_t e; + uint64_t faddr; + uint32_t reserved3; + uint16_t reserved4; + uint16_t pec; +} QEMU_PACKED PciCcdfErr; + +typedef struct PciCcdfAvail { + uint32_t reserved1; + uint32_t fh; + uint32_t fid; + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + uint16_t reserved6; + uint16_t pec; +} QEMU_PACKED PciCcdfAvail; + +typedef struct ChscSeiNt2Res { + uint16_t length; + uint16_t code; + uint16_t reserved1; + uint8_t reserved2; + uint8_t nt; + uint8_t flags; + uint8_t reserved3; + uint8_t reserved4; + uint8_t cc; + uint32_t reserved5[13]; + uint8_t ccdf[4016]; +} QEMU_PACKED ChscSeiNt2Res; + +typedef struct PciCfgSccb { + SCCBHeader header; + uint8_t atype; + uint8_t reserved1; + uint16_t reserved2; + uint32_t aid; +} QEMU_PACKED PciCfgSccb; + +typedef struct S390MsixInfo { + bool available; + uint8_t table_bar; + uint8_t pba_bar; + uint16_t entries; + uint32_t table_offset; + uint32_t pba_offset; +} S390MsixInfo; + +typedef struct S390PCIBusDevice { + PCIDevice *pdev; + bool configured; + bool error_state; + bool lgstg_blocked; + uint32_t fh; + uint32_t fid; + uint64_t g_iota; + uint64_t pba; + uint64_t pal; + uint64_t fmb_addr; + uint8_t isc; + uint16_t noi; + uint8_t sum; + S390MsixInfo msix; + AdapterRoutes routes; + AddressSpace as; + MemoryRegion mr; +} S390PCIBusDevice; + +typedef struct S390pciState { + PCIHostState parent_obj; + S390PCIBusDevice pbdev[PCI_SLOT_MAX]; + AddressSpace msix_notify_as; + MemoryRegion msix_notify_mr; + QTAILQ_HEAD(, SeiContainer) pending_sei; +} S390pciState; + +int chsc_sei_nt2_get_event(void *res); +int chsc_sei_nt2_have_event(void); +void s390_pci_sclp_configure(int configure, SCCB *sccb); +S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx); +S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh); +S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid); + +#endif diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c new file mode 100644 index 0000000000..5ea13e5d79 --- /dev/null +++ b/hw/s390x/s390-pci-inst.c @@ -0,0 +1,811 @@ +/* + * s390 PCI instructions + * + * Copyright 2014 IBM Corp. + * Author(s): Frank Blaschka <frank.blaschka@de.ibm.com> + * Hong Bo Li <lihbbj@cn.ibm.com> + * Yi Min Zhao <zyimin@cn.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "s390-pci-inst.h" +#include "s390-pci-bus.h" +#include <exec/memory-internal.h> +#include <qemu/error-report.h> + +/* #define DEBUG_S390PCI_INST */ +#ifdef DEBUG_S390PCI_INST +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +static void s390_set_status_code(CPUS390XState *env, + uint8_t r, uint64_t status_code) +{ + env->regs[r] &= ~0xff000000ULL; + env->regs[r] |= (status_code & 0xff) << 24; +} + +static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) +{ + S390PCIBusDevice *pbdev; + uint32_t res_code, initial_l2, g_l2, finish; + int rc, idx; + uint64_t resume_token; + + rc = 0; + if (lduw_p(&rrb->request.hdr.len) != 32) { + res_code = CLP_RC_LEN; + rc = -EINVAL; + goto out; + } + + if ((ldl_p(&rrb->request.fmt) & CLP_MASK_FMT) != 0) { + res_code = CLP_RC_FMT; + rc = -EINVAL; + goto out; + } + + if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || + ldq_p(&rrb->request.reserved1) != 0 || + ldq_p(&rrb->request.reserved2) != 0) { + res_code = CLP_RC_RESNOT0; + rc = -EINVAL; + goto out; + } + + resume_token = ldq_p(&rrb->request.resume_token); + + if (resume_token) { + pbdev = s390_pci_find_dev_by_idx(resume_token); + if (!pbdev) { + res_code = CLP_RC_LISTPCI_BADRT; + rc = -EINVAL; + goto out; + } + } + + if (lduw_p(&rrb->response.hdr.len) < 48) { + res_code = CLP_RC_8K; + rc = -EINVAL; + goto out; + } + + initial_l2 = lduw_p(&rrb->response.hdr.len); + if ((initial_l2 - LIST_PCI_HDR_LEN) % sizeof(ClpFhListEntry) + != 0) { + res_code = CLP_RC_LEN; + rc = -EINVAL; + *cc = 3; + goto out; + } + + stl_p(&rrb->response.fmt, 0); + stq_p(&rrb->response.reserved1, 0); + stq_p(&rrb->response.reserved2, 0); + stl_p(&rrb->response.mdd, FH_VIRT); + stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); + rrb->response.entry_size = sizeof(ClpFhListEntry); + finish = 0; + idx = resume_token; + g_l2 = LIST_PCI_HDR_LEN; + do { + pbdev = s390_pci_find_dev_by_idx(idx); + if (!pbdev) { + finish = 1; + break; + } + stw_p(&rrb->response.fh_list[idx - resume_token].device_id, + pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID)); + stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id, + pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID)); + stl_p(&rrb->response.fh_list[idx - resume_token].config, 0x80000000); + stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid); + stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh); + + g_l2 += sizeof(ClpFhListEntry); + /* Add endian check for DPRINTF? */ + DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n", + g_l2, + lduw_p(&rrb->response.fh_list[idx - resume_token].vendor_id), + lduw_p(&rrb->response.fh_list[idx - resume_token].device_id), + ldl_p(&rrb->response.fh_list[idx - resume_token].fid), + ldl_p(&rrb->response.fh_list[idx - resume_token].fh)); + idx++; + } while (g_l2 < initial_l2); + + if (finish == 1) { + resume_token = 0; + } else { + resume_token = idx; + } + stq_p(&rrb->response.resume_token, resume_token); + stw_p(&rrb->response.hdr.len, g_l2); + stw_p(&rrb->response.hdr.rsp, CLP_RC_OK); +out: + if (rc) { + DPRINTF("list pci failed rc 0x%x\n", rc); + stw_p(&rrb->response.hdr.rsp, res_code); + } + return rc; +} + +int clp_service_call(S390CPU *cpu, uint8_t r2) +{ + ClpReqHdr *reqh; + ClpRspHdr *resh; + S390PCIBusDevice *pbdev; + uint32_t req_len; + uint32_t res_len; + uint8_t buffer[4096 * 2]; + uint8_t cc = 0; + CPUS390XState *env = &cpu->env; + int i; + + cpu_synchronize_state(CPU(cpu)); + + if (env->psw.mask & PSW_MASK_PSTATE) { + program_interrupt(env, PGM_PRIVILEGED, 4); + return 0; + } + + cpu_physical_memory_read(env->regs[r2], buffer, sizeof(*reqh)); + reqh = (ClpReqHdr *)buffer; + req_len = lduw_p(&reqh->len); + if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + + cpu_physical_memory_read(env->regs[r2], buffer, req_len + sizeof(*resh)); + resh = (ClpRspHdr *)(buffer + req_len); + res_len = lduw_p(&resh->len); + if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + if ((req_len + res_len) > 8192) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + + cpu_physical_memory_read(env->regs[r2], buffer, req_len + res_len); + + if (req_len != 32) { + stw_p(&resh->rsp, CLP_RC_LEN); + goto out; + } + + switch (lduw_p(&reqh->cmd)) { + case CLP_LIST_PCI: { + ClpReqRspListPci *rrb = (ClpReqRspListPci *)buffer; + list_pci(rrb, &cc); + break; + } + case CLP_SET_PCI_FN: { + ClpReqSetPci *reqsetpci = (ClpReqSetPci *)reqh; + ClpRspSetPci *ressetpci = (ClpRspSetPci *)resh; + + pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqsetpci->fh)); + if (!pbdev) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH); + goto out; + } + + switch (reqsetpci->oc) { + case CLP_SET_ENABLE_PCI_FN: + pbdev->fh = pbdev->fh | 1 << ENABLE_BIT_OFFSET; + stl_p(&ressetpci->fh, pbdev->fh); + stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); + break; + case CLP_SET_DISABLE_PCI_FN: + pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET); + pbdev->error_state = false; + pbdev->lgstg_blocked = false; + stl_p(&ressetpci->fh, pbdev->fh); + stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); + break; + default: + DPRINTF("unknown set pci command\n"); + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + break; + } + break; + } + case CLP_QUERY_PCI_FN: { + ClpReqQueryPci *reqquery = (ClpReqQueryPci *)reqh; + ClpRspQueryPci *resquery = (ClpRspQueryPci *)resh; + + pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqquery->fh)); + if (!pbdev) { + DPRINTF("query pci no pci dev\n"); + stw_p(&resquery->hdr.rsp, CLP_RC_SETPCIFN_FH); + goto out; + } + + for (i = 0; i < PCI_BAR_COUNT; i++) { + uint32_t data = pci_get_long(pbdev->pdev->config + + PCI_BASE_ADDRESS_0 + (i * 4)); + + stl_p(&resquery->bar[i], data); + resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ? + ctz64(pbdev->pdev->io_regions[i].size) : 0; + DPRINTF("bar %d addr 0x%x size 0x%" PRIx64 "barsize 0x%x\n", i, + ldl_p(&resquery->bar[i]), + pbdev->pdev->io_regions[i].size, + resquery->bar_size[i]); + } + + stq_p(&resquery->sdma, ZPCI_SDMA_ADDR); + stq_p(&resquery->edma, ZPCI_EDMA_ADDR); + stw_p(&resquery->pchid, 0); + stw_p(&resquery->ug, 1); + stl_p(&resquery->uid, pbdev->fid); + stw_p(&resquery->hdr.rsp, CLP_RC_OK); + break; + } + case CLP_QUERY_PCI_FNGRP: { + ClpRspQueryPciGrp *resgrp = (ClpRspQueryPciGrp *)resh; + resgrp->fr = 1; + stq_p(&resgrp->dasm, 0); + stq_p(&resgrp->msia, ZPCI_MSI_ADDR); + stw_p(&resgrp->mui, 0); + stw_p(&resgrp->i, 128); + resgrp->version = 0; + + stw_p(&resgrp->hdr.rsp, CLP_RC_OK); + break; + } + default: + DPRINTF("unknown clp command\n"); + stw_p(&resh->rsp, CLP_RC_CMD); + break; + } + +out: + cpu_physical_memory_write(env->regs[r2], buffer, req_len + res_len); + setcc(cpu, cc); + return 0; +} + +int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) +{ + CPUS390XState *env = &cpu->env; + S390PCIBusDevice *pbdev; + uint64_t offset; + uint64_t data; + uint8_t len; + uint32_t fh; + uint8_t pcias; + + cpu_synchronize_state(CPU(cpu)); + + if (env->psw.mask & PSW_MASK_PSTATE) { + program_interrupt(env, PGM_PRIVILEGED, 4); + return 0; + } + + if (r2 & 0x1) { + program_interrupt(env, PGM_SPECIFICATION, 4); + return 0; + } + + fh = env->regs[r2] >> 32; + pcias = (env->regs[r2] >> 16) & 0xf; + len = env->regs[r2] & 0xf; + offset = env->regs[r2 + 1]; + + pbdev = s390_pci_find_dev_by_fh(fh); + if (!pbdev) { + DPRINTF("pcilg no pci dev\n"); + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + + if (pbdev->lgstg_blocked) { + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); + return 0; + } + + if (pcias < 6) { + if ((8 - (offset & 0x7)) < len) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + MemoryRegion *mr = pbdev->pdev->io_regions[pcias].memory; + io_mem_read(mr, offset, &data, len); + } else if (pcias == 15) { + if ((4 - (offset & 0x3)) < len) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + data = pci_host_config_read_common( + pbdev->pdev, offset, pci_config_size(pbdev->pdev), len); + + switch (len) { + case 1: + break; + case 2: + data = bswap16(data); + break; + case 4: + data = bswap32(data); + break; + case 8: + data = bswap64(data); + break; + default: + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + } else { + DPRINTF("invalid space\n"); + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS); + return 0; + } + + env->regs[r1] = data; + setcc(cpu, ZPCI_PCI_LS_OK); + return 0; +} + +static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t offset, + uint64_t *data, uint8_t len) +{ + uint32_t val; + uint8_t *msg_data; + + if (offset % PCI_MSIX_ENTRY_SIZE != 8) { + return; + } + + if (len != 4) { + DPRINTF("access msix table msg data but len is %d\n", len); + return; + } + + msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL; + val = pci_get_long(msg_data) | (pbdev->fid << ZPCI_MSI_VEC_BITS); + pci_set_long(msg_data, val); + DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data); +} + +static int trap_msix(S390PCIBusDevice *pbdev, uint64_t offset, uint8_t pcias) +{ + if (pbdev->msix.available && pbdev->msix.table_bar == pcias && + offset >= pbdev->msix.table_offset && + offset <= pbdev->msix.table_offset + + (pbdev->msix.entries - 1) * PCI_MSIX_ENTRY_SIZE) { + return 1; + } else { + return 0; + } +} + +int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) +{ + CPUS390XState *env = &cpu->env; + uint64_t offset, data; + S390PCIBusDevice *pbdev; + uint8_t len; + uint32_t fh; + uint8_t pcias; + + cpu_synchronize_state(CPU(cpu)); + + if (env->psw.mask & PSW_MASK_PSTATE) { + program_interrupt(env, PGM_PRIVILEGED, 4); + return 0; + } + + if (r2 & 0x1) { + program_interrupt(env, PGM_SPECIFICATION, 4); + return 0; + } + + fh = env->regs[r2] >> 32; + pcias = (env->regs[r2] >> 16) & 0xf; + len = env->regs[r2] & 0xf; + offset = env->regs[r2 + 1]; + + pbdev = s390_pci_find_dev_by_fh(fh); + if (!pbdev) { + DPRINTF("pcistg no pci dev\n"); + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + + if (pbdev->lgstg_blocked) { + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); + return 0; + } + + data = env->regs[r1]; + if (pcias < 6) { + if ((8 - (offset & 0x7)) < len) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + MemoryRegion *mr; + if (trap_msix(pbdev, offset, pcias)) { + offset = offset - pbdev->msix.table_offset; + mr = &pbdev->pdev->msix_table_mmio; + update_msix_table_msg_data(pbdev, offset, &data, len); + } else { + mr = pbdev->pdev->io_regions[pcias].memory; + } + + io_mem_write(mr, offset, data, len); + } else if (pcias == 15) { + if ((4 - (offset & 0x3)) < len) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + switch (len) { + case 1: + break; + case 2: + data = bswap16(data); + break; + case 4: + data = bswap32(data); + break; + case 8: + data = bswap64(data); + break; + default: + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } + + pci_host_config_write_common(pbdev->pdev, offset, + pci_config_size(pbdev->pdev), + data, len); + } else { + DPRINTF("pcistg invalid space\n"); + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS); + return 0; + } + + setcc(cpu, ZPCI_PCI_LS_OK); + return 0; +} + +int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) +{ + CPUS390XState *env = &cpu->env; + uint32_t fh; + S390PCIBusDevice *pbdev; + ram_addr_t size; + IOMMUTLBEntry entry; + MemoryRegion *mr; + + cpu_synchronize_state(CPU(cpu)); + + if (env->psw.mask & PSW_MASK_PSTATE) { + program_interrupt(env, PGM_PRIVILEGED, 4); + goto out; + } + + if (r2 & 0x1) { + program_interrupt(env, PGM_SPECIFICATION, 4); + goto out; + } + + fh = env->regs[r1] >> 32; + size = env->regs[r2 + 1]; + + pbdev = s390_pci_find_dev_by_fh(fh); + + if (!pbdev) { + DPRINTF("rpcit no pci dev\n"); + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + goto out; + } + + mr = pci_device_iommu_address_space(pbdev->pdev)->root; + entry = mr->iommu_ops->translate(mr, env->regs[r2], 0); + + if (!entry.translated_addr) { + setcc(cpu, ZPCI_PCI_LS_ERR); + goto out; + } + + entry.addr_mask = size - 1; + memory_region_notify_iommu(mr, entry); + setcc(cpu, ZPCI_PCI_LS_OK); +out: + return 0; +} + +int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr) +{ + CPUS390XState *env = &cpu->env; + S390PCIBusDevice *pbdev; + MemoryRegion *mr; + int i; + uint64_t val; + uint32_t fh; + uint8_t pcias; + uint8_t len; + + if (env->psw.mask & PSW_MASK_PSTATE) { + program_interrupt(env, PGM_PRIVILEGED, 6); + return 0; + } + + fh = env->regs[r1] >> 32; + pcias = (env->regs[r1] >> 16) & 0xf; + len = env->regs[r1] & 0xff; + + if (pcias > 5) { + DPRINTF("pcistb invalid space\n"); + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INVAL_AS); + return 0; + } + + switch (len) { + case 16: + case 32: + case 64: + case 128: + break; + default: + program_interrupt(env, PGM_SPECIFICATION, 6); + return 0; + } + + pbdev = s390_pci_find_dev_by_fh(fh); + if (!pbdev) { + DPRINTF("pcistb no pci dev fh 0x%x\n", fh); + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + + if (pbdev->lgstg_blocked) { + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED); + return 0; + } + + mr = pbdev->pdev->io_regions[pcias].memory; + if (!memory_region_access_valid(mr, env->regs[r3], len, true)) { + program_interrupt(env, PGM_ADDRESSING, 6); + return 0; + } + + for (i = 0; i < len / 8; i++) { + val = ldq_phys(&address_space_memory, gaddr + i * 8); + io_mem_write(mr, env->regs[r3] + i * 8, val, 8); + } + + setcc(cpu, ZPCI_PCI_LS_OK); + return 0; +} + +static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) +{ + int ret; + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + ret = css_register_io_adapter(S390_PCIPT_ADAPTER, + FIB_DATA_ISC(ldl_p(&fib.data)), true, false, + &pbdev->routes.adapter.adapter_id); + assert(ret == 0); + + fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, + ldq_p(&fib.aisb), true); + fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, + ldq_p(&fib.aibv), true); + + pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb); + pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data)); + pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv); + pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data)); + pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data)); + pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data)); + pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data)); + + DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); + return 0; +} + +static int dereg_irqs(S390PCIBusDevice *pbdev) +{ + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, + pbdev->routes.adapter.ind_addr, false); + + pbdev->routes.adapter.summary_addr = 0; + pbdev->routes.adapter.summary_offset = 0; + pbdev->routes.adapter.ind_addr = 0; + pbdev->routes.adapter.ind_offset = 0; + pbdev->isc = 0; + pbdev->noi = 0; + pbdev->sum = 0; + + DPRINTF("dereg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); + return 0; +} + +static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) +{ + uint64_t pba = ldq_p(&fib.pba); + uint64_t pal = ldq_p(&fib.pal); + uint64_t g_iota = ldq_p(&fib.iota); + uint8_t dt = (g_iota >> 2) & 0x7; + uint8_t t = (g_iota >> 11) & 0x1; + + if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) { + program_interrupt(env, PGM_OPERAND, 6); + return -EINVAL; + } + + /* currently we only support designation type 1 with translation */ + if (!(dt == ZPCI_IOTA_RTTO && t)) { + error_report("unsupported ioat dt %d t %d", dt, t); + program_interrupt(env, PGM_OPERAND, 6); + return -EINVAL; + } + + pbdev->pba = pba; + pbdev->pal = pal; + pbdev->g_iota = g_iota; + return 0; +} + +static void dereg_ioat(S390PCIBusDevice *pbdev) +{ + pbdev->pba = 0; + pbdev->pal = 0; + pbdev->g_iota = 0; +} + +int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba) +{ + CPUS390XState *env = &cpu->env; + uint8_t oc; + uint32_t fh; + ZpciFib fib; + S390PCIBusDevice *pbdev; + uint64_t cc = ZPCI_PCI_LS_OK; + + if (env->psw.mask & PSW_MASK_PSTATE) { + program_interrupt(env, PGM_PRIVILEGED, 6); + return 0; + } + + oc = env->regs[r1] & 0xff; + fh = env->regs[r1] >> 32; + + if (fiba & 0x7) { + program_interrupt(env, PGM_SPECIFICATION, 6); + return 0; + } + + pbdev = s390_pci_find_dev_by_fh(fh); + if (!pbdev) { + DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + + cpu_physical_memory_read(fiba, (uint8_t *)&fib, sizeof(fib)); + + switch (oc) { + case ZPCI_MOD_FC_REG_INT: + if (reg_irqs(env, pbdev, fib)) { + cc = ZPCI_PCI_LS_ERR; + } + break; + case ZPCI_MOD_FC_DEREG_INT: + dereg_irqs(pbdev); + break; + case ZPCI_MOD_FC_REG_IOAT: + if (reg_ioat(env, pbdev, fib)) { + cc = ZPCI_PCI_LS_ERR; + } + break; + case ZPCI_MOD_FC_DEREG_IOAT: + dereg_ioat(pbdev); + break; + case ZPCI_MOD_FC_REREG_IOAT: + dereg_ioat(pbdev); + if (reg_ioat(env, pbdev, fib)) { + cc = ZPCI_PCI_LS_ERR; + } + break; + case ZPCI_MOD_FC_RESET_ERROR: + pbdev->error_state = false; + pbdev->lgstg_blocked = false; + break; + case ZPCI_MOD_FC_RESET_BLOCK: + pbdev->lgstg_blocked = false; + break; + case ZPCI_MOD_FC_SET_MEASURE: + pbdev->fmb_addr = ldq_p(&fib.fmb_addr); + break; + default: + program_interrupt(&cpu->env, PGM_OPERAND, 6); + cc = ZPCI_PCI_LS_ERR; + } + + setcc(cpu, cc); + return 0; +} + +int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba) +{ + CPUS390XState *env = &cpu->env; + uint32_t fh; + ZpciFib fib; + S390PCIBusDevice *pbdev; + uint32_t data; + uint64_t cc = ZPCI_PCI_LS_OK; + + if (env->psw.mask & PSW_MASK_PSTATE) { + program_interrupt(env, PGM_PRIVILEGED, 6); + return 0; + } + + fh = env->regs[r1] >> 32; + + if (fiba & 0x7) { + program_interrupt(env, PGM_SPECIFICATION, 6); + return 0; + } + + pbdev = s390_pci_find_dev_by_fh(fh); + if (!pbdev) { + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + + memset(&fib, 0, sizeof(fib)); + stq_p(&fib.pba, pbdev->pba); + stq_p(&fib.pal, pbdev->pal); + stq_p(&fib.iota, pbdev->g_iota); + stq_p(&fib.aibv, pbdev->routes.adapter.ind_addr); + stq_p(&fib.aisb, pbdev->routes.adapter.summary_addr); + stq_p(&fib.fmb_addr, pbdev->fmb_addr); + + data = (pbdev->isc << 28) | (pbdev->noi << 16) | + (pbdev->routes.adapter.ind_offset << 8) | (pbdev->sum << 7) | + pbdev->routes.adapter.summary_offset; + stw_p(&fib.data, data); + + if (pbdev->fh >> ENABLE_BIT_OFFSET) { + fib.fc |= 0x80; + } + + if (pbdev->error_state) { + fib.fc |= 0x40; + } + + if (pbdev->lgstg_blocked) { + fib.fc |= 0x20; + } + + if (pbdev->g_iota) { + fib.fc |= 0x10; + } + + cpu_physical_memory_write(fiba, (uint8_t *)&fib, sizeof(fib)); + setcc(cpu, cc); + return 0; +} diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h new file mode 100644 index 0000000000..7e6c804737 --- /dev/null +++ b/hw/s390x/s390-pci-inst.h @@ -0,0 +1,288 @@ +/* + * s390 PCI instruction definitions + * + * Copyright 2014 IBM Corp. + * Author(s): Frank Blaschka <frank.blaschka@de.ibm.com> + * Hong Bo Li <lihbbj@cn.ibm.com> + * Yi Min Zhao <zyimin@cn.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390_PCI_INST_H +#define HW_S390_PCI_INST_H + +#include <sysemu/dma.h> + +/* CLP common request & response block size */ +#define CLP_BLK_SIZE 4096 +#define PCI_BAR_COUNT 6 +#define PCI_MAX_FUNCTIONS 4096 + +typedef struct ClpReqHdr { + uint16_t len; + uint16_t cmd; +} QEMU_PACKED ClpReqHdr; + +typedef struct ClpRspHdr { + uint16_t len; + uint16_t rsp; +} QEMU_PACKED ClpRspHdr; + +/* CLP Response Codes */ +#define CLP_RC_OK 0x0010 /* Command request successfully */ +#define CLP_RC_CMD 0x0020 /* Command code not recognized */ +#define CLP_RC_PERM 0x0030 /* Command not authorized */ +#define CLP_RC_FMT 0x0040 /* Invalid command request format */ +#define CLP_RC_LEN 0x0050 /* Invalid command request length */ +#define CLP_RC_8K 0x0060 /* Command requires 8K LPCB */ +#define CLP_RC_RESNOT0 0x0070 /* Reserved field not zero */ +#define CLP_RC_NODATA 0x0080 /* No data available */ +#define CLP_RC_FC_UNKNOWN 0x0100 /* Function code not recognized */ + +/* + * Call Logical Processor - Command Codes + */ +#define CLP_LIST_PCI 0x0002 +#define CLP_QUERY_PCI_FN 0x0003 +#define CLP_QUERY_PCI_FNGRP 0x0004 +#define CLP_SET_PCI_FN 0x0005 + +/* PCI function handle list entry */ +typedef struct ClpFhListEntry { + uint16_t device_id; + uint16_t vendor_id; +#define CLP_FHLIST_MASK_CONFIG 0x80000000 + uint32_t config; + uint32_t fid; + uint32_t fh; +} QEMU_PACKED ClpFhListEntry; + +#define CLP_RC_SETPCIFN_FH 0x0101 /* Invalid PCI fn handle */ +#define CLP_RC_SETPCIFN_FHOP 0x0102 /* Fn handle not valid for op */ +#define CLP_RC_SETPCIFN_DMAAS 0x0103 /* Invalid DMA addr space */ +#define CLP_RC_SETPCIFN_RES 0x0104 /* Insufficient resources */ +#define CLP_RC_SETPCIFN_ALRDY 0x0105 /* Fn already in requested state */ +#define CLP_RC_SETPCIFN_ERR 0x0106 /* Fn in permanent error state */ +#define CLP_RC_SETPCIFN_RECPND 0x0107 /* Error recovery pending */ +#define CLP_RC_SETPCIFN_BUSY 0x0108 /* Fn busy */ +#define CLP_RC_LISTPCI_BADRT 0x010a /* Resume token not recognized */ +#define CLP_RC_QUERYPCIFG_PFGID 0x010b /* Unrecognized PFGID */ + +/* request or response block header length */ +#define LIST_PCI_HDR_LEN 32 + +/* Number of function handles fitting in response block */ +#define CLP_FH_LIST_NR_ENTRIES \ + ((CLP_BLK_SIZE - 2 * LIST_PCI_HDR_LEN) \ + / sizeof(ClpFhListEntry)) + +#define CLP_SET_ENABLE_PCI_FN 0 /* Yes, 0 enables it */ +#define CLP_SET_DISABLE_PCI_FN 1 /* Yes, 1 disables it */ + +#define CLP_UTIL_STR_LEN 64 + +#define CLP_MASK_FMT 0xf0000000 + +/* List PCI functions request */ +typedef struct ClpReqListPci { + ClpReqHdr hdr; + uint32_t fmt; + uint64_t reserved1; + uint64_t resume_token; + uint64_t reserved2; +} QEMU_PACKED ClpReqListPci; + +/* List PCI functions response */ +typedef struct ClpRspListPci { + ClpRspHdr hdr; + uint32_t fmt; + uint64_t reserved1; + uint64_t resume_token; + uint32_t mdd; + uint16_t max_fn; + uint8_t reserved2; + uint8_t entry_size; + ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES]; +} QEMU_PACKED ClpRspListPci; + +/* Query PCI function request */ +typedef struct ClpReqQueryPci { + ClpReqHdr hdr; + uint32_t fmt; + uint64_t reserved1; + uint32_t fh; /* function handle */ + uint32_t reserved2; + uint64_t reserved3; +} QEMU_PACKED ClpReqQueryPci; + +/* Query PCI function response */ +typedef struct ClpRspQueryPci { + ClpRspHdr hdr; + uint32_t fmt; + uint64_t reserved1; + uint16_t vfn; /* virtual fn number */ +#define CLP_RSP_QPCI_MASK_UTIL 0x100 +#define CLP_RSP_QPCI_MASK_PFGID 0xff + uint16_t ug; + uint32_t fid; /* pci function id */ + uint8_t bar_size[PCI_BAR_COUNT]; + uint16_t pchid; + uint32_t bar[PCI_BAR_COUNT]; + uint64_t reserved2; + uint64_t sdma; /* start dma as */ + uint64_t edma; /* end dma as */ + uint32_t reserved3[11]; + uint32_t uid; + uint8_t util_str[CLP_UTIL_STR_LEN]; /* utility string */ +} QEMU_PACKED ClpRspQueryPci; + +/* Query PCI function group request */ +typedef struct ClpReqQueryPciGrp { + ClpReqHdr hdr; + uint32_t fmt; + uint64_t reserved1; +#define CLP_REQ_QPCIG_MASK_PFGID 0xff + uint32_t g; + uint32_t reserved2; + uint64_t reserved3; +} QEMU_PACKED ClpReqQueryPciGrp; + +/* Query PCI function group response */ +typedef struct ClpRspQueryPciGrp { + ClpRspHdr hdr; + uint32_t fmt; + uint64_t reserved1; +#define CLP_RSP_QPCIG_MASK_NOI 0xfff + uint16_t i; + uint8_t version; +#define CLP_RSP_QPCIG_MASK_FRAME 0x2 +#define CLP_RSP_QPCIG_MASK_REFRESH 0x1 + uint8_t fr; + uint16_t reserved2; + uint16_t mui; + uint64_t reserved3; + uint64_t dasm; /* dma address space mask */ + uint64_t msia; /* MSI address */ + uint64_t reserved4; + uint64_t reserved5; +} QEMU_PACKED ClpRspQueryPciGrp; + +/* Set PCI function request */ +typedef struct ClpReqSetPci { + ClpReqHdr hdr; + uint32_t fmt; + uint64_t reserved1; + uint32_t fh; /* function handle */ + uint16_t reserved2; + uint8_t oc; /* operation controls */ + uint8_t ndas; /* number of dma spaces */ + uint64_t reserved3; +} QEMU_PACKED ClpReqSetPci; + +/* Set PCI function response */ +typedef struct ClpRspSetPci { + ClpRspHdr hdr; + uint32_t fmt; + uint64_t reserved1; + uint32_t fh; /* function handle */ + uint32_t reserved3; + uint64_t reserved4; +} QEMU_PACKED ClpRspSetPci; + +typedef struct ClpReqRspListPci { + ClpReqListPci request; + ClpRspListPci response; +} QEMU_PACKED ClpReqRspListPci; + +typedef struct ClpReqRspSetPci { + ClpReqSetPci request; + ClpRspSetPci response; +} QEMU_PACKED ClpReqRspSetPci; + +typedef struct ClpReqRspQueryPci { + ClpReqQueryPci request; + ClpRspQueryPci response; +} QEMU_PACKED ClpReqRspQueryPci; + +typedef struct ClpReqRspQueryPciGrp { + ClpReqQueryPciGrp request; + ClpRspQueryPciGrp response; +} QEMU_PACKED ClpReqRspQueryPciGrp; + +/* Load/Store status codes */ +#define ZPCI_PCI_ST_FUNC_NOT_ENABLED 4 +#define ZPCI_PCI_ST_FUNC_IN_ERR 8 +#define ZPCI_PCI_ST_BLOCKED 12 +#define ZPCI_PCI_ST_INSUF_RES 16 +#define ZPCI_PCI_ST_INVAL_AS 20 +#define ZPCI_PCI_ST_FUNC_ALREADY_ENABLED 24 +#define ZPCI_PCI_ST_DMA_AS_NOT_ENABLED 28 +#define ZPCI_PCI_ST_2ND_OP_IN_INV_AS 36 +#define ZPCI_PCI_ST_FUNC_NOT_AVAIL 40 +#define ZPCI_PCI_ST_ALREADY_IN_RQ_STATE 44 + +/* Load/Store return codes */ +#define ZPCI_PCI_LS_OK 0 +#define ZPCI_PCI_LS_ERR 1 +#define ZPCI_PCI_LS_BUSY 2 +#define ZPCI_PCI_LS_INVAL_HANDLE 3 + +/* Modify PCI Function Controls */ +#define ZPCI_MOD_FC_REG_INT 2 +#define ZPCI_MOD_FC_DEREG_INT 3 +#define ZPCI_MOD_FC_REG_IOAT 4 +#define ZPCI_MOD_FC_DEREG_IOAT 5 +#define ZPCI_MOD_FC_REREG_IOAT 6 +#define ZPCI_MOD_FC_RESET_ERROR 7 +#define ZPCI_MOD_FC_RESET_BLOCK 9 +#define ZPCI_MOD_FC_SET_MEASURE 10 + +/* FIB function controls */ +#define ZPCI_FIB_FC_ENABLED 0x80 +#define ZPCI_FIB_FC_ERROR 0x40 +#define ZPCI_FIB_FC_LS_BLOCKED 0x20 +#define ZPCI_FIB_FC_DMAAS_REG 0x10 + +/* FIB function controls */ +#define ZPCI_FIB_FC_ENABLED 0x80 +#define ZPCI_FIB_FC_ERROR 0x40 +#define ZPCI_FIB_FC_LS_BLOCKED 0x20 +#define ZPCI_FIB_FC_DMAAS_REG 0x10 + +/* Function Information Block */ +typedef struct ZpciFib { + uint8_t fmt; /* format */ + uint8_t reserved1[7]; + uint8_t fc; /* function controls */ + uint8_t reserved2; + uint16_t reserved3; + uint32_t reserved4; + uint64_t pba; /* PCI base address */ + uint64_t pal; /* PCI address limit */ + uint64_t iota; /* I/O Translation Anchor */ +#define FIB_DATA_ISC(x) (((x) >> 28) & 0x7) +#define FIB_DATA_NOI(x) (((x) >> 16) & 0xfff) +#define FIB_DATA_AIBVO(x) (((x) >> 8) & 0x3f) +#define FIB_DATA_SUM(x) (((x) >> 7) & 0x1) +#define FIB_DATA_AISBO(x) ((x) & 0x3f) + uint32_t data; + uint32_t reserved5; + uint64_t aibv; /* Adapter int bit vector address */ + uint64_t aisb; /* Adapter int summary bit address */ + uint64_t fmb_addr; /* Function measurement address and key */ + uint32_t reserved6; + uint32_t gd; +} QEMU_PACKED ZpciFib; + +int clp_service_call(S390CPU *cpu, uint8_t r2); +int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); +int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); +int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); +int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr); +int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba); +int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba); + +#endif diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index bc4dc2ae8a..71bafe06ee 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -18,6 +18,7 @@ #include "css.h" #include "virtio-ccw.h" #include "qemu/config-file.h" +#include "s390-pci-bus.h" #define TYPE_S390_CCW_MACHINE "s390-ccw-machine" @@ -91,6 +92,7 @@ static void ccw_init(MachineState *machine) uint8_t *storage_keys; int ret; VirtualCssBus *css_bus; + DeviceState *dev; QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); ram_addr_t pad_size = 0; ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size); @@ -127,6 +129,11 @@ static void ccw_init(MachineState *machine) machine->initrd_filename, "s390-ccw.img"); s390_flic_init(); + dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE); + object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE, + OBJECT(dev), NULL); + qdev_init_nofail(dev); + /* register hypercalls */ virtio_ccw_register_hcalls(); @@ -181,7 +188,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_serial = 1; mc->no_parallel = 1; mc->no_sdcard = 1; - mc->use_sclp = 1, + mc->use_sclp = 1; mc->max_cpus = 255; nc->nmi_monitor_handler = s390_nmi; } diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index a759da7f34..a969975a78 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -20,6 +20,7 @@ #include "qemu/config-file.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" +#include "hw/s390x/s390-pci-bus.h" static inline SCLPEventFacility *get_event_facility(void) { @@ -62,7 +63,8 @@ static void read_SCP_info(SCCB *sccb) read_info->entries[i].type = 0; } - read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO); + read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | + SCLP_HAS_PCI_RECONFIG); /* * The storage increment size is a multiple of 1M and is a power of 2. @@ -350,6 +352,12 @@ static void sclp_execute(SCCB *sccb, uint32_t code) case SCLP_UNASSIGN_STORAGE: unassign_storage(sccb); break; + case SCLP_CMDW_CONFIGURE_PCI: + s390_pci_sclp_configure(1, sccb); + break; + case SCLP_CMDW_DECONFIGURE_PCI: + s390_pci_sclp_configure(0, sccb); + break; default: efc->command_handler(ef, sccb, code); break; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b4e73d1f35..014a92ce5f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2129,16 +2129,19 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, */ static void vfio_disable_interrupts(VFIOPCIDevice *vdev) { - switch (vdev->interrupt) { - case VFIO_INT_INTx: - vfio_disable_intx(vdev); - break; - case VFIO_INT_MSI: - vfio_disable_msi(vdev); - break; - case VFIO_INT_MSIX: + /* + * More complicated than it looks. Disabling MSI/X transitions the + * device to INTx mode (if supported). Therefore we need to first + * disable MSI/X and then cleanup by disabling INTx. + */ + if (vdev->interrupt == VFIO_INT_MSIX) { vfio_disable_msix(vdev); - break; + } else if (vdev->interrupt == VFIO_INT_MSI) { + vfio_disable_msi(vdev); + } + + if (vdev->interrupt == VFIO_INT_INTx) { + vfio_disable_intx(vdev); } } @@ -2301,7 +2304,7 @@ static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr) static void vfio_map_bar(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; - unsigned size = bar->region.size; + uint64_t size = bar->region.size; char name[64]; uint32_t pci_bar; uint8_t type; @@ -2351,7 +2354,7 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr) } if (vdev->msix && vdev->msix->table_bar == nr) { - unsigned start; + uint64_t start; start = HOST_PAGE_ALIGN(vdev->msix->table_offset + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE)); diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index c1bf357154..f2893b28aa 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -736,7 +736,7 @@ static int xen_pt_initfn(PCIDevice *d) } out: - memory_listener_register(&s->memory_listener, &address_space_memory); + memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); memory_listener_register(&s->io_listener, &address_space_io); XEN_PT_LOG(d, "Real physical device %02x:%02x.%d registered successfully!\n", |