diff options
| -rw-r--r-- | blockdev.c | 9 | ||||
| -rw-r--r-- | hmp-commands.hx | 20 | ||||
| -rw-r--r-- | hmp.c | 23 | ||||
| -rw-r--r-- | hmp.h | 2 | ||||
| -rw-r--r-- | hw/pci/msi.c | 4 | ||||
| -rw-r--r-- | hw/pci/pci-stub.c | 14 | ||||
| -rw-r--r-- | hw/pci/pcie_aer.c | 39 | ||||
| -rw-r--r-- | hw/xen/xen_pt.c | 51 | ||||
| -rw-r--r-- | hw/xen/xen_pt.h | 7 | ||||
| -rw-r--r-- | hw/xen/xen_pt_config_init.c | 235 | ||||
| -rw-r--r-- | hw/xen/xen_pt_msi.c | 12 | ||||
| -rw-r--r-- | include/hw/pci/pci_regs.h | 2 | ||||
| -rw-r--r-- | include/monitor/monitor.h | 7 | ||||
| -rw-r--r-- | include/sysemu/blockdev.h | 2 | ||||
| -rw-r--r-- | include/sysemu/sysemu.h | 4 | ||||
| -rw-r--r-- | monitor.c | 397 | ||||
| -rw-r--r-- | qapi-schema.json | 19 | ||||
| -rw-r--r-- | qmp-commands.hx | 16 | ||||
| -rw-r--r-- | stubs/mon-is-qmp.c | 4 |
19 files changed, 448 insertions, 419 deletions
diff --git a/blockdev.c b/blockdev.c index 5eaf77e599..de94a8bcb3 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2113,7 +2113,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, aio_context_release(aio_context); } -int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) +void hmp_drive_del(Monitor *mon, const QDict *qdict) { const char *id = qdict_get_str(qdict, "id"); BlockBackend *blk; @@ -2124,14 +2124,14 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) blk = blk_by_name(id); if (!blk) { error_report("Device '%s' not found", id); - return -1; + return; } bs = blk_bs(blk); if (!blk_legacy_dinfo(blk)) { error_report("Deleting device added with blockdev-add" " is not supported"); - return -1; + return; } aio_context = bdrv_get_aio_context(bs); @@ -2140,7 +2140,7 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { error_report_err(local_err); aio_context_release(aio_context); - return -1; + return; } /* quiesce block driver; prevent further io */ @@ -2163,7 +2163,6 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) } aio_context_release(aio_context); - return 0; } void qmp_block_resize(bool has_device, const char *device, diff --git a/hmp-commands.hx b/hmp-commands.hx index e864a6ca81..3d7dfccf7c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -178,8 +178,7 @@ ETEXI .args_type = "id:B", .params = "device", .help = "remove host block device", - .user_print = monitor_user_noop, - .mhandler.cmd_new = hmp_drive_del, + .mhandler.cmd = hmp_drive_del, }, STEXI @@ -654,8 +653,7 @@ ETEXI .args_type = "device:O", .params = "driver[,prop=value][,...]", .help = "add device, like -device on the command line", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_device_add, + .mhandler.cmd = hmp_device_add, .command_completion = device_add_completion, }, @@ -1011,17 +1009,16 @@ ETEXI .name = "client_migrate_info", .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", .params = "protocol hostname port tls-port cert-subject", - .help = "send migration info to spice/vnc client", - .user_print = monitor_user_noop, - .mhandler.cmd_new = client_migrate_info, + .help = "set migration information for remote display", + .mhandler.cmd = hmp_client_migrate_info, }, STEXI @item client_migrate_info @var{protocol} @var{hostname} @var{port} @var{tls-port} @var{cert-subject} @findex client_migrate_info -Set the spice/vnc connection info for the migration target. The spice/vnc -server will ask the spice/vnc client to automatically reconnect using the -new parameters (if specified) once the vm migration finished successfully. +Set migration information for remote display. This makes the server +ask the client to automatically reconnect using the new parameters +once migration finished successfully. Only implemented for SPICE. ETEXI { @@ -1186,8 +1183,7 @@ ETEXI "<error_status> = error string or 32bit\n\t\t\t" "<tlb header> = 32bit x 4\n\t\t\t" "<tlb header prefix> = 32bit x 4", - .user_print = pcie_aer_inject_error_print, - .mhandler.cmd_new = hmp_pcie_aer_inject_error, + .mhandler.cmd = hmp_pcie_aer_inject_error, }, STEXI diff --git a/hmp.c b/hmp.c index e17852d1f9..514f22fbfa 100644 --- a/hmp.c +++ b/hmp.c @@ -22,6 +22,7 @@ #include "qmp-commands.h" #include "qemu/sockets.h" #include "monitor/monitor.h" +#include "monitor/qdev.h" #include "qapi/opts-visitor.h" #include "qapi/string-output-visitor.h" #include "qapi-visit.h" @@ -1250,6 +1251,23 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) } } +void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *hostname = qdict_get_str(qdict, "hostname"); + bool has_port = qdict_haskey(qdict, "port"); + int port = qdict_get_try_int(qdict, "port", -1); + bool has_tls_port = qdict_haskey(qdict, "tls-port"); + int tls_port = qdict_get_try_int(qdict, "tls-port", -1); + const char *cert_subject = qdict_get_try_str(qdict, "cert-subject"); + + qmp_client_migrate_info(protocol, hostname, + has_port, port, has_tls_port, tls_port, + !!cert_subject, cert_subject, &err); + hmp_handle_error(mon, &err); +} + void hmp_set_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); @@ -1482,6 +1500,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) } } +void hmp_device_add(Monitor *mon, const QDict *qdict) +{ + do_device_add(mon, qdict, NULL); +} + void hmp_device_del(Monitor *mon, const QDict *qdict) { const char *id = qdict_get_str(qdict, "id"); diff --git a/hmp.h b/hmp.h index a158e3fda1..a70ac4fd0f 100644 --- a/hmp.h +++ b/hmp.h @@ -67,6 +67,7 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict); void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict); +void hmp_client_migrate_info(Monitor *mon, const QDict *qdict); void hmp_set_password(Monitor *mon, const QDict *qdict); void hmp_expire_password(Monitor *mon, const QDict *qdict); void hmp_eject(Monitor *mon, const QDict *qdict); @@ -79,6 +80,7 @@ void hmp_block_job_pause(Monitor *mon, const QDict *qdict); void hmp_block_job_resume(Monitor *mon, const QDict *qdict); void hmp_block_job_complete(Monitor *mon, const QDict *qdict); void hmp_migrate(Monitor *mon, const QDict *qdict); +void hmp_device_add(Monitor *mon, const QDict *qdict); void hmp_device_del(Monitor *mon, const QDict *qdict); void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict); void hmp_netdev_add(Monitor *mon, const QDict *qdict); diff --git a/hw/pci/msi.c b/hw/pci/msi.c index c111dbaff6..f9c0484420 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -21,10 +21,6 @@ #include "hw/pci/msi.h" #include "qemu/range.h" -/* Eventually those constants should go to Linux pci_regs.h */ -#define PCI_MSI_PENDING_32 0x10 -#define PCI_MSI_PENDING_64 0x14 - /* PCI_MSI_ADDRESS_LO */ #define PCI_MSI_ADDRESS_LO_MASK (~0x3) diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c index 5e564c3a87..f8f237e823 100644 --- a/hw/pci/pci-stub.c +++ b/hw/pci/pci-stub.c @@ -29,19 +29,7 @@ PciInfoList *qmp_query_pci(Error **errp) return NULL; } -static void pci_error_message(Monitor *mon) +void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "PCI devices not supported\n"); } - -int hmp_pcie_aer_inject_error(Monitor *mon, - const QDict *qdict, QObject **ret_data) -{ - pci_error_message(mon); - return -ENOSYS; -} - -void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) -{ - pci_error_message(mon); -} diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index b48c09cd11..c8dea8ed9c 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -815,21 +815,6 @@ const VMStateDescription vmstate_pcie_aer_log = { } }; -void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) -{ - QDict *qdict; - int devfn; - assert(qobject_type(data) == QTYPE_QDICT); - qdict = qobject_to_qdict(data); - - devfn = (int)qdict_get_int(qdict, "devfn"); - monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n", - qdict_get_str(qdict, "id"), - qdict_get_str(qdict, "root_bus"), - (int) qdict_get_int(qdict, "bus"), - PCI_SLOT(devfn), PCI_FUNC(devfn)); -} - typedef struct PCIEAERErrorName { const char *name; uint32_t val; @@ -962,8 +947,8 @@ static int pcie_aer_parse_error_string(const char *error_name, return -EINVAL; } -int hmp_pcie_aer_inject_error(Monitor *mon, - const QDict *qdict, QObject **ret_data) +static int do_pcie_aer_inject_error(Monitor *mon, + const QDict *qdict, QObject **ret_data) { const char *id = qdict_get_str(qdict, "id"); const char *error_name; @@ -1035,3 +1020,23 @@ int hmp_pcie_aer_inject_error(Monitor *mon, return 0; } + +void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) +{ + QObject *data; + int devfn; + + if (do_pcie_aer_inject_error(mon, qdict, &data) < 0) { + return; + } + + assert(qobject_type(data) == QTYPE_QDICT); + qdict = qobject_to_qdict(data); + + devfn = (int)qdict_get_int(qdict, "devfn"); + monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n", + qdict_get_str(qdict, "id"), + qdict_get_str(qdict, "root_bus"), + (int) qdict_get_int(qdict, "bus"), + PCI_SLOT(devfn), PCI_FUNC(devfn)); +} diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index d095c081cc..9afcda8e21 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -234,11 +234,12 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, int index = 0; XenPTRegGroup *reg_grp_entry = NULL; int rc = 0; - uint32_t read_val = 0; + uint32_t read_val = 0, wb_mask; int emul_len = 0; XenPTReg *reg_entry = NULL; uint32_t find_addr = addr; XenPTRegInfo *reg = NULL; + bool wp_flag = false; if (xen_pt_pci_config_access_check(d, addr, len)) { return; @@ -271,10 +272,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, if (rc < 0) { XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); memset(&read_val, 0xff, len); + wb_mask = 0; + } else { + wb_mask = 0xFFFFFFFF >> ((4 - len) << 3); } /* pass directly to the real device for passthrough type register group */ if (reg_grp_entry == NULL) { + if (!s->permissive) { + wb_mask = 0; + wp_flag = true; + } goto out; } @@ -295,9 +303,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); uint8_t *ptr_val = NULL; + uint32_t wp_mask = reg->emu_mask | reg->ro_mask; valid_mask <<= (find_addr - real_offset) << 3; ptr_val = (uint8_t *)&val + (real_offset & 3); + if (!s->permissive) { + wp_mask |= reg->res_mask; + } + if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) { + wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3)) + << ((len - emul_len) << 3)); + } /* do emulation based on register size */ switch (reg->size) { @@ -339,6 +355,16 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, } else { /* nothing to do with passthrough type register, * continue to find next byte */ + if (!s->permissive) { + wb_mask &= ~(0xff << ((len - emul_len) << 3)); + /* Unused BARs will make it here, but we don't want to issue + * warnings for writes to them (bogus writes get dealt with + * above). + */ + if (index < 0) { + wp_flag = true; + } + } emul_len--; find_addr++; } @@ -350,10 +376,26 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, memory_region_transaction_commit(); out: - if (!(reg && reg->no_wb)) { + if (wp_flag && !s->permissive_warned) { + s->permissive_warned = true; + xen_pt_log(d, "Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n", + addr, len * 2, wb_mask); + xen_pt_log(d, "If the device doesn't work, try enabling permissive mode\n"); + xen_pt_log(d, "(unsafe) and if it helps report the problem to xen-devel\n"); + } + for (index = 0; wb_mask; index += len) { /* unknown regs are passed through */ - rc = xen_host_pci_set_block(&s->real_device, addr, - (uint8_t *)&val, len); + while (!(wb_mask & 0xff)) { + index++; + wb_mask >>= 8; + } + len = 0; + do { + len++; + wb_mask >>= 8; + } while (wb_mask & 0xff); + rc = xen_host_pci_set_block(&s->real_device, addr + index, + (uint8_t *)&val + index, len); if (rc < 0) { XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); @@ -807,6 +849,7 @@ static void xen_pt_unregister_device(PCIDevice *d) static Property xen_pci_passthrough_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr), + DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 942dc60cc7..4bba559763 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -101,12 +101,12 @@ struct XenPTRegInfo { uint32_t offset; uint32_t size; uint32_t init_val; + /* reg reserved field mask (ON:reserved, OFF:defined) */ + uint32_t res_mask; /* reg read only field mask (ON:RO/ROS, OFF:other) */ uint32_t ro_mask; /* reg emulate field mask (ON:emu, OFF:passthrough) */ uint32_t emu_mask; - /* no write back allowed */ - uint32_t no_wb; xen_pt_conf_reg_init init; /* read/write function pointer * for double_word/word/byte size */ @@ -177,6 +177,7 @@ typedef struct XenPTMSIXEntry { uint32_t data; uint32_t vector_ctrl; bool updated; /* indicate whether MSI ADDR or DATA is updated */ + bool warned; /* avoid issuing (bogus) warning more than once */ } XenPTMSIXEntry; typedef struct XenPTMSIX { uint32_t ctrl_offset; @@ -196,6 +197,8 @@ struct XenPCIPassthroughState { PCIHostDeviceAddress hostaddr; bool is_virtfn; + bool permissive; + bool permissive_warned; XenHostPCIDevice real_device; XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */ QLIST_HEAD(, XenPTRegGroup) reg_grps; diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index 95a51dbbb1..f3cf069b60 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -95,6 +95,18 @@ XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) return NULL; } +static uint32_t get_throughable_mask(const XenPCIPassthroughState *s, + const XenPTRegInfo *reg, + uint32_t valid_mask) +{ + uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask); + + if (!s->permissive) { + throughable_mask &= ~reg->res_mask; + } + + return throughable_mask & valid_mask; +} /**************** * general register functions @@ -157,14 +169,13 @@ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint8_t writable_mask = 0; - uint8_t throughable_mask = 0; + uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask); /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; @@ -175,14 +186,13 @@ static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; - uint16_t throughable_mask = 0; + uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; @@ -193,14 +203,13 @@ static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; - uint32_t throughable_mask = 0; + uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; @@ -292,15 +301,13 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; - uint16_t throughable_mask = 0; + uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); /* modify emulate register */ writable_mask = ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; - if (*val & PCI_COMMAND_INTX_DISABLE) { throughable_mask |= PCI_COMMAND_INTX_DISABLE; } else { @@ -454,7 +461,6 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, PCIDevice *d = &s->dev; const PCIIORegion *r; uint32_t writable_mask = 0; - uint32_t throughable_mask = 0; uint32_t bar_emu_mask = 0; uint32_t bar_ro_mask = 0; uint32_t r_size = 0; @@ -511,8 +517,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, } /* create value for writing to I/O device register */ - throughable_mask = ~bar_emu_mask & valid_mask; - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); return 0; } @@ -526,9 +531,8 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, XenPTRegion *base = NULL; PCIDevice *d = (PCIDevice *)&s->dev; uint32_t writable_mask = 0; - uint32_t throughable_mask = 0; + uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); pcibus_t r_size = 0; - uint32_t bar_emu_mask = 0; uint32_t bar_ro_mask = 0; r_size = d->io_regions[PCI_ROM_SLOT].size; @@ -537,7 +541,6 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, r_size = xen_pt_get_emul_size(base->bar_flag, r_size); /* set emulate mask and read-only mask */ - bar_emu_mask = reg->emu_mask; bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; /* modify emulate register */ @@ -545,7 +548,6 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~bar_emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; @@ -580,7 +582,7 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = { .offset = PCI_COMMAND, .size = 2, .init_val = 0x0000, - .ro_mask = 0xF880, + .res_mask = 0xF880, .emu_mask = 0x0743, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_word_reg_read, @@ -605,7 +607,8 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = { .offset = PCI_STATUS, .size = 2, .init_val = 0x0000, - .ro_mask = 0x06FF, + .res_mask = 0x0007, + .ro_mask = 0x06F8, .emu_mask = 0x0010, .init = xen_pt_status_reg_init, .u.w.read = xen_pt_word_reg_read, @@ -755,6 +758,15 @@ static XenPTRegInfo xen_pt_emu_reg_vpd[] = { .u.b.write = xen_pt_byte_reg_write, }, { + .offset = PCI_VPD_ADDR, + .size = 2, + .ro_mask = 0x0003, + .emu_mask = 0x0003, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + { .size = 0, }, }; @@ -873,7 +885,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { .offset = PCI_EXP_DEVCAP, .size = 4, .init_val = 0x00000000, - .ro_mask = 0x1FFCFFFF, + .ro_mask = 0xFFFFFFFF, .emu_mask = 0x10000000, .init = xen_pt_common_reg_init, .u.dw.read = xen_pt_long_reg_read, @@ -890,6 +902,16 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, + /* Device Status reg */ + { + .offset = PCI_EXP_DEVSTA, + .size = 2, + .res_mask = 0xFFC0, + .ro_mask = 0x0030, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, /* Link Control reg */ { .offset = PCI_EXP_LNKCTL, @@ -901,6 +923,15 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, + /* Link Status reg */ + { + .offset = PCI_EXP_LNKSTA, + .size = 2, + .ro_mask = 0x3FFF, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, /* Device Control 2 reg */ { .offset = 0x28, @@ -933,39 +964,22 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { * Power Management Capability */ -/* read Power Management Control/Status register */ -static int xen_pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint16_t *value, uint16_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - uint16_t valid_emu_mask = reg->emu_mask; - - valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; - - valid_emu_mask = valid_emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); - - return 0; -} /* write Power Management Control/Status register */ static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; - uint16_t emu_mask = reg->emu_mask; uint16_t writable_mask = 0; - uint16_t throughable_mask = 0; - - emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; + uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); /* modify emulate register */ - writable_mask = emu_mask & ~reg->ro_mask & valid_mask; + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~emu_mask & valid_mask; - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS, + throughable_mask); return 0; } @@ -999,10 +1013,11 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = { .offset = PCI_PM_CTRL, .size = 2, .init_val = 0x0008, - .ro_mask = 0xE1FC, - .emu_mask = 0x8100, + .res_mask = 0x00F0, + .ro_mask = 0xE10C, + .emu_mask = 0x810B, .init = xen_pt_common_reg_init, - .u.w.read = xen_pt_pmcsr_reg_read, + .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_pmcsr_reg_write, }, { @@ -1016,13 +1031,9 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = { */ /* Helper */ -static bool xen_pt_msgdata_check_type(uint32_t offset, uint16_t flags) -{ - /* check the offset whether matches the type or not */ - bool is_32 = (offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT); - bool is_64 = (offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT); - return is_32 || is_64; -} +#define xen_pt_msi_check_type(offset, flags, what) \ + ((offset) == ((flags) & PCI_MSI_FLAGS_64BIT ? \ + PCI_MSI_##what##_64 : PCI_MSI_##what##_32)) /* Message Control register */ static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s, @@ -1056,8 +1067,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, XenPTRegInfo *reg = cfg_entry->reg; XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; - uint16_t throughable_mask = 0; - uint16_t raw_val; + uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); /* Currently no support for multi-vector */ if (*val & PCI_MSI_FLAGS_QSIZE) { @@ -1070,12 +1080,10 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE; /* create value for writing to I/O device register */ - raw_val = *val; - throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); /* update MSI */ - if (raw_val & PCI_MSI_FLAGS_ENABLE) { + if (*val & PCI_MSI_FLAGS_ENABLE) { /* setup MSI pirq for the first time */ if (!msi->initialized) { /* Init physical one */ @@ -1103,10 +1111,6 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, xen_pt_msi_disable(s); } - /* pass through MSI_ENABLE bit */ - *val &= ~PCI_MSI_FLAGS_ENABLE; - *val |= raw_val & PCI_MSI_FLAGS_ENABLE; - return 0; } @@ -1134,7 +1138,45 @@ static int xen_pt_msgdata_reg_init(XenPCIPassthroughState *s, uint32_t offset = reg->offset; /* check the offset whether matches the type or not */ - if (xen_pt_msgdata_check_type(offset, flags)) { + if (xen_pt_msi_check_type(offset, flags, DATA)) { + *data = reg->init_val; + } else { + *data = XEN_PT_INVALID_REG; + } + return 0; +} + +/* this function will be called twice (for 32 bit and 64 bit type) */ +/* initialize Mask register */ +static int xen_pt_mask_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint32_t flags = s->msi->flags; + + /* check the offset whether matches the type or not */ + if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { + *data = XEN_PT_INVALID_REG; + } else if (xen_pt_msi_check_type(reg->offset, flags, MASK)) { + *data = reg->init_val; + } else { + *data = XEN_PT_INVALID_REG; + } + return 0; +} + +/* this function will be called twice (for 32 bit and 64 bit type) */ +/* initialize Pending register */ +static int xen_pt_pending_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint32_t flags = s->msi->flags; + + /* check the offset whether matches the type or not */ + if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { + *data = XEN_PT_INVALID_REG; + } else if (xen_pt_msi_check_type(reg->offset, flags, PENDING)) { *data = reg->init_val; } else { *data = XEN_PT_INVALID_REG; @@ -1149,7 +1191,6 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; - uint32_t throughable_mask = 0; uint32_t old_addr = cfg_entry->data; /* modify emulate register */ @@ -1158,8 +1199,7 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s, s->msi->addr_lo = cfg_entry->data; /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ if (cfg_entry->data != old_addr) { @@ -1177,7 +1217,6 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; - uint32_t throughable_mask = 0; uint32_t old_addr = cfg_entry->data; /* check whether the type is 64 bit or not */ @@ -1194,8 +1233,7 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, s->msi->addr_hi = cfg_entry->data; /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ if (cfg_entry->data != old_addr) { @@ -1217,12 +1255,11 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, XenPTRegInfo *reg = cfg_entry->reg; XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; - uint16_t throughable_mask = 0; uint16_t old_data = cfg_entry->data; uint32_t offset = reg->offset; /* check the offset whether matches the type or not */ - if (!xen_pt_msgdata_check_type(offset, msi->flags)) { + if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) { /* exit I/O emulator */ XEN_PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n"); return -1; @@ -1235,8 +1272,7 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, msi->data = cfg_entry->data; /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ if (cfg_entry->data != old_data) { @@ -1266,8 +1302,9 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = { .offset = PCI_MSI_FLAGS, .size = 2, .init_val = 0x0000, - .ro_mask = 0xFF8E, - .emu_mask = 0x007F, + .res_mask = 0xFE00, + .ro_mask = 0x018E, + .emu_mask = 0x017E, .init = xen_pt_msgctrl_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_msgctrl_reg_write, @@ -1279,7 +1316,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = { .init_val = 0x00000000, .ro_mask = 0x00000003, .emu_mask = 0xFFFFFFFF, - .no_wb = 1, .init = xen_pt_common_reg_init, .u.dw.read = xen_pt_long_reg_read, .u.dw.write = xen_pt_msgaddr32_reg_write, @@ -1291,7 +1327,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = { .init_val = 0x00000000, .ro_mask = 0x00000000, .emu_mask = 0xFFFFFFFF, - .no_wb = 1, .init = xen_pt_msgaddr64_reg_init, .u.dw.read = xen_pt_long_reg_read, .u.dw.write = xen_pt_msgaddr64_reg_write, @@ -1303,7 +1338,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = { .init_val = 0x0000, .ro_mask = 0x0000, .emu_mask = 0xFFFF, - .no_wb = 1, .init = xen_pt_msgdata_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_msgdata_reg_write, @@ -1315,11 +1349,54 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = { .init_val = 0x0000, .ro_mask = 0x0000, .emu_mask = 0xFFFF, - .no_wb = 1, .init = xen_pt_msgdata_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_msgdata_reg_write, }, + /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */ + { + .offset = PCI_MSI_MASK_32, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0xFFFFFFFF, + .emu_mask = 0xFFFFFFFF, + .init = xen_pt_mask_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_long_reg_write, + }, + /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */ + { + .offset = PCI_MSI_MASK_64, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0xFFFFFFFF, + .emu_mask = 0xFFFFFFFF, + .init = xen_pt_mask_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_long_reg_write, + }, + /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */ + { + .offset = PCI_MSI_MASK_32 + 4, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0xFFFFFFFF, + .emu_mask = 0x00000000, + .init = xen_pt_pending_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_long_reg_write, + }, + /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */ + { + .offset = PCI_MSI_MASK_64 + 4, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0xFFFFFFFF, + .emu_mask = 0x00000000, + .init = xen_pt_pending_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_long_reg_write, + }, { .size = 0, }, @@ -1358,7 +1435,7 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; - uint16_t throughable_mask = 0; + uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); int debug_msix_enabled_old; /* modify emulate register */ @@ -1366,7 +1443,6 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s, cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); /* update MSI-X */ @@ -1405,7 +1481,8 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = { .offset = PCI_MSI_FLAGS, .size = 2, .init_val = 0x0000, - .ro_mask = 0x3FFF, + .res_mask = 0x3800, + .ro_mask = 0x07FF, .emu_mask = 0x0000, .init = xen_pt_msixctrl_reg_init, .u.w.read = xen_pt_word_reg_read, diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 9ed9321c9f..68db6233dc 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -434,11 +434,10 @@ static void pci_msix_write(void *opaque, hwaddr addr, XenPCIPassthroughState *s = opaque; XenPTMSIX *msix = s->msix; XenPTMSIXEntry *entry; - int entry_nr, offset; + unsigned int entry_nr, offset; entry_nr = addr / PCI_MSIX_ENTRY_SIZE; - if (entry_nr < 0 || entry_nr >= msix->total_entries) { - XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr); + if (entry_nr >= msix->total_entries) { return; } entry = &msix->msix_entry[entry_nr]; @@ -460,8 +459,11 @@ static void pci_msix_write(void *opaque, hwaddr addr, + PCI_MSIX_ENTRY_VECTOR_CTRL; if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { - XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is" - " already enabled.\n", entry_nr); + if (!entry->warned) { + entry->warned = true; + XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is" + " already enabled.\n", entry_nr); + } return; } diff --git a/include/hw/pci/pci_regs.h b/include/hw/pci/pci_regs.h index 56a404be6e..57e8c80c30 100644 --- a/include/hw/pci/pci_regs.h +++ b/include/hw/pci/pci_regs.h @@ -298,8 +298,10 @@ #define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ #define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ #define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ +#define PCI_MSI_PENDING_32 16 /* Pending bits register for 32-bit devices */ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ #define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ +#define PCI_MSI_PENDING_64 20 /* Pending bits register for 32-bit devices */ /* MSI-X registers */ #define PCI_MSIX_FLAGS 2 diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index df67d56ec0..57f8394a94 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -16,10 +16,7 @@ extern Monitor *default_mon; #define MONITOR_USE_CONTROL 0x04 #define MONITOR_USE_PRETTY 0x08 -/* flags for monitor commands */ -#define MONITOR_CMD_ASYNC 0x0001 - -int monitor_cur_is_qmp(void); +bool monitor_cur_is_qmp(void); void monitor_init(CharDriverState *chr, int flags); @@ -43,8 +40,6 @@ void monitor_flush(Monitor *mon); int monitor_set_cpu(int cpu_index); int monitor_get_cpu_index(void); -typedef void (MonitorCompletion)(void *opaque, QObject *ret_data); - void monitor_set_error(Monitor *mon, QError *qerror); void monitor_read_command(Monitor *mon, int show_prompt); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 7ca59b5070..310415025c 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -66,5 +66,5 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type); void qmp_change_blockdev(const char *device, const char *filename, const char *format, Error **errp); void hmp_commit(Monitor *mon, const QDict *qdict); -int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); +void hmp_drive_del(Monitor *mon, const QDict *qdict); #endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 8a52934728..e10c2c5217 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -161,9 +161,7 @@ extern unsigned int nb_prom_envs; void hmp_drive_add(Monitor *mon, const QDict *qdict); /* pcie aer error injection */ -void pcie_aer_inject_error_print(Monitor *mon, const QObject *data); -int hmp_pcie_aer_inject_error(Monitor *mon, - const QDict *qdict, QObject **ret_data); +void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); /* serial ports */ diff --git a/monitor.c b/monitor.c index b2561e1e2d..c7baa9198c 100644 --- a/monitor.c +++ b/monitor.c @@ -118,25 +118,15 @@ * */ -typedef struct MonitorCompletionData MonitorCompletionData; -struct MonitorCompletionData { - Monitor *mon; - void (*user_print)(Monitor *mon, const QObject *data); -}; - typedef struct mon_cmd_t { const char *name; const char *args_type; const char *params; const char *help; - void (*user_print)(Monitor *mon, const QObject *data); union { void (*cmd)(Monitor *mon, const QDict *qdict); int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data); - int (*cmd_async)(Monitor *mon, const QDict *params, - MonitorCompletion *cb, void *opaque); } mhandler; - int flags; /* @sub_table is a list of 2nd level of commands. If it do not exist, * mhandler should be used. If it exist, sub_table[?].mhandler should be * used, and mhandler of 1st level plays the role of help function. @@ -171,11 +161,16 @@ struct MonFdset { QLIST_ENTRY(MonFdset) next; }; -typedef struct MonitorControl { +typedef struct { QObject *id; JSONMessageParser parser; - int command_mode; -} MonitorControl; + /* + * When a client connects, we're in capabilities negotiation mode. + * When command qmp_capabilities succeeds, we go into command + * mode. + */ + bool in_command_mode; /* are we in command mode? */ +} MonitorQMP; /* * To prevent flooding clients, events can be throttled. The @@ -205,7 +200,7 @@ struct Monitor { int mux_out; ReadLineState *rs; - MonitorControl *mc; + MonitorQMP qmp; CPUState *mon_cpu; BlockCompletionFunc *password_completion_cb; void *password_opaque; @@ -236,21 +231,20 @@ Monitor *default_mon; static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque); -static inline int qmp_cmd_mode(const Monitor *mon) -{ - return (mon->mc ? mon->mc->command_mode : 0); -} - -/* Return true if in control mode, false otherwise */ -static inline int monitor_ctrl_mode(const Monitor *mon) +/** + * Is @mon a QMP monitor? + */ +static inline bool monitor_is_qmp(const Monitor *mon) { return (mon->flags & MONITOR_USE_CONTROL); } -/* Return non-zero iff we have a current monitor, and it is in QMP mode. */ -int monitor_cur_is_qmp(void) +/** + * Is the current monitor, if any, a QMP monitor? + */ +bool monitor_cur_is_qmp(void) { - return cur_mon && monitor_ctrl_mode(cur_mon); + return cur_mon && monitor_is_qmp(cur_mon); } void monitor_read_command(Monitor *mon, int show_prompt) @@ -360,7 +354,7 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) if (!mon) return; - if (monitor_ctrl_mode(mon)) { + if (monitor_is_qmp(mon)) { return; } @@ -387,23 +381,6 @@ static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream, return 0; } -static void monitor_user_noop(Monitor *mon, const QObject *data) { } - -static inline int handler_is_qobject(const mon_cmd_t *cmd) -{ - return cmd->user_print != NULL; -} - -static inline bool handler_is_async(const mon_cmd_t *cmd) -{ - return cmd->flags & MONITOR_CMD_ASYNC; -} - -static inline int monitor_has_error(const Monitor *mon) -{ - return mon->error != NULL; -} - static void monitor_json_emitter(Monitor *mon, const QObject *data) { QString *json; @@ -418,24 +395,25 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data) QDECREF(json); } -static QDict *build_qmp_error_dict(const QError *err) +static QDict *build_qmp_error_dict(Error *err) { QObject *obj; - obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %p } }", - ErrorClass_lookup[err->err_class], - qerror_human(err)); + obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %s } }", + ErrorClass_lookup[error_get_class(err)], + error_get_pretty(err)); return qobject_to_qdict(obj); } -static void monitor_protocol_emitter(Monitor *mon, QObject *data) +static void monitor_protocol_emitter(Monitor *mon, QObject *data, + Error *err) { QDict *qmp; trace_monitor_protocol_emitter(mon); - if (!monitor_has_error(mon)) { + if (!err) { /* success response */ qmp = qdict_new(); if (data) { @@ -447,14 +425,12 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) } } else { /* error response */ - qmp = build_qmp_error_dict(mon->error); - QDECREF(mon->error); - mon->error = NULL; + qmp = build_qmp_error_dict(err); } - if (mon->mc->id) { - qdict_put_obj(qmp, "id", mon->mc->id); - mon->mc->id = NULL; + if (mon->qmp.id) { + qdict_put_obj(qmp, "id", mon->qmp.id); + mon->qmp.id = NULL; } monitor_json_emitter(mon, QOBJECT(qmp)); @@ -474,7 +450,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QObject *data) trace_monitor_protocol_event_emit(event, data); QLIST_FOREACH(mon, &mon_list, entry) { - if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) { + if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) { monitor_json_emitter(mon, data); } } @@ -594,15 +570,11 @@ static void monitor_qapi_event_init(void) static int do_qmp_capabilities(Monitor *mon, const QDict *params, QObject **ret_data) { - /* Will setup QMP capabilities in the future */ - if (monitor_ctrl_mode(mon)) { - mon->mc->command_mode = 1; - } - + mon->qmp.in_command_mode = true; return 0; } -static void handle_user_command(Monitor *mon, const char *cmdline); +static void handle_hmp_command(Monitor *mon, const char *cmdline); static void monitor_data_init(Monitor *mon) { @@ -641,7 +613,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, } } - handle_user_command(&hmp, command_line); + handle_hmp_command(&hmp, command_line); cur_mon = old_mon; qemu_mutex_lock(&hmp.out_lock); @@ -917,45 +889,6 @@ static void hmp_trace_file(Monitor *mon, const QDict *qdict) } #endif -static void user_monitor_complete(void *opaque, QObject *ret_data) -{ - MonitorCompletionData *data = (MonitorCompletionData *)opaque; - - if (ret_data) { - data->user_print(data->mon, ret_data); - } - monitor_resume(data->mon); - g_free(data); -} - -static void qmp_monitor_complete(void *opaque, QObject *ret_data) -{ - monitor_protocol_emitter(opaque, ret_data); -} - -static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, - const QDict *params) -{ - return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon); -} - -static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, - const QDict *params) -{ - int ret; - - MonitorCompletionData *cb_data = g_malloc(sizeof(*cb_data)); - cb_data->mon = mon; - cb_data->user_print = cmd->user_print; - monitor_suspend(mon); - ret = cmd->mhandler.cmd_async(mon, params, - user_monitor_complete, cb_data); - if (ret < 0) { - monitor_resume(mon); - g_free(cb_data); - } -} - static void hmp_info_help(Monitor *mon, const QDict *qdict) { help_cmd(mon, "info"); @@ -1085,39 +1018,33 @@ static void hmp_info_trace_events(Monitor *mon, const QDict *qdict) qapi_free_TraceEventInfoList(events); } -static int client_migrate_info(Monitor *mon, const QDict *qdict, - QObject **ret_data) +void qmp_client_migrate_info(const char *protocol, const char *hostname, + bool has_port, int64_t port, + bool has_tls_port, int64_t tls_port, + bool has_cert_subject, const char *cert_subject, + Error **errp) { - const char *protocol = qdict_get_str(qdict, "protocol"); - const char *hostname = qdict_get_str(qdict, "hostname"); - const char *subject = qdict_get_try_str(qdict, "cert-subject"); - int port = qdict_get_try_int(qdict, "port", -1); - int tls_port = qdict_get_try_int(qdict, "tls-port", -1); - Error *err = NULL; - int ret; - if (strcmp(protocol, "spice") == 0) { - if (!qemu_using_spice(&err)) { - qerror_report_err(err); - error_free(err); - return -1; + if (!qemu_using_spice(errp)) { + return; } - if (port == -1 && tls_port == -1) { - qerror_report(QERR_MISSING_PARAMETER, "port/tls-port"); - return -1; + if (!has_port && !has_tls_port) { + error_set(errp, QERR_MISSING_PARAMETER, "port/tls-port"); + return; } - ret = qemu_spice_migrate_info(hostname, port, tls_port, subject); - if (ret != 0) { - qerror_report(QERR_UNDEFINED_ERROR); - return -1; + if (qemu_spice_migrate_info(hostname, + has_port ? port : -1, + has_tls_port ? tls_port : -1, + cert_subject)) { + error_set(errp, QERR_UNDEFINED_ERROR); + return; } - return 0; + return; } - qerror_report(QERR_INVALID_PARAMETER, "protocol"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "spice"); } static void hmp_logfile(Monitor *mon, const QDict *qdict) @@ -4098,19 +4025,7 @@ void monitor_set_error(Monitor *mon, QError *qerror) } } -static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret) -{ - if (ret && !monitor_has_error(mon)) { - /* - * If it returns failure, it must have passed on error. - * - * Action: Report an internal error to the client if in QMP. - */ - qerror_report(QERR_UNDEFINED_ERROR); - } -} - -static void handle_user_command(Monitor *mon, const char *cmdline) +static void handle_hmp_command(Monitor *mon, const char *cmdline) { QDict *qdict; const mon_cmd_t *cmd; @@ -4118,26 +4033,10 @@ static void handle_user_command(Monitor *mon, const char *cmdline) qdict = qdict_new(); cmd = monitor_parse_command(mon, cmdline, 0, mon->cmd_table, qdict); - if (!cmd) - goto out; - - if (handler_is_async(cmd)) { - user_async_cmd_handler(mon, cmd, qdict); - } else if (handler_is_qobject(cmd)) { - QObject *data = NULL; - - /* XXX: ignores the error code */ - cmd->mhandler.cmd_new(mon, qdict, &data); - assert(!monitor_has_error(mon)); - if (data) { - cmd->user_print(mon, data); - qobject_decref(data); - } - } else { + if (cmd) { cmd->mhandler.cmd(mon, qdict); } -out: QDECREF(qdict); } @@ -4803,19 +4702,21 @@ static int monitor_can_read(void *opaque) return (mon->suspend_cnt == 0) ? 1 : 0; } -static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd) +static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd, + Error **errp) { bool is_cap = cmd->mhandler.cmd_new == do_qmp_capabilities; - if (is_cap && qmp_cmd_mode(mon)) { - qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND, - "Capabilities negotiation is already complete, command " - "'%s' ignored", cmd->name); + + if (is_cap && mon->qmp.in_command_mode) { + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "Capabilities negotiation is already complete, command " + "'%s' ignored", cmd->name); return true; } - if (!is_cap && !qmp_cmd_mode(mon)) { - qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND, - "Expecting capabilities negotiation with " - "'qmp_capabilities' before command '%s'", cmd->name); + if (!is_cap && !mon->qmp.in_command_mode) { + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "Expecting capabilities negotiation with " + "'qmp_capabilities' before command '%s'", cmd->name); return true; } return false; @@ -4831,8 +4732,9 @@ static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd) * the QMP_ACCEPT_UNKNOWNS flag is set, then the * checking is skipped for it. */ -static int check_client_args_type(const QDict *client_args, - const QDict *cmd_args, int flags) +static void check_client_args_type(const QDict *client_args, + const QDict *cmd_args, int flags, + Error **errp) { const QDictEntry *ent; @@ -4849,8 +4751,8 @@ static int check_client_args_type(const QDict *client_args, continue; } /* client arg doesn't exist */ - qerror_report(QERR_INVALID_PARAMETER, client_arg_name); - return -1; + error_set(errp, QERR_INVALID_PARAMETER, client_arg_name); + return; } arg_type = qobject_to_qstring(obj); @@ -4862,9 +4764,9 @@ static int check_client_args_type(const QDict *client_args, case 'B': case 's': if (qobject_type(client_arg) != QTYPE_QSTRING) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "string"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "string"); + return; } break; case 'i': @@ -4872,25 +4774,25 @@ static int check_client_args_type(const QDict *client_args, case 'M': case 'o': if (qobject_type(client_arg) != QTYPE_QINT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "int"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "int"); + return; } break; case 'T': if (qobject_type(client_arg) != QTYPE_QINT && qobject_type(client_arg) != QTYPE_QFLOAT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "number"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "number"); + return; } break; case 'b': case '-': if (qobject_type(client_arg) != QTYPE_QBOOL) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "bool"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "bool"); + return; } break; case 'O': @@ -4909,16 +4811,15 @@ static int check_client_args_type(const QDict *client_args, abort(); } } - - return 0; } /* * - Check if the client has passed all mandatory args * - Set special flags for argument validation */ -static int check_mandatory_args(const QDict *cmd_args, - const QDict *client_args, int *flags) +static void check_mandatory_args(const QDict *cmd_args, + const QDict *client_args, int *flags, + Error **errp) { const QDictEntry *ent; @@ -4933,12 +4834,10 @@ static int check_mandatory_args(const QDict *cmd_args, } else if (qstring_get_str(type)[0] != '-' && qstring_get_str(type)[1] != '?' && !qdict_haskey(client_args, cmd_arg_name)) { - qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name); - return -1; + error_set(errp, QERR_MISSING_PARAMETER, cmd_arg_name); + return; } } - - return 0; } static QDict *qdict_from_args_type(const char *args_type) @@ -4994,24 +4893,26 @@ out: * 3. Each argument provided by the client must have the type expected * by the command */ -static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args) +static void qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args, + Error **errp) { - int flags, err; + Error *err = NULL; + int flags; QDict *cmd_args; cmd_args = qdict_from_args_type(cmd->args_type); flags = 0; - err = check_mandatory_args(cmd_args, client_args, &flags); + check_mandatory_args(cmd_args, client_args, &flags, &err); if (err) { goto out; } - err = check_client_args_type(client_args, cmd_args, flags); + check_client_args_type(client_args, cmd_args, flags, &err); out: + error_propagate(errp, err); QDECREF(cmd_args); - return err; } /* @@ -5024,14 +4925,14 @@ out: * 5. If the "id" key exists, it can be anything (ie. json-value) * 6. Any argument not listed above is considered invalid */ -static QDict *qmp_check_input_obj(QObject *input_obj) +static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp) { const QDictEntry *ent; int has_exec_key = 0; QDict *input_dict; if (qobject_type(input_obj) != QTYPE_QDICT) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object"); + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "object"); return NULL; } @@ -5043,81 +4944,68 @@ static QDict *qmp_check_input_obj(QObject *input_obj) if (!strcmp(arg_name, "execute")) { if (qobject_type(arg_obj) != QTYPE_QSTRING) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", - "string"); + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, + "execute", "string"); return NULL; } has_exec_key = 1; } else if (!strcmp(arg_name, "arguments")) { if (qobject_type(arg_obj) != QTYPE_QDICT) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments", - "object"); + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, + "arguments", "object"); return NULL; } - } else if (!strcmp(arg_name, "id")) { - /* FIXME: check duplicated IDs for async commands */ } else { - qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name); + error_set(errp, QERR_QMP_EXTRA_MEMBER, arg_name); return NULL; } } if (!has_exec_key) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute"); + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute"); return NULL; } return input_dict; } -static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd, - const QDict *params) -{ - int ret; - QObject *data = NULL; - - ret = cmd->mhandler.cmd_new(mon, params, &data); - handler_audit(mon, cmd, ret); - monitor_protocol_emitter(mon, data); - qobject_decref(data); -} - static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) { - int err; - QObject *obj; + Error *local_err = NULL; + QObject *obj, *data; QDict *input, *args; const mon_cmd_t *cmd; const char *cmd_name; Monitor *mon = cur_mon; args = input = NULL; + data = NULL; obj = json_parser_parse(tokens, NULL); if (!obj) { // FIXME: should be triggered in json_parser_parse() - qerror_report(QERR_JSON_PARSING); + error_set(&local_err, QERR_JSON_PARSING); goto err_out; } - input = qmp_check_input_obj(obj); + input = qmp_check_input_obj(obj, &local_err); if (!input) { qobject_decref(obj); goto err_out; } - mon->mc->id = qdict_get(input, "id"); - qobject_incref(mon->mc->id); + mon->qmp.id = qdict_get(input, "id"); + qobject_incref(mon->qmp.id); cmd_name = qdict_get_str(input, "execute"); trace_handle_qmp_command(mon, cmd_name); cmd = qmp_find_cmd(cmd_name); if (!cmd) { - qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND, - "The command %s has not been found", cmd_name); + error_set(&local_err, ERROR_CLASS_COMMAND_NOT_FOUND, + "The command %s has not been found", cmd_name); goto err_out; } - if (invalid_qmp_mode(mon, cmd)) { + if (invalid_qmp_mode(mon, cmd, &local_err)) { goto err_out; } @@ -5129,40 +5017,39 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) QINCREF(args); } - err = qmp_check_client_args(cmd, args); - if (err < 0) { + qmp_check_client_args(cmd, args, &local_err); + if (local_err) { goto err_out; } - if (handler_is_async(cmd)) { - err = qmp_async_cmd_handler(mon, cmd, args); - if (err) { - /* emit the error response */ - goto err_out; + if (cmd->mhandler.cmd_new(mon, args, &data)) { + /* Command failed... */ + if (!mon->error) { + /* ... without setting an error, so make one up */ + error_set(&local_err, QERR_UNDEFINED_ERROR); } - } else { - qmp_call_cmd(mon, cmd, args); } - - goto out; + if (mon->error) { + error_set(&local_err, mon->error->err_class, "%s", + mon->error->err_msg); + } err_out: - monitor_protocol_emitter(mon, NULL); -out: + monitor_protocol_emitter(mon, data, local_err); + qobject_decref(data); + QDECREF(mon->error); + mon->error = NULL; QDECREF(input); QDECREF(args); } -/** - * monitor_control_read(): Read and handle QMP input - */ -static void monitor_control_read(void *opaque, const uint8_t *buf, int size) +static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) { Monitor *old_mon = cur_mon; cur_mon = opaque; - json_message_parser_feed(&cur_mon->mc->parser, (const char *) buf, size); + json_message_parser_feed(&cur_mon->qmp.parser, (const char *) buf, size); cur_mon = old_mon; } @@ -5181,7 +5068,7 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size) if (size == 0 || buf[size - 1] != 0) monitor_printf(cur_mon, "corrupted command\n"); else - handle_user_command(cur_mon, (char *)buf); + handle_hmp_command(cur_mon, (char *)buf); } cur_mon = old_mon; @@ -5193,7 +5080,7 @@ static void monitor_command_cb(void *opaque, const char *cmdline, Monitor *mon = opaque; monitor_suspend(mon); - handle_user_command(mon, cmdline); + handle_hmp_command(mon, cmdline); monitor_resume(mon); } @@ -5221,25 +5108,22 @@ static QObject *get_qmp_greeting(void) return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver); } -/** - * monitor_control_event(): Print QMP gretting - */ -static void monitor_control_event(void *opaque, int event) +static void monitor_qmp_event(void *opaque, int event) { QObject *data; Monitor *mon = opaque; switch (event) { case CHR_EVENT_OPENED: - mon->mc->command_mode = 0; + mon->qmp.in_command_mode = false; data = get_qmp_greeting(); monitor_json_emitter(mon, data); qobject_decref(data); mon_refcount++; break; case CHR_EVENT_CLOSED: - json_message_parser_destroy(&mon->mc->parser); - json_message_parser_init(&mon->mc->parser, handle_qmp_command); + json_message_parser_destroy(&mon->qmp.parser); + json_message_parser_init(&mon->qmp.parser, handle_qmp_command); mon_refcount--; monitor_fdsets_cleanup(); break; @@ -5371,14 +5255,11 @@ void monitor_init(CharDriverState *chr, int flags) monitor_read_command(mon, 0); } - if (monitor_ctrl_mode(mon)) { - mon->mc = g_malloc0(sizeof(MonitorControl)); - /* Control mode requires special handlers */ - qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read, - monitor_control_event, mon); + if (monitor_is_qmp(mon)) { + qemu_chr_add_handlers(chr, monitor_can_read, monitor_qmp_read, + monitor_qmp_event, mon); qemu_chr_fe_set_echo(chr, true); - - json_message_parser_init(&mon->mc->parser, handle_qmp_command); + json_message_parser_init(&mon->qmp.parser, handle_qmp_command); } else { qemu_chr_add_handlers(chr, monitor_can_read, monitor_read, monitor_event, mon); diff --git a/qapi-schema.json b/qapi-schema.json index 0662a9b445..6e17a5c36c 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -638,6 +638,25 @@ 'returns': 'MigrationParameters' } ## +# @client_migrate_info +# +# Set migration information for remote display. This makes the server +# ask the client to automatically reconnect using the new parameters +# once migration finished successfully. Only implemented for SPICE. +# +# @protocol: must be "spice" +# @hostname: migration target hostname +# @port: #optional spice tcp port for plaintext channels +# @tls-port: #optional spice tcp port for tls-secured channels +# @cert-subject: #optional server certificate subject +# +# Since: 0.14.0 +## +{ 'command': 'client_migrate_info', + 'data': { 'protocol': 'str', 'hostname': 'str', '*port': 'int', + '*tls-port': 'int', '*cert-subject': 'str' } } + +## # @MouseInfo: # # Information about a mouse device. diff --git a/qmp-commands.hx b/qmp-commands.hx index 14e109eb5c..867a21fab6 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -784,23 +784,23 @@ EQMP .name = "client_migrate_info", .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", .params = "protocol hostname port tls-port cert-subject", - .help = "send migration info to spice/vnc client", - .mhandler.cmd_new = client_migrate_info, + .help = "set migration information for remote display", + .mhandler.cmd_new = qmp_marshal_input_client_migrate_info, }, SQMP client_migrate_info ------------------- +------------------- -Set the spice/vnc connection info for the migration target. The spice/vnc -server will ask the spice/vnc client to automatically reconnect using the -new parameters (if specified) once the vm migration finished successfully. +Set migration information for remote display. This makes the server +ask the client to automatically reconnect using the new parameters +once migration finished successfully. Only implemented for SPICE. Arguments: -- "protocol": protocol: "spice" or "vnc" (json-string) +- "protocol": must be "spice" (json-string) - "hostname": migration target hostname (json-string) -- "port": spice/vnc tcp port for plaintext channels (json-int, optional) +- "port": spice tcp port for plaintext channels (json-int, optional) - "tls-port": spice tcp port for tls-secured channels (json-int, optional) - "cert-subject": server certificate subject (json-string, optional) diff --git a/stubs/mon-is-qmp.c b/stubs/mon-is-qmp.c index 1f0a8fd98a..1ef136ab1d 100644 --- a/stubs/mon-is-qmp.c +++ b/stubs/mon-is-qmp.c @@ -1,7 +1,7 @@ #include "qemu-common.h" #include "monitor/monitor.h" -int monitor_cur_is_qmp(void) +bool monitor_cur_is_qmp(void) { - return 0; + return false; } |