diff options
217 files changed, 7438 insertions, 2383 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 0463696dd3..4ed82154ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -486,7 +486,8 @@ F: hw/ppc/prep.c F: hw/pci-host/prep.[hc] F: hw/isa/pc87312.[hc] -sPAPR +sPAPR (pseries) +M: David Gibson <david@gibson.dropbear.id.au> M: Alexander Graf <agraf@suse.de> L: qemu-ppc@nongnu.org S: Supported @@ -389,13 +389,8 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) endif endif -install-confdir: - $(INSTALL_DIR) "$(DESTDIR)$(qemu_confdir)" -install-sysconfig: install-datadir install-confdir - $(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(qemu_confdir)" - -install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig \ +install: all $(if $(BUILD_DOCS),install-doc) \ install-datadir install-localstatedir ifneq ($(TOOLS),) $(call install-prog,$(TOOLS),$(DESTDIR)$(bindir)) diff --git a/Makefile.objs b/Makefile.objs index 28999d39c4..4881d2c2a6 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -76,6 +76,8 @@ common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) +common-obj-$(CONFIG_FDT) += device_tree.o + ###################################################################### # qapi diff --git a/Makefile.target b/Makefile.target index be01dd39c1..ec5b92cb60 100644 --- a/Makefile.target +++ b/Makefile.target @@ -1,5 +1,7 @@ # -*- Mode: makefile -*- +BUILD_DIR?=$(CURDIR)/.. + include ../config-host.mak include config-target.mak include config-devices.mak @@ -129,7 +131,6 @@ ifdef CONFIG_SOFTMMU obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o obj-y += qtest.o bootdevice.o obj-y += hw/ -obj-$(CONFIG_FDT) += device_tree.o obj-$(CONFIG_KVM) += kvm-all.o obj-y += memory.o savevm.o cputlb.o obj-y += memory_mapping.o diff --git a/arch_init.c b/arch_init.c index 23d3feba44..d29447497b 100644 --- a/arch_init.c +++ b/arch_init.c @@ -136,7 +136,6 @@ static struct defconfig_file { bool userconfig; } default_config_files[] = { { CONFIG_QEMU_CONFDIR "/qemu.conf", true }, - { CONFIG_QEMU_CONFDIR "/target-" TARGET_NAME ".conf", true }, { NULL }, /* end of list */ }; @@ -610,52 +609,10 @@ ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, return (next - base) << TARGET_PAGE_BITS; } -static inline bool migration_bitmap_set_dirty(ram_addr_t addr) -{ - bool ret; - int nr = addr >> TARGET_PAGE_BITS; - - ret = test_and_set_bit(nr, migration_bitmap); - - if (!ret) { - migration_dirty_pages++; - } - return ret; -} - static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) { - ram_addr_t addr; - unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); - - /* start address is aligned at the start of a word? */ - if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { - int k; - int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); - unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]; - - for (k = page; k < page + nr; k++) { - if (src[k]) { - unsigned long new_dirty; - new_dirty = ~migration_bitmap[k]; - migration_bitmap[k] |= src[k]; - new_dirty &= src[k]; - migration_dirty_pages += ctpopl(new_dirty); - src[k] = 0; - } - } - } else { - for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { - cpu_physical_memory_reset_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION); - migration_bitmap_set_dirty(start + addr); - } - } - } + migration_dirty_pages += + cpu_physical_memory_sync_dirty_bitmap(migration_bitmap, start, length); } diff --git a/block/blkdebug.c b/block/blkdebug.c index 3c30edba73..1e92607ef3 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -216,10 +216,9 @@ static int get_event_by_name(const char *name, BlkDebugEvent *event) struct add_rule_data { BDRVBlkdebugState *s; int action; - Error **errp; }; -static int add_rule(QemuOpts *opts, void *opaque) +static int add_rule(void *opaque, QemuOpts *opts, Error **errp) { struct add_rule_data *d = opaque; BDRVBlkdebugState *s = d->s; @@ -230,10 +229,10 @@ static int add_rule(QemuOpts *opts, void *opaque) /* Find the right event for the rule */ event_name = qemu_opt_get(opts, "event"); if (!event_name) { - error_setg(d->errp, "Missing event name for rule"); + error_setg(errp, "Missing event name for rule"); return -1; } else if (get_event_by_name(event_name, &event) < 0) { - error_setg(d->errp, "Invalid event name \"%s\"", event_name); + error_setg(errp, "Invalid event name \"%s\"", event_name); return -1; } @@ -319,8 +318,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename, d.s = s; d.action = ACTION_INJECT_ERROR; - d.errp = &local_err; - qemu_opts_foreach(&inject_error_opts, add_rule, &d, 1); + qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err); if (local_err) { error_propagate(errp, local_err); ret = -EINVAL; @@ -328,7 +326,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename, } d.action = ACTION_SET_STATE; - qemu_opts_foreach(&set_state_opts, add_rule, &d, 1); + qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err); if (local_err) { error_propagate(errp, local_err); ret = -EINVAL; diff --git a/block/iscsi.c b/block/iscsi.c index 8fca1d32cb..14e97a6b48 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1323,13 +1323,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, const char *filename; int i, ret = 0; - if ((BDRV_SECTOR_SIZE % 512) != 0) { - error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. " - "BDRV_SECTOR_SIZE(%lld) is not a multiple " - "of 512", BDRV_SECTOR_SIZE); - return -EINVAL; - } - opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { 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/bsd-user/main.c b/bsd-user/main.c index 5bfaf5c421..ba0b9981f5 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -108,10 +108,6 @@ void cpu_list_unlock(void) /***********************************************************/ /* CPUX86 core interface */ -void cpu_smm_update(CPUX86State *env) -{ -} - uint64_t cpu_get_tsc(CPUX86State *env) { return cpu_get_real_ticks(); @@ -353,7 +353,7 @@ for opt do ;; --cpu=*) cpu="$optarg" ;; - --extra-cflags=*) QEMU_CFLAGS="$optarg $QEMU_CFLAGS" + --extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg" EXTRA_CFLAGS="$optarg" ;; --extra-ldflags=*) LDFLAGS="$optarg $LDFLAGS" @@ -3115,9 +3115,11 @@ fi if test "$fdt" != "no" ; then fdt_libs="-lfdt" # explicitly check for libfdt_env.h as it is missing in some stable installs + # and test for required functions to make sure we are on a version >= 1.4.0 cat > $TMPC << EOF +#include <libfdt.h> #include <libfdt_env.h> -int main(void) { return 0; } +int main(void) { fdt_get_property_by_offset(0, 0, 0); return 0; } EOF if compile_prog "" "$fdt_libs" ; then # system DTC is good - use it @@ -3135,7 +3137,7 @@ EOF fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs" elif test "$fdt" = "yes" ; then # have neither and want - prompt for system/submodule install - error_exit "DTC (libfdt) not present. Your options:" \ + error_exit "DTC (libfdt) version >= 1.4.0 not present. Your options:" \ " (1) Preferred: Install the DTC (libfdt) devel package" \ " (2) Fetch the DTC submodule, using:" \ " git submodule update --init dtc" @@ -105,6 +105,7 @@ static bool all_cpu_threads_idle(void) /* Protected by TimersState seqlock */ +static bool icount_sleep = true; static int64_t vm_clock_warp_start = -1; /* Conversion factor from emulated instructions to virtual clock ticks. */ static int icount_time_shift; @@ -393,15 +394,18 @@ void qemu_clock_warp(QEMUClockType type) return; } - /* - * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now. - * This ensures that the deadline for the timer is computed correctly below. - * This also makes sure that the insn counter is synchronized before the - * CPU starts running, in case the CPU is woken by an event other than - * the earliest QEMU_CLOCK_VIRTUAL timer. - */ - icount_warp_rt(NULL); - timer_del(icount_warp_timer); + if (icount_sleep) { + /* + * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now. + * This ensures that the deadline for the timer is computed correctly + * below. + * This also makes sure that the insn counter is synchronized before + * the CPU starts running, in case the CPU is woken by an event other + * than the earliest QEMU_CLOCK_VIRTUAL timer. + */ + icount_warp_rt(NULL); + timer_del(icount_warp_timer); + } if (!all_cpu_threads_idle()) { return; } @@ -415,6 +419,11 @@ void qemu_clock_warp(QEMUClockType type) clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); if (deadline < 0) { + static bool notified; + if (!icount_sleep && !notified) { + error_report("WARNING: icount sleep disabled and no active timers"); + notified = true; + } return; } @@ -425,23 +434,35 @@ void qemu_clock_warp(QEMUClockType type) * interrupt to wake it up, but the interrupt never comes because * the vCPU isn't running any insns and thus doesn't advance the * QEMU_CLOCK_VIRTUAL. - * - * An extreme solution for this problem would be to never let VCPUs - * sleep in icount mode if there is a pending QEMU_CLOCK_VIRTUAL - * timer; rather time could just advance to the next QEMU_CLOCK_VIRTUAL - * event. Instead, we do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL - * after some "real" time, (related to the time left until the next - * event) has passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this. - * This avoids that the warps are visible externally; for example, - * you will not be sending network packets continuously instead of - * every 100ms. */ - seqlock_write_lock(&timers_state.vm_clock_seqlock); - if (vm_clock_warp_start == -1 || vm_clock_warp_start > clock) { - vm_clock_warp_start = clock; + if (!icount_sleep) { + /* + * We never let VCPUs sleep in no sleep icount mode. + * If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance + * to the next QEMU_CLOCK_VIRTUAL event and notify it. + * It is useful when we want a deterministic execution time, + * isolated from host latencies. + */ + seqlock_write_lock(&timers_state.vm_clock_seqlock); + timers_state.qemu_icount_bias += deadline; + seqlock_write_unlock(&timers_state.vm_clock_seqlock); + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } else { + /* + * We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some + * "real" time, (related to the time left until the next event) has + * passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this. + * This avoids that the warps are visible externally; for example, + * you will not be sending network packets continuously instead of + * every 100ms. + */ + seqlock_write_lock(&timers_state.vm_clock_seqlock); + if (vm_clock_warp_start == -1 || vm_clock_warp_start > clock) { + vm_clock_warp_start = clock; + } + seqlock_write_unlock(&timers_state.vm_clock_seqlock); + timer_mod_anticipate(icount_warp_timer, clock + deadline); } - seqlock_write_unlock(&timers_state.vm_clock_seqlock); - timer_mod_anticipate(icount_warp_timer, clock + deadline); } else if (deadline == 0) { qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } @@ -504,9 +525,18 @@ void configure_icount(QemuOpts *opts, Error **errp) } return; } + + icount_sleep = qemu_opt_get_bool(opts, "sleep", true); + if (icount_sleep) { + icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + icount_warp_rt, NULL); + } + icount_align_option = qemu_opt_get_bool(opts, "align", false); - icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, - icount_warp_rt, NULL); + + if (icount_align_option && !icount_sleep) { + error_setg(errp, "align=on and sleep=no are incompatible"); + } if (strcmp(option, "auto") != 0) { errno = 0; icount_time_shift = strtol(option, &rem_str, 0); @@ -517,6 +547,8 @@ void configure_icount(QemuOpts *opts, Error **errp) return; } else if (icount_align_option) { error_setg(errp, "shift=auto and align=on are incompatible"); + } else if (!icount_sleep) { + error_setg(errp, "shift=auto and sleep=no are incompatible"); } use_icount = 2; @@ -125,14 +125,13 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) can be detected */ void tlb_protect_code(ram_addr_t ram_addr) { - cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE, - DIRTY_MEMORY_CODE); + cpu_physical_memory_test_and_clear_dirty(ram_addr, TARGET_PAGE_SIZE, + DIRTY_MEMORY_CODE); } /* update the TLB so that writes in physical page 'phys_addr' are no longer tested for self modifying code */ -void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr, - target_ulong vaddr) +void tlb_unprotect_code(ram_addr_t ram_addr) { cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); } diff --git a/device_tree.c b/device_tree.c index 3d119ef0bd..d2de580947 100644 --- a/device_tree.c +++ b/device_tree.c @@ -18,7 +18,6 @@ #include <unistd.h> #include <stdlib.h> -#include "config.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "sysemu/device_tree.h" diff --git a/docs/pci_expander_bridge.txt b/docs/pci_expander_bridge.txt new file mode 100644 index 0000000000..d7913fb4ae --- /dev/null +++ b/docs/pci_expander_bridge.txt @@ -0,0 +1,58 @@ +PCI EXPANDER BRIDGE (PXB) +========================= + +Description +=========== +PXB is a "light-weight" host bridge in the same PCI domain +as the main host bridge whose purpose is to enable +the main host bridge to support multiple PCI root buses. +It is implemented only for i440fx and can be placed only +on bus 0 (pci.0). + +As opposed to PCI-2-PCI bridge's secondary bus, PXB's bus +is a primary bus and can be associated with a NUMA node +(different from the main host bridge) allowing the guest OS +to recognize the proximity of a pass-through device to +other resources as RAM and CPUs. + +Usage +===== +A detailed command line would be: + +[qemu-bin + storage options] +-m 2G +-object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0 +-object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1 +-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd-device e1000,bus=bridge1,addr=0x4,netdev=nd +-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8,bus=pci.0 -device e1000,bus=bridge2,addr=0x3 +-device pxb,id=bridge3,bus=pci.0,bus_nr=40,bus=pci.0 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1 + +Here you have: + - 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes) + - a pxb host bridge attached to NUMA 1 with an e1000 behind it + - a pxb host bridge attached to NUMA 0 with an e1000 behind it + - a pxb host bridge not attached to any NUMA with a hard drive behind it. + +Limitations +=========== +Please observe that we specified the bus "pci.0" for the second and third pxb. +This is because when no bus is given, another pxb can be selected by QEMU as default bus, +however, PXBs can be placed only under the root bus. + +Implementation +============== +The PXB is composed by: +- HostBridge (TYPE_PXB_HOST) + The host bridge allows to register and query the PXB's rPCI root bus in QEMU. +- PXBDev(TYPE_PXB_DEVICE) + It is a regular PCI Device that resides on the piix host-bridge bus and its bus uses the same PCI domain. + However, the bus behind is exposed through ACPI as a primary PCI bus and starts a new PCI hierarchy. + The interrupts from devices behind the PXB are routed through this device the same as if it were a + PCI-2-PCI bridge. The _PRT follows the i440fx model. +- PCIBridgeDev(TYPE_PCI_BRIDGE_DEV) + Created automatically as part of init sequence. + When adding a device to PXB it is attached to the bridge for two reasons: + - Using the bridge will enable hotplug support + - All the devices behind the bridge will use bridge's IO/MEM windows compacting + the PCI address space. + diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt new file mode 100644 index 0000000000..d35771cc2b --- /dev/null +++ b/docs/specs/ppc-spapr-hotplug.txt @@ -0,0 +1,287 @@ += sPAPR Dynamic Reconfiguration = + +sPAPR/"pseries" guests make use of a facility called dynamic-reconfiguration +to handle hotplugging of dynamic "physical" resources like PCI cards, or +"logical"/paravirtual resources like memory, CPUs, and "physical" +host-bridges, which are generally managed by the host/hypervisor and provided +to guests as virtualized resources. The specifics of dynamic-reconfiguration +are documented extensively in PAPR+ v2.7, Section 13.1. This document +provides a summary of that information as it applies to the implementation +within QEMU. + +== Dynamic-reconfiguration Connectors == + +To manage hotplug/unplug of these resources, a firmware abstraction known as +a Dynamic Resource Connector (DRC) is used to assign a particular dynamic +resource to the guest, and provide an interface for the guest to manage +configuration/removal of the resource associated with it. + +== Device-tree description of DRCs == + +A set of 4 Open Firmware device tree array properties are used to describe +the name/index/power-domain/type of each DRC allocated to a guest at +boot-time. There may be multiple sets of these arrays, rooted at different +paths in the device tree depending on the type of resource the DRCs manage. + +In some cases, the DRCs themselves may be provided by a dynamic resource, +such as the DRCs managing PCI slots on a hotplugged PHB. In this case the +arrays would be fetched as part of the device tree retrieval interfaces +for hotplugged resources described under "Guest->Host interface". + +The array properties are described below. Each entry/element in an array +describes the DRC identified by the element in the corresponding position +of ibm,drc-indexes: + +ibm,drc-names: + first 4-bytes: BE-encoded integer denoting the number of entries + each entry: a NULL-terminated <name> string encoded as a byte array + + <name> values for logical/virtual resources are defined in PAPR+ v2.7, + Section 13.5.2.4, and basically consist of the type of the resource + followed by a space and a numerical value that's unique across resources + of that type. + + <name> values for "physical" resources such as PCI or VIO devices are + defined as being "location codes", which are the "location labels" of + each encapsulating device, starting from the chassis down to the + individual slot for the device, concatenated by a hyphen. This provides + a mapping of resources to a physical location in a chassis for debugging + purposes. For QEMU, this mapping is less important, so we assign a + location code that conforms to naming specifications, but is simply a + location label for the slot by itself to simplify the implementation. + The naming convention for location labels is documented in detail in + PAPR+ v2.7, Section 12.3.1.5, and in our case amounts to using "C<n>" + for PCI/VIO device slots, where <n> is unique across all PCI/VIO + device slots. + +ibm,drc-indexes: + first 4-bytes: BE-encoded integer denoting the number of entries + each 4-byte entry: BE-encoded <index> integer that is unique across all DRCs + in the machine + + <index> is arbitrary, but in the case of QEMU we try to maintain the + convention used to assign them to pSeries guests on pHyp: + + bit[31:28]: integer encoding of <type>, where <type> is: + 1 for CPU resource + 2 for PHB resource + 3 for VIO resource + 4 for PCI resource + 8 for Memory resource + bit[27:0]: integer encoding of <id>, where <id> is unique across + all resources of specified type + +ibm,drc-power-domains: + first 4-bytes: BE-encoded integer denoting the number of entries + each 4-byte entry: 32-bit, BE-encoded <index> integer that specifies the + power domain the resource will be assigned to. In the case of QEMU + we associated all resources with a "live insertion" domain, where the + power is assumed to be managed automatically. The integer value for + this domain is a special value of -1. + + +ibm,drc-types: + first 4-bytes: BE-encoded integer denoting the number of entries + each entry: a NULL-terminated <type> string encoded as a byte array + + <type> is assigned as follows: + "CPU" for a CPU + "PHB" for a physical host-bridge + "SLOT" for a VIO slot + "28" for a PCI slot + "MEM" for memory resource + +== Guest->Host interface to manage dynamic resources == + +Each DRC is given a globally unique DRC Index, and resources associated with +a particular DRC are configured/managed by the guest via a number of RTAS +calls which reference individual DRCs based on the DRC index. This can be +considered the guest->host interface. + +rtas-set-power-level: + arg[0]: integer identifying power domain + arg[1]: new power level for the domain, 0-100 + output[0]: status, 0 on success + output[1]: power level after command + + Set the power level for a specified power domain + +rtas-get-power-level: + arg[0]: integer identifying power domain + output[0]: status, 0 on success + output[1]: current power level + + Get the power level for a specified power domain + +rtas-set-indicator: + arg[0]: integer identifying sensor/indicator type + arg[1]: index of sensor, for DR-related sensors this is generally the + DRC index + arg[2]: desired sensor value + output[0]: status, 0 on success + + Set the state of an indicator or sensor. For the purpose of this document we + focus on the indicator/sensor types associated with a DRC. The types are: + + 9001: isolation-state, controls/indicates whether a device has been made + accessible to a guest + + supported sensor values: + 0: isolate, device is made unaccessible by guest OS + 1: unisolate, device is made available to guest OS + + 9002: dr-indicator, controls "visual" indicator associated with device + + supported sensor values: + 0: inactive, resource may be safely removed + 1: active, resource is in use and cannot be safely removed + 2: identify, used to visually identify slot for interactive hotplug + 3: action, in most cases, used in the same manner as identify + + 9003: allocation-state, generally only used for "logical" DR resources to + request the allocation/deallocation of a resource prior to acquiring + it via isolation-state->unisolate, or after releasing it via + isolation-state->isolate, respectively. for "physical" DR (like PCI + hotplug/unplug) the pre-allocation of the resource is implied and + this sensor is unused. + + supported sensor values: + 0: unusable, tell firmware/system the resource can be + unallocated/reclaimed and added back to the system resource pool + 1: usable, request the resource be allocated/reserved for use by + guest OS + 2: exchange, used to allocate a spare resource to use for fail-over + in certain situations. unused in QEMU + 3: recover, used to reclaim a previously allocated resource that's + not currently allocated to the guest OS. unused in QEMU + +rtas-get-sensor-state: + arg[0]: integer identifying sensor/indicator type + arg[1]: index of sensor, for DR-related sensors this is generally the + DRC index + output[0]: status, 0 on success + + Used to read an indicator or sensor value. + + For DR-related operations, the only noteworthy sensor is dr-entity-sense, + which has a type value of 9003, as allocation-state does in the case of + rtas-set-indicator. The semantics/encodings of the sensor values are distinct + however: + + supported sensor values for dr-entity-sense (9003) sensor: + 0: empty, + for physical resources: DRC/slot is empty + for logical resources: unused + 1: present, + for physical resources: DRC/slot is populated with a device/resource + for logical resources: resource has been allocated to the DRC + 2: unusable, + for physical resources: unused + for logical resources: DRC has no resource allocated to it + 3: exchange, + for physical resources: unused + for logical resources: resource available for exchange (see + allocation-state sensor semantics above) + 4: recovery, + for physical resources: unused + for logical resources: resource available for recovery (see + allocation-state sensor semantics above) + +rtas-ibm-configure-connector: + arg[0]: guest physical address of 4096-byte work area buffer + arg[1]: 0, or address of additional 4096-byte work area buffer. only non-zero + if a prior RTAS response indicated a need for additional memory + output[0]: status: + 0: completed transmittal of device-tree node + 1: instruct guest to prepare for next DT sibling node + 2: instruct guest to prepare for next DT child node + 3: instruct guest to prepare for next DT property + 4: instruct guest to ascend to parent DT node + 5: instruct guest to provide additional work-area buffer + via arg[1] + 990x: instruct guest that operation took too long and to try + again later + + Used to fetch an OF device-tree description of the resource associated with + a particular DRC. The DRC index is encoded in the first 4-bytes of the first + work area buffer. + + Work area layout, using 4-byte offsets: + wa[0]: DRC index of the DRC to fetch device-tree nodes from + wa[1]: 0 (hard-coded) + wa[2]: for next-sibling/next-child response: + wa offset of null-terminated string denoting the new node's name + for next-property response: + wa offset of null-terminated string denoting new property's name + wa[3]: for next-property response (unused otherwise): + byte-length of new property's value + wa[4]: for next-property response (unused otherwise): + new property's value, encoded as an OFDT-compatible byte array + +== hotplug/unplug events == + +For most DR operations, the hypervisor will issue host->guest add/remove events +using the EPOW/check-exception notification framework, where the host issues a +check-exception interrupt, then provides an RTAS event log via an +rtas-check-exception call issued by the guest in response. This framework is +documented by PAPR+ v2.7, and already use in by QEMU for generating powerdown +requests via EPOW events. + +For DR, this framework has been extended to include hotplug events, which were +previously unneeded due to direct manipulation of DR-related guest userspace +tools by host-level management such as an HMC. This level of management is not +applicable to PowerKVM, hence the reason for extending the notification +framework to support hotplug events. + +Note that these events are not yet formally part of the PAPR+ specification, +but support for this format has already been implemented in DR-related +guest tools such as powerpc-utils/librtas, as well as kernel patches that have +been submitted to handle in-kernel processing of memory/cpu-related hotplug +events[1], and is planned for formal inclusion is PAPR+ specification. The +hotplug-specific payload is QEMU implemented as follows (with all values +encoded in big-endian format): + +struct rtas_event_log_v6_hp { +#define SECTION_ID_HOTPLUG 0x4850 /* HP */ + struct section_header { + uint16_t section_id; /* set to SECTION_ID_HOTPLUG */ + uint16_t section_length; /* sizeof(rtas_event_log_v6_hp), + * plus the length of the DRC name + * if a DRC name identifier is + * specified for hotplug_identifier + */ + uint8_t section_version; /* version 1 */ + uint8_t section_subtype; /* unused */ + uint16_t creator_component_id; /* unused */ + } hdr; +#define RTAS_LOG_V6_HP_TYPE_CPU 1 +#define RTAS_LOG_V6_HP_TYPE_MEMORY 2 +#define RTAS_LOG_V6_HP_TYPE_SLOT 3 +#define RTAS_LOG_V6_HP_TYPE_PHB 4 +#define RTAS_LOG_V6_HP_TYPE_PCI 5 + uint8_t hotplug_type; /* type of resource/device */ +#define RTAS_LOG_V6_HP_ACTION_ADD 1 +#define RTAS_LOG_V6_HP_ACTION_REMOVE 2 + uint8_t hotplug_action; /* action (add/remove) */ +#define RTAS_LOG_V6_HP_ID_DRC_NAME 1 +#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 +#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 + uint8_t hotplug_identifier; /* type of the resource identifier, + * which serves as the discriminator + * for the 'drc' union field below + */ + uint8_t reserved; + union { + uint32_t index; /* DRC index of resource to take action + * on + */ + uint32_t count; /* number of DR resources to take + * action on (guest chooses which) + */ + char name[1]; /* string representing the name of the + * DRC to take action on + */ + } drc; +} QEMU_PACKED; + +[1] http://thread.gmane.org/gmane.linux.ports.ppc.embedded/75350/focus=106867 diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt index f3df2066a4..ab1fdd36b4 100644 --- a/docs/writing-qmp-commands.txt +++ b/docs/writing-qmp-commands.txt @@ -598,7 +598,7 @@ stored in its "value" member. In our example, the "value" member is a pointer to an TimerAlarmMethod instance. Notice that the "current" variable is used as "true" only in the first -interation of the loop. That's because the alarm timer method in use is the +iteration of the loop. That's because the alarm timer method in use is the first element of the alarm_timers array. Also notice that QAPI lists are handled by hand and we return the head of the list. diff --git a/dtc b/dtc -Subproject bc895d6d09695d05ceb8b52486ffe861d6cfbdd +Subproject 65cc4d2748a2c2e6f27f1cf39e07a5dbabd80eb @@ -59,8 +59,6 @@ //#define DEBUG_SUBPAGE #if !defined(CONFIG_USER_ONLY) -static bool in_migration; - /* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes * are protected by the ramlist lock. */ @@ -173,17 +171,22 @@ static void phys_map_node_reserve(PhysPageMap *map, unsigned nodes) } } -static uint32_t phys_map_node_alloc(PhysPageMap *map) +static uint32_t phys_map_node_alloc(PhysPageMap *map, bool leaf) { unsigned i; uint32_t ret; + PhysPageEntry e; + PhysPageEntry *p; ret = map->nodes_nb++; + p = map->nodes[ret]; assert(ret != PHYS_MAP_NODE_NIL); assert(ret != map->nodes_nb_alloc); + + e.skip = leaf ? 0 : 1; + e.ptr = leaf ? PHYS_SECTION_UNASSIGNED : PHYS_MAP_NODE_NIL; for (i = 0; i < P_L2_SIZE; ++i) { - map->nodes[ret][i].skip = 1; - map->nodes[ret][i].ptr = PHYS_MAP_NODE_NIL; + memcpy(&p[i], &e, sizeof(e)); } return ret; } @@ -193,21 +196,12 @@ static void phys_page_set_level(PhysPageMap *map, PhysPageEntry *lp, int level) { PhysPageEntry *p; - int i; hwaddr step = (hwaddr)1 << (level * P_L2_BITS); if (lp->skip && lp->ptr == PHYS_MAP_NODE_NIL) { - lp->ptr = phys_map_node_alloc(map); - p = map->nodes[lp->ptr]; - if (level == 0) { - for (i = 0; i < P_L2_SIZE; i++) { - p[i].skip = 0; - p[i].ptr = PHYS_SECTION_UNASSIGNED; - } - } - } else { - p = map->nodes[lp->ptr]; + lp->ptr = phys_map_node_alloc(map, level == 0); } + p = map->nodes[lp->ptr]; lp = &p[(*index >> (level * P_L2_BITS)) & (P_L2_SIZE - 1)]; while (*nb && lp < &p[P_L2_SIZE]) { @@ -858,21 +852,27 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) } /* Note: start and end must be within the same ram block. */ -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, - unsigned client) +bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client) { - if (length == 0) - return; - cpu_physical_memory_clear_dirty_range_type(start, length, client); + unsigned long end, page; + bool dirty; - if (tcg_enabled()) { + if (length == 0) { + return false; + } + + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + dirty = bitmap_test_and_clear_atomic(ram_list.dirty_memory[client], + page, end - page); + + if (dirty && tcg_enabled()) { tlb_reset_dirty_range_all(start, length); } -} -static void cpu_physical_memory_set_dirty_tracking(bool enable) -{ - in_migration = enable; + return dirty; } /* Called from RCU critical section */ @@ -1362,7 +1362,8 @@ int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp) cpu_physical_memory_clear_dirty_range(block->offset, block->used_length); block->used_length = newsize; - cpu_physical_memory_set_dirty_range(block->offset, block->used_length); + cpu_physical_memory_set_dirty_range(block->offset, block->used_length, + DIRTY_CLIENTS_ALL); memory_region_set_size(block->mr, newsize); if (block->resized) { block->resized(block->idstr, newsize, block->host); @@ -1436,7 +1437,8 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) } } cpu_physical_memory_set_dirty_range(new_block->offset, - new_block->used_length); + new_block->used_length, + DIRTY_CLIENTS_ALL); if (new_block->host) { qemu_ram_setup_dump(new_block->host, new_block->max_length); @@ -1824,7 +1826,11 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, default: abort(); } - cpu_physical_memory_set_dirty_range_nocode(ram_addr, size); + /* Set both VGA and migration bits for simplicity and to remove + * the notdirty callback faster. + */ + cpu_physical_memory_set_dirty_range(ram_addr, size, + DIRTY_CLIENTS_NOCODE); /* we remove the notdirty callback only if the code has been flushed */ if (!cpu_physical_memory_is_clean(ram_addr)) { @@ -2165,22 +2171,6 @@ static void tcg_commit(MemoryListener *listener) } } -static void core_log_global_start(MemoryListener *listener) -{ - cpu_physical_memory_set_dirty_tracking(true); -} - -static void core_log_global_stop(MemoryListener *listener) -{ - cpu_physical_memory_set_dirty_tracking(false); -} - -static MemoryListener core_memory_listener = { - .log_global_start = core_log_global_start, - .log_global_stop = core_log_global_stop, - .priority = 1, -}; - void address_space_init_dispatch(AddressSpace *as) { as->dispatch = NULL; @@ -2220,8 +2210,6 @@ static void memory_map_init(void) memory_region_init_io(system_io, NULL, &unassigned_io_ops, NULL, "io", 65536); address_space_init(&address_space_io, system_io, "I/O"); - - memory_listener_register(&core_memory_listener, &address_space_memory); } MemoryRegion *get_system_memory(void) @@ -2279,14 +2267,23 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, #else -static void invalidate_and_set_dirty(hwaddr addr, +static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr length) { - if (cpu_physical_memory_range_includes_clean(addr, length)) { - tb_invalidate_phys_range(addr, addr + length, 0); - cpu_physical_memory_set_dirty_range_nocode(addr, length); + uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr); + /* No early return if dirty_log_mask is or becomes 0, because + * cpu_physical_memory_set_dirty_range will still call + * xen_modified_memory. + */ + if (dirty_log_mask) { + dirty_log_mask = + cpu_physical_memory_range_includes_clean(addr, length, dirty_log_mask); } - xen_modified_memory(addr, length); + if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { + tb_invalidate_phys_range(addr, addr + length); + dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); + } + cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); } static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) @@ -2371,7 +2368,7 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, /* RAM case */ ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); - invalidate_and_set_dirty(addr1, l); + invalidate_and_set_dirty(mr, addr1, l); } } else { if (!memory_access_is_direct(mr, is_write)) { @@ -2468,7 +2465,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, switch (type) { case WRITE_DATA: memcpy(ptr, buf, l); - invalidate_and_set_dirty(addr1, l); + invalidate_and_set_dirty(mr, addr1, l); break; case FLUSH_CACHE: flush_icache_range((uintptr_t)ptr, (uintptr_t)ptr + l); @@ -2693,7 +2690,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, mr = qemu_ram_addr_from_host(buffer, &addr1); assert(mr != NULL); if (is_write) { - invalidate_and_set_dirty(addr1, access_len); + invalidate_and_set_dirty(mr, addr1, access_len); } if (xen_enabled()) { xen_invalidate_map_cache_entry(buffer); @@ -3022,6 +3019,7 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val, hwaddr l = 4; hwaddr addr1; MemTxResult r; + uint8_t dirty_log_mask; rcu_read_lock(); mr = address_space_translate(as, addr, &addr1, &l, @@ -3033,14 +3031,9 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val, ptr = qemu_get_ram_ptr(addr1); stl_p(ptr, val); - if (unlikely(in_migration)) { - if (cpu_physical_memory_is_clean(addr1)) { - /* invalidate code */ - tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); - /* set dirty bit */ - cpu_physical_memory_set_dirty_range_nocode(addr1, 4); - } - } + dirty_log_mask = memory_region_get_dirty_log_mask(mr); + dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); + cpu_physical_memory_set_dirty_range(addr1, 4, dirty_log_mask); r = MEMTX_OK; } if (result) { @@ -3096,7 +3089,7 @@ static inline void address_space_stl_internal(AddressSpace *as, stl_p(ptr, val); break; } - invalidate_and_set_dirty(addr1, 4); + invalidate_and_set_dirty(mr, addr1, 4); r = MEMTX_OK; } if (result) { @@ -3200,7 +3193,7 @@ static inline void address_space_stw_internal(AddressSpace *as, stw_p(ptr, val); break; } - invalidate_and_set_dirty(addr1, 2); + invalidate_and_set_dirty(mr, addr1, 2); r = MEMTX_OK; } if (result) { diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index fa1214a4b8..6dd41d8978 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -113,7 +113,7 @@ const float16 float16_default_nan = const_float16(0xFE00); #if defined(TARGET_SPARC) const float32 float32_default_nan = const_float32(0x7FFFFFFF); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) + defined(TARGET_XTENSA) || defined(TARGET_S390X) const float32 float32_default_nan = const_float32(0x7FC00000); #elif SNAN_BIT_IS_ONE const float32 float32_default_nan = const_float32(0x7FBFFFFF); @@ -126,7 +126,8 @@ const float32 float32_default_nan = const_float32(0xFFC00000); *----------------------------------------------------------------------------*/ #if defined(TARGET_SPARC) const float64 float64_default_nan = const_float64(LIT64( 0x7FFFFFFFFFFFFFFF )); -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ + defined(TARGET_S390X) const float64 float64_default_nan = const_float64(LIT64( 0x7FF8000000000000 )); #elif SNAN_BIT_IS_ONE const float64 float64_default_nan = const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); @@ -155,6 +156,9 @@ const floatx80 floatx80_default_nan #if SNAN_BIT_IS_ONE #define float128_default_nan_high LIT64(0x7FFF7FFFFFFFFFFF) #define float128_default_nan_low LIT64(0xFFFFFFFFFFFFFFFF) +#elif defined(TARGET_S390X) +#define float128_default_nan_high LIT64( 0x7FFF800000000000 ) +#define float128_default_nan_low LIT64( 0x0000000000000000 ) #else #define float128_default_nan_high LIT64( 0xFFFF800000000000 ) #define float128_default_nan_low LIT64( 0x0000000000000000 ) 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 @@ -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"); @@ -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/acpi/aml-build.c b/hw/acpi/aml-build.c index 2bebf23db2..0d4b3247b7 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -687,6 +687,14 @@ Aml *aml_else(void) return var; } +/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefWhile */ +Aml *aml_while(Aml *predicate) +{ + Aml *var = aml_bundle(0xA2 /* WhileOp */, AML_PACKAGE); + aml_append(var, predicate); + return var; +} + /* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */ Aml *aml_method(const char *name, int arg_count) { diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 51913d6932..0f201d8c6d 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -22,6 +22,7 @@ #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/acpi/acpi.h" +#include "hw/nvram/fw_cfg.h" #include "qemu/config-file.h" #include "qapi/opts-visitor.h" #include "qapi/dealloc-visitor.h" @@ -592,14 +593,26 @@ static const MemoryRegionOps acpi_pm_cnt_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val) +void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, + bool disable_s3, bool disable_s4, uint8_t s4_val) { + FWCfgState *fw_cfg; + ar->pm1.cnt.s4_val = s4_val; ar->wakeup.notify = acpi_notify_wakeup; qemu_register_wakeup_notifier(&ar->wakeup); memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent), &acpi_pm_cnt_ops, ar, "acpi-cnt", 2); memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io); + + fw_cfg = fw_cfg_find(); + if (fw_cfg) { + uint8_t suspend[6] = {128, 0, 0, 129, 128, 128}; + suspend[3] = 1 | ((!disable_s3) << 7); + suspend[4] = s4_val | ((!disable_s4) << 7); + + fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6); + } } void acpi_pm1_cnt_reset(ACPIREGS *ar) @@ -666,6 +679,13 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr) return val; } +void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq, + AcpiGPEStatusBits status) +{ + ar->gpe.sts[0] |= status; + acpi_update_sci(ar, irq); +} + void acpi_update_sci(ACPIREGS *regs, qemu_irq irq) { int sci_level, pm1a_sts; diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index b8ebfadc30..f5b9972f2d 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -59,8 +59,7 @@ void acpi_cpu_plug_cb(ACPIREGS *ar, qemu_irq irq, return; } - ar->gpe.sts[0] |= ACPI_CPU_HOTPLUG_STATUS; - acpi_update_sci(ar, irq); + acpi_send_gpe_event(ar, irq, ACPI_CPU_HOTPLUG_STATUS); } void acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 84e5bb8d39..25bc023882 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -94,7 +94,8 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val, ICH9LPCPMRegs *pm = opaque; switch (addr) { case 0: - pm->smi_en = val; + pm->smi_en &= ~pm->smi_en_wmask; + pm->smi_en |= (val & pm->smi_en_wmask); break; } } @@ -198,6 +199,7 @@ static void pm_reset(void *opaque) * support SMM mode. */ pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN; } + pm->smi_en_wmask = ~0; acpi_update_sci(&pm->acpi_regs, pm->irq); } @@ -219,7 +221,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); - acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->s4_val); + acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->disable_s3, pm->disable_s4, + pm->s4_val); acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm, diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 34cef1e5c3..2ff0d5ce1b 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -241,8 +241,7 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, mdev->is_inserting = true; /* do ACPI magic */ - ar->gpe.sts[0] |= ACPI_MEMORY_HOTPLUG_STATUS; - acpi_update_sci(ar, irq); + acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); return; } @@ -260,8 +259,7 @@ void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq, mdev->is_removing = true; /* Do ACPI magic */ - ar->gpe.sts[0] |= ACPI_MEMORY_HOTPLUG_STATUS; - acpi_update_sci(ar, irq); + acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); } void acpi_memory_unplug_cb(MemHotplugState *mem_st, diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 1e11af906d..fbbc4dde4f 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -45,7 +45,6 @@ # define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0) #endif -#define ACPI_PCI_HOTPLUG_STATUS 2 #define ACPI_PCIHP_ADDR 0xae00 #define ACPI_PCIHP_SIZE 0x0014 #define ACPI_PCIHP_LEGACY_SIZE 0x000f @@ -202,8 +201,7 @@ void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, s->acpi_pcihp_pci_status[bsel].up |= (1U << slot); - ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS; - acpi_update_sci(ar, irq); + acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS); } void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, @@ -220,8 +218,7 @@ void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); - ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS; - acpi_update_sci(ar, irq); + acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS); } static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 1b28481bbd..b730ca6ced 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -475,7 +475,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp) acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io); acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_cnt_init(&s->ar, &s->io, s->s4_val); + acpi_pm1_cnt_init(&s->ar, &s->io, s->disable_s3, s->disable_s4, s->s4_val); acpi_gpe_init(&s->ar, GPE_LEN); s->powerdown_notifier.notify = piix4_pm_powerdown_req; @@ -503,8 +503,7 @@ Object *piix4_pm_find(void) I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, qemu_irq sci_irq, qemu_irq smi_irq, - int kvm_enabled, FWCfgState *fw_cfg, - DeviceState **piix4_pm) + int kvm_enabled, DeviceState **piix4_pm) { DeviceState *dev; PIIX4PMState *s; @@ -525,14 +524,6 @@ I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, qdev_init_nofail(dev); - if (fw_cfg) { - uint8_t suspend[6] = {128, 0, 0, 129, 128, 128}; - suspend[3] = 1 | ((!s->disable_s3) << 7); - suspend[4] = s->s4_val | ((!s->disable_s4) << 7); - - fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6); - } - return s->smb.smbus; } diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 9fe7e8b5cb..f86e7bb830 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -55,7 +55,7 @@ static void clipper_init(MachineState *machine) ISABus *isa_bus; qemu_irq rtc_irq; long size, i; - const char *palcode_filename; + char *palcode_filename; uint64_t palcode_entry, palcode_low, palcode_high; uint64_t kernel_entry, kernel_low, kernel_high; @@ -101,8 +101,8 @@ static void clipper_init(MachineState *machine) /* Load PALcode. Given that this is not "real" cpu palcode, but one explicitly written for the emulation, we might as well load it directly from and ELF image. */ - palcode_filename = (bios_name ? bios_name : "palcode-clipper"); - palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename); + palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + bios_name ? bios_name : "palcode-clipper"); if (palcode_filename == NULL) { hw_error("no palcode provided\n"); exit(1); @@ -114,6 +114,7 @@ static void clipper_init(MachineState *machine) hw_error("could not load palcode '%s'\n", palcode_filename); exit(1); } + g_free(palcode_filename); /* Start all cpus at the PALcode RESET entry point. */ for (i = 0; i < smp_cpus; ++i) { diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 7df842dff7..421162e1d4 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -841,7 +841,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, } } - *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1); + *p_rtc_irq = qemu_allocate_irq(typhoon_set_timer_irq, s, 0); /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, but the address space hole reserved at this point is 8TB. */ @@ -918,11 +918,11 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Init the ISA bus. */ /* ??? Technically there should be a cy82c693ub pci-isa bridge. */ { - qemu_irq isa_pci_irq, *isa_irqs; + qemu_irq *isa_irqs; *isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io); - isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1); - isa_irqs = i8259_init(*isa_bus, isa_pci_irq); + isa_irqs = i8259_init(*isa_bus, + qemu_allocate_irq(typhoon_set_isa_irq, s, 0)); isa_bus_irqs(*isa_bus, isa_irqs); } diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 4b09caf594..cf346c1d0a 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -5,6 +5,7 @@ obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-$(CONFIG_ACPI) += virt-acpi-build.o obj-y += netduino2.o +obj-y += sysbus-fdt.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o diff --git a/hw/arm/boot.c b/hw/arm/boot.c index fa6950352c..d036624948 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -557,7 +557,7 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, fw_cfg_add_bytes(fw_cfg, data_key, data, size); } -void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) +static void arm_load_kernel_notify(Notifier *notifier, void *data) { CPUState *cs; int kernel_size; @@ -568,6 +568,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) hwaddr entry, kernel_load_offset; int big_endian; static const ARMInsnFixup *primary_loader; + ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, + notifier, notifier); + ARMCPU *cpu = n->cpu; + struct arm_boot_info *info = + container_of(n, struct arm_boot_info, load_kernel_notifier); /* CPU objects (unlike devices) are not automatically reset on system * reset, so we must always register a handler to do so. If we're @@ -775,3 +780,10 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) ARM_CPU(cs)->env.boot_info = info; } } + +void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) +{ + info->load_kernel_notifier.cpu = cpu; + info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify; + qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier); +} diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index d243159664..a659e8525d 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -133,9 +133,8 @@ static void n800_mmc_cs_cb(void *opaque, int line, int level) static void n8x0_gpio_setup(struct n800_s *s) { - qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->mpu->mmc, 1); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO, mmc_cs[0]); - + qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO, + qemu_allocate_irq(n800_mmc_cs_cb, s->mpu->mmc, 0)); qemu_irq_lower(qdev_get_gpio_in(s->mpu->gpio, N800_BAT_COVER_GPIO)); } diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 671e02c4ed..4b0f7f9c42 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -103,7 +103,6 @@ static void sx1_init(MachineState *machine, const int version) struct omap_mpu_state_s *mpu; MemoryRegion *address_space = get_system_memory(); MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *flash_1 = g_new(MemoryRegion, 1); MemoryRegion *cs = g_new(MemoryRegion, 4); static uint32_t cs0val = 0x00213090; static uint32_t cs1val = 0x00215070; @@ -165,6 +164,7 @@ static void sx1_init(MachineState *machine, const int version) if ((version == 1) && (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { + MemoryRegion *flash_1 = g_new(MemoryRegion, 1); memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", flash1_size, &error_abort); vmstate_register_ram_global(flash_1); diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c new file mode 100644 index 0000000000..3038b94b4a --- /dev/null +++ b/hw/arm/sysbus-fdt.c @@ -0,0 +1,174 @@ +/* + * ARM Platform Bus device tree generation helpers + * + * Copyright (c) 2014 Linaro Limited + * + * Authors: + * Alex Graf <agraf@suse.de> + * Eric Auger <eric.auger@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "hw/arm/sysbus-fdt.h" +#include "qemu/error-report.h" +#include "sysemu/device_tree.h" +#include "hw/platform-bus.h" +#include "sysemu/sysemu.h" + +/* + * internal struct that contains the information to create dynamic + * sysbus device node + */ +typedef struct PlatformBusFDTData { + void *fdt; /* device tree handle */ + int irq_start; /* index of the first IRQ usable by platform bus devices */ + const char *pbus_node_name; /* name of the platform bus node */ + PlatformBusDevice *pbus; +} PlatformBusFDTData; + +/* + * struct used when calling the machine init done notifier + * that constructs the fdt nodes of platform bus devices + */ +typedef struct PlatformBusFDTNotifierParams { + Notifier notifier; + ARMPlatformBusFDTParams *fdt_params; +} PlatformBusFDTNotifierParams; + +/* struct that associates a device type name and a node creation function */ +typedef struct NodeCreationPair { + const char *typename; + int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque); +} NodeCreationPair; + +/* list of supported dynamic sysbus devices */ +static const NodeCreationPair add_fdt_node_functions[] = { + {"", NULL}, /* last element */ +}; + +/** + * add_fdt_node - add the device tree node of a dynamic sysbus device + * + * @sbdev: handle to the sysbus device + * @opaque: handle to the PlatformBusFDTData + * + * Checks the sysbus type belongs to the list of device types that + * are dynamically instantiable and if so call the node creation + * function. + */ +static int add_fdt_node(SysBusDevice *sbdev, void *opaque) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(add_fdt_node_functions); i++) { + if (!strcmp(object_get_typename(OBJECT(sbdev)), + add_fdt_node_functions[i].typename)) { + ret = add_fdt_node_functions[i].add_fdt_node_fn(sbdev, opaque); + assert(!ret); + return 0; + } + } + error_report("Device %s can not be dynamically instantiated", + qdev_fw_name(DEVICE(sbdev))); + exit(1); +} + +/** + * add_all_platform_bus_fdt_nodes - create all the platform bus nodes + * + * builds the parent platform bus node and all the nodes of dynamic + * sysbus devices attached to it. + */ +static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) +{ + const char platcomp[] = "qemu,platform\0simple-bus"; + PlatformBusDevice *pbus; + DeviceState *dev; + gchar *node; + uint64_t addr, size; + int irq_start, dtb_size; + struct arm_boot_info *info = fdt_params->binfo; + const ARMPlatformBusSystemParams *params = fdt_params->system_params; + const char *intc = fdt_params->intc; + void *fdt = info->get_dtb(info, &dtb_size); + + /* + * If the user provided a dtb, we assume the dynamic sysbus nodes + * already are integrated there. This corresponds to a use case where + * the dynamic sysbus nodes are complex and their generation is not yet + * supported. In that case the user can take charge of the guest dt + * while qemu takes charge of the qom stuff. + */ + if (info->dtb_filename) { + return; + } + + assert(fdt); + + node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); + addr = params->platform_bus_base; + size = params->platform_bus_size; + irq_start = params->platform_bus_first_irq; + + /* Create a /platform node that we can put all devices into */ + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp)); + + /* Our platform bus region is less than 32bits, so 1 cell is enough for + * address and size + */ + qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); + qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); + qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size); + + qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc); + + dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); + pbus = PLATFORM_BUS_DEVICE(dev); + + /* We can only create dt nodes for dynamic devices when they're ready */ + assert(pbus->done_gathering); + + PlatformBusFDTData data = { + .fdt = fdt, + .irq_start = irq_start, + .pbus_node_name = node, + .pbus = pbus, + }; + + /* Loop through all dynamic sysbus devices and create their node */ + foreach_dynamic_sysbus_device(add_fdt_node, &data); + + g_free(node); +} + +static void platform_bus_fdt_notify(Notifier *notifier, void *data) +{ + PlatformBusFDTNotifierParams *p = DO_UPCAST(PlatformBusFDTNotifierParams, + notifier, notifier); + + add_all_platform_bus_fdt_nodes(p->fdt_params); + g_free(p->fdt_params); + g_free(p); +} + +void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params) +{ + PlatformBusFDTNotifierParams *p = g_new(PlatformBusFDTNotifierParams, 1); + + p->fdt_params = fdt_params; + p->notifier.notify = platform_bus_fdt_notify; + qemu_add_machine_init_done_notifier(&p->notifier); +} diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 8f1a5ea992..da217884e6 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -525,7 +525,7 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE); qdev_prop_set_uint8(dev, "width", 4); qdev_prop_set_uint8(dev, "device-width", 2); - qdev_prop_set_uint8(dev, "big-endian", 0); + qdev_prop_set_bit(dev, "big-endian", false); qdev_prop_set_uint16(dev, "id0", 0x89); qdev_prop_set_uint16(dev, "id1", 0x18); qdev_prop_set_uint16(dev, "id2", 0x00); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 05db8cb2f7..1b1cc716ad 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -45,9 +45,11 @@ #include "qemu/error-report.h" #include "hw/pci-host/gpex.h" #include "hw/arm/virt-acpi-build.h" +#include "hw/arm/sysbus-fdt.h" +#include "hw/platform-bus.h" /* Number of external interrupt lines to configure the GIC with */ -#define NUM_IRQS 128 +#define NUM_IRQS 256 #define GIC_FDT_IRQ_TYPE_SPI 0 #define GIC_FDT_IRQ_TYPE_PPI 1 @@ -60,6 +62,10 @@ #define GIC_FDT_IRQ_PPI_CPU_START 8 #define GIC_FDT_IRQ_PPI_CPU_WIDTH 8 +#define PLATFORM_BUS_NUM_IRQS 64 + +static ARMPlatformBusSystemParams platform_bus_params; + typedef struct VirtBoardInfo { struct arm_boot_info bootinfo; const char *cpu_model; @@ -69,6 +75,8 @@ typedef struct VirtBoardInfo { void *fdt; int fdt_size; uint32_t clock_phandle; + uint32_t gic_phandle; + uint32_t v2m_phandle; } VirtBoardInfo; typedef struct { @@ -103,20 +111,22 @@ typedef struct { */ static const MemMapEntry a15memmap[] = { /* Space up to 0x8000000 is reserved for a boot ROM */ - [VIRT_FLASH] = { 0, 0x08000000 }, - [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 }, + [VIRT_FLASH] = { 0, 0x08000000 }, + [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 }, /* GIC distributor and CPU interfaces sit inside the CPU peripheral space */ - [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 }, - [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 }, - [VIRT_UART] = { 0x09000000, 0x00001000 }, - [VIRT_RTC] = { 0x09010000, 0x00001000 }, - [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, - [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 }, + [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 }, + [VIRT_GIC_V2M] = { 0x08020000, 0x00001000 }, + [VIRT_UART] = { 0x09000000, 0x00001000 }, + [VIRT_RTC] = { 0x09010000, 0x00001000 }, + [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, + [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ - [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 }, - [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, - [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, - [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, + [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, + [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 }, + [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, + [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, + [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, }; static const int a15irqmap[] = { @@ -124,6 +134,8 @@ static const int a15irqmap[] = { [VIRT_RTC] = 2, [VIRT_PCIE] = 3, /* ... to 6 */ [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ + [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ + [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ }; static VirtBoardInfo machines[] = { @@ -299,12 +311,23 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) } } -static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi) +static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi) { - uint32_t gic_phandle; + vbi->v2m_phandle = qemu_fdt_alloc_phandle(vbi->fdt); + qemu_fdt_add_subnode(vbi->fdt, "/intc/v2m"); + qemu_fdt_setprop_string(vbi->fdt, "/intc/v2m", "compatible", + "arm,gic-v2m-frame"); + qemu_fdt_setprop(vbi->fdt, "/intc/v2m", "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/v2m", "reg", + 2, vbi->memmap[VIRT_GIC_V2M].base, + 2, vbi->memmap[VIRT_GIC_V2M].size); + qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle); +} - gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt); - qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle); +static void fdt_add_gic_node(VirtBoardInfo *vbi) +{ + vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt); + qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle); qemu_fdt_add_subnode(vbi->fdt, "/intc"); /* 'cortex-a15-gic' means 'GIC v2' */ @@ -317,12 +340,32 @@ static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi) 2, vbi->memmap[VIRT_GIC_DIST].size, 2, vbi->memmap[VIRT_GIC_CPU].base, 2, vbi->memmap[VIRT_GIC_CPU].size); - qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); + qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2); + qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2); + qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0); + qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle); +} + +static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) +{ + int i; + int irq = vbi->irqmap[VIRT_GIC_V2M]; + DeviceState *dev; - return gic_phandle; + dev = qdev_create(NULL, "arm-gicv2m"); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_V2M].base); + qdev_prop_set_uint32(dev, "base-spi", irq); + qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS); + qdev_init_nofail(dev); + + for (i = 0; i < NUM_GICV2M_SPIS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + } + + fdt_add_v2m_gic_node(vbi); } -static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic) +static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic) { /* We create a standalone GIC v2 */ DeviceState *gicdev; @@ -371,7 +414,9 @@ static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic) pic[i] = qdev_get_gpio_in(gicdev, i); } - return fdt_add_gic_node(vbi); + fdt_add_gic_node(vbi); + + create_v2m(vbi, pic); } static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) @@ -510,7 +555,7 @@ static void create_one_flash(const char *name, hwaddr flashbase, qdev_prop_set_uint64(dev, "sector-length", sectorlength); qdev_prop_set_uint8(dev, "width", 4); qdev_prop_set_uint8(dev, "device-width", 2); - qdev_prop_set_uint8(dev, "big-endian", 0); + qdev_prop_set_bit(dev, "big-endian", false); qdev_prop_set_uint16(dev, "id0", 0x89); qdev_prop_set_uint16(dev, "id1", 0x18); qdev_prop_set_uint16(dev, "id2", 0x00); @@ -587,7 +632,7 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, int first_irq, const char *nodename) { int devfn, pin; - uint32_t full_irq_map[4 * 4 * 8] = { 0 }; + uint32_t full_irq_map[4 * 4 * 10] = { 0 }; uint32_t *irq_map = full_irq_map; for (devfn = 0; devfn <= 0x18; devfn += 0x8) { @@ -600,13 +645,13 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, uint32_t map[] = { devfn << 8, 0, 0, /* devfn */ pin + 1, /* PCI pin */ - gic_phandle, irq_type, irq_nr, irq_level }; /* GIC irq */ + gic_phandle, 0, 0, irq_type, irq_nr, irq_level }; /* GIC irq */ /* Convert map to big endian */ - for (i = 0; i < 8; i++) { + for (i = 0; i < 10; i++) { irq_map[i] = cpu_to_be32(map[i]); } - irq_map += 8; + irq_map += 10; } } @@ -618,8 +663,7 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, 0x7 /* PCI irq */); } -static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, - uint32_t gic_phandle) +static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) { hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size; @@ -676,6 +720,8 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); + qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base_ecam, 2, size_ecam); qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", @@ -685,11 +731,52 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, 2, base_mmio, 2, size_mmio); qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1); - create_pcie_irq_map(vbi, gic_phandle, irq, nodename); + create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename); g_free(nodename); } +static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic) +{ + DeviceState *dev; + SysBusDevice *s; + int i; + ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1); + MemoryRegion *sysmem = get_system_memory(); + + platform_bus_params.platform_bus_base = vbi->memmap[VIRT_PLATFORM_BUS].base; + platform_bus_params.platform_bus_size = vbi->memmap[VIRT_PLATFORM_BUS].size; + platform_bus_params.platform_bus_first_irq = vbi->irqmap[VIRT_PLATFORM_BUS]; + platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS; + + fdt_params->system_params = &platform_bus_params; + fdt_params->binfo = &vbi->bootinfo; + fdt_params->intc = "/intc"; + /* + * register a machine init done notifier that creates the device tree + * nodes of the platform bus and its children dynamic sysbus devices + */ + arm_register_platform_bus_fdt_creator(fdt_params); + + dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); + dev->id = TYPE_PLATFORM_BUS_DEVICE; + qdev_prop_set_uint32(dev, "num_irqs", + platform_bus_params.platform_bus_num_irqs); + qdev_prop_set_uint32(dev, "mmio_size", + platform_bus_params.platform_bus_size); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + + for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) { + int irqn = platform_bus_params.platform_bus_first_irq + i; + sysbus_connect_irq(s, i, pic[irqn]); + } + + memory_region_add_subregion(sysmem, + platform_bus_params.platform_bus_base, + sysbus_mmio_get_region(s, 0)); +} + static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) { const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; @@ -717,7 +804,6 @@ static void machvirt_init(MachineState *machine) VirtBoardInfo *vbi; VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); VirtGuestInfo *guest_info = &guest_info_state->info; - uint32_t gic_phandle; char **cpustr; if (!cpu_model) { @@ -794,13 +880,13 @@ static void machvirt_init(MachineState *machine) create_flash(vbi); - gic_phandle = create_gic(vbi, pic); + create_gic(vbi, pic); create_uart(vbi, pic); create_rtc(vbi, pic); - create_pcie(vbi, pic, gic_phandle); + create_pcie(vbi, pic); /* Create mmio transports, so the user can create virtio backends * (which will be automatically plugged in to the transports). If @@ -828,6 +914,14 @@ static void machvirt_init(MachineState *machine) vbi->bootinfo.get_dtb = machvirt_dtb; vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); + + /* + * arm_load_kernel machine init done notifier registration must + * happen before the platform_bus_create call. In this latter, + * another notifier is registered which adds platform bus nodes. + * Notifiers are executed in registration reverse order. + */ + create_platform_bus(vbi, pic); } static bool virt_get_secure(Object *obj, Error **errp) @@ -866,6 +960,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->desc = "ARM Virtual Machine", mc->init = machvirt_init; mc->max_cpus = 8; + mc->has_dynamic_sysbus = true; } static const TypeInfo machvirt_info = { diff --git a/hw/block/fdc.c b/hw/block/fdc.c index d8a8edd936..6e794597dc 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -324,7 +324,7 @@ static void fd_revalidate(FDrive *drv) /* Intel 82078 floppy disk controller emulation */ static void fdctrl_reset(FDCtrl *fdctrl, int do_irq); -static void fdctrl_reset_fifo(FDCtrl *fdctrl); +static void fdctrl_to_command_phase(FDCtrl *fdctrl); static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len); static void fdctrl_raise_irq(FDCtrl *fdctrl); @@ -495,6 +495,33 @@ enum { FD_DIR_DSKCHG = 0x80, }; +/* + * See chapter 5.0 "Controller phases" of the spec: + * + * Command phase: + * The host writes a command and its parameters into the FIFO. The command + * phase is completed when all parameters for the command have been supplied, + * and execution phase is entered. + * + * Execution phase: + * Data transfers, either DMA or non-DMA. For non-DMA transfers, the FIFO + * contains the payload now, otherwise it's unused. When all bytes of the + * required data have been transferred, the state is switched to either result + * phase (if the command produces status bytes) or directly back into the + * command phase for the next command. + * + * Result phase: + * The host reads out the FIFO, which contains one or more result bytes now. + */ +enum { + /* Only for migration: reconstruct phase from registers like qemu 2.3 */ + FD_PHASE_RECONSTRUCT = 0, + + FD_PHASE_COMMAND = 1, + FD_PHASE_EXECUTION = 2, + FD_PHASE_RESULT = 3, +}; + #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) @@ -504,6 +531,7 @@ struct FDCtrl { /* Controller state */ QEMUTimer *result_timer; int dma_chann; + uint8_t phase; /* Controller's identification */ uint8_t version; /* HW */ @@ -744,6 +772,28 @@ static const VMStateDescription vmstate_fdrive = { } }; +/* + * Reconstructs the phase from register values according to the logic that was + * implemented in qemu 2.3. This is the default value that is used if the phase + * subsection is not present on migration. + * + * Don't change this function to reflect newer qemu versions, it is part of + * the migration ABI. + */ +static int reconstruct_phase(FDCtrl *fdctrl) +{ + if (fdctrl->msr & FD_MSR_NONDMA) { + return FD_PHASE_EXECUTION; + } else if ((fdctrl->msr & FD_MSR_RQM) == 0) { + /* qemu 2.3 disabled RQM only during DMA transfers */ + return FD_PHASE_EXECUTION; + } else if (fdctrl->msr & FD_MSR_DIO) { + return FD_PHASE_RESULT; + } else { + return FD_PHASE_COMMAND; + } +} + static void fdc_pre_save(void *opaque) { FDCtrl *s = opaque; @@ -751,12 +801,24 @@ static void fdc_pre_save(void *opaque) s->dor_vmstate = s->dor | GET_CUR_DRV(s); } +static int fdc_pre_load(void *opaque) +{ + FDCtrl *s = opaque; + s->phase = FD_PHASE_RECONSTRUCT; + return 0; +} + static int fdc_post_load(void *opaque, int version_id) { FDCtrl *s = opaque; SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK); s->dor = s->dor_vmstate & ~FD_DOR_SELMASK; + + if (s->phase == FD_PHASE_RECONSTRUCT) { + s->phase = reconstruct_phase(s); + } + return 0; } @@ -794,11 +856,29 @@ static const VMStateDescription vmstate_fdc_result_timer = { } }; +static bool fdc_phase_needed(void *opaque) +{ + FDCtrl *fdctrl = opaque; + + return reconstruct_phase(fdctrl) != fdctrl->phase; +} + +static const VMStateDescription vmstate_fdc_phase = { + .name = "fdc/phase", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(phase, FDCtrl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_fdc = { .name = "fdc", .version_id = 2, .minimum_version_id = 2, .pre_save = fdc_pre_save, + .pre_load = fdc_pre_load, .post_load = fdc_post_load, .fields = (VMStateField[]) { /* Controller State */ @@ -839,6 +919,9 @@ static const VMStateDescription vmstate_fdc = { .vmsd = &vmstate_fdc_result_timer, .needed = fdc_result_timer_needed, } , { + .vmsd = &vmstate_fdc_phase, + .needed = fdc_phase_needed, + } , { /* empty */ } } @@ -918,7 +1001,7 @@ static void fdctrl_reset(FDCtrl *fdctrl, int do_irq) fdctrl->data_dir = FD_DIR_WRITE; for (i = 0; i < MAX_FD; i++) fd_recalibrate(&fdctrl->drives[i]); - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); if (do_irq) { fdctrl->status0 |= FD_SR0_RDYCHG; fdctrl_raise_irq(fdctrl); @@ -1134,17 +1217,22 @@ static uint32_t fdctrl_read_dir(FDCtrl *fdctrl) return retval; } -/* FIFO state control */ -static void fdctrl_reset_fifo(FDCtrl *fdctrl) +/* Clear the FIFO and update the state for receiving the next command */ +static void fdctrl_to_command_phase(FDCtrl *fdctrl) { + fdctrl->phase = FD_PHASE_COMMAND; fdctrl->data_dir = FD_DIR_WRITE; fdctrl->data_pos = 0; + fdctrl->data_len = 1; /* Accept command byte, adjust for params later */ fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO); + fdctrl->msr |= FD_MSR_RQM; } -/* Set FIFO status for the host to read */ -static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len) +/* Update the state to allow the guest to read out the command status. + * @fifo_len is the number of result bytes to be read out. */ +static void fdctrl_to_result_phase(FDCtrl *fdctrl, int fifo_len) { + fdctrl->phase = FD_PHASE_RESULT; fdctrl->data_dir = FD_DIR_READ; fdctrl->data_len = fifo_len; fdctrl->data_pos = 0; @@ -1157,7 +1245,7 @@ static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction) qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n", fdctrl->fifo[0]); fdctrl->fifo[0] = FD_SR0_INVCMD; - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); } /* Seek to next sector @@ -1238,7 +1326,7 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO; fdctrl->msr &= ~FD_MSR_NONDMA; - fdctrl_set_fifo(fdctrl, 7); + fdctrl_to_result_phase(fdctrl, 7); fdctrl_raise_irq(fdctrl); } @@ -1352,7 +1440,7 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) } } FLOPPY_DPRINTF("start non-DMA transfer\n"); - fdctrl->msr |= FD_MSR_NONDMA; + fdctrl->msr |= FD_MSR_NONDMA | FD_MSR_RQM; if (direction != FD_DIR_WRITE) fdctrl->msr |= FD_MSR_DIO; /* IO based transfer: calculate len */ @@ -1505,9 +1593,16 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) FLOPPY_DPRINTF("error: controller not ready for reading\n"); return 0; } + + /* If data_len spans multiple sectors, the current position in the FIFO + * wraps around while fdctrl->data_pos is the real position in the whole + * request. */ pos = fdctrl->data_pos; pos %= FD_SECTOR_LEN; - if (fdctrl->msr & FD_MSR_NONDMA) { + + switch (fdctrl->phase) { + case FD_PHASE_EXECUTION: + assert(fdctrl->msr & FD_MSR_NONDMA); if (pos == 0) { if (fdctrl->data_pos != 0) if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { @@ -1523,20 +1618,28 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) memset(fdctrl->fifo, 0, FD_SECTOR_LEN); } } - } - retval = fdctrl->fifo[pos]; - if (++fdctrl->data_pos == fdctrl->data_len) { - fdctrl->data_pos = 0; - /* Switch from transfer mode to status mode - * then from status mode to command mode - */ - if (fdctrl->msr & FD_MSR_NONDMA) { + + if (++fdctrl->data_pos == fdctrl->data_len) { + fdctrl->msr &= ~FD_MSR_RQM; fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } else { - fdctrl_reset_fifo(fdctrl); + } + break; + + case FD_PHASE_RESULT: + assert(!(fdctrl->msr & FD_MSR_NONDMA)); + if (++fdctrl->data_pos == fdctrl->data_len) { + fdctrl->msr &= ~FD_MSR_RQM; + fdctrl_to_command_phase(fdctrl); fdctrl_reset_irq(fdctrl); } + break; + + case FD_PHASE_COMMAND: + default: + abort(); } + + retval = fdctrl->fifo[pos]; FLOPPY_DPRINTF("data register: 0x%02x\n", retval); return retval; @@ -1606,7 +1709,7 @@ static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction) { fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0; fdctrl->fifo[0] = fdctrl->lock << 4; - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); } static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction) @@ -1631,20 +1734,20 @@ static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction) (cur_drv->perpendicular << 2); fdctrl->fifo[8] = fdctrl->config; fdctrl->fifo[9] = fdctrl->precomp_trk; - fdctrl_set_fifo(fdctrl, 10); + fdctrl_to_result_phase(fdctrl, 10); } static void fdctrl_handle_version(FDCtrl *fdctrl, int direction) { /* Controller's version */ fdctrl->fifo[0] = fdctrl->version; - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); } static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction) { fdctrl->fifo[0] = 0x41; /* Stepping 1 */ - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); } static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction) @@ -1667,7 +1770,7 @@ static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction) fdctrl->config = fdctrl->fifo[11]; fdctrl->precomp_trk = fdctrl->fifo[12]; fdctrl->pwrd = fdctrl->fifo[13]; - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); } static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) @@ -1697,7 +1800,7 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) fdctrl->fifo[12] = fdctrl->pwrd; fdctrl->fifo[13] = 0; fdctrl->fifo[14] = 0; - fdctrl_set_fifo(fdctrl, 15); + fdctrl_to_result_phase(fdctrl, 15); } static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) @@ -1746,7 +1849,7 @@ static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction) else fdctrl->dor |= FD_DOR_DMAEN; /* No result back */ - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); } static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction) @@ -1762,7 +1865,7 @@ static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction) (cur_drv->head << 2) | GET_CUR_DRV(fdctrl) | 0x28; - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); } static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction) @@ -1772,7 +1875,7 @@ static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction) SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); fd_recalibrate(cur_drv); - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); /* Raise Interrupt */ fdctrl->status0 |= FD_SR0_SEEK; fdctrl_raise_irq(fdctrl); @@ -1788,7 +1891,7 @@ static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction) fdctrl->reset_sensei--; } else if (!(fdctrl->sra & FD_SRA_INTPEND)) { fdctrl->fifo[0] = FD_SR0_INVCMD; - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); return; } else { fdctrl->fifo[0] = @@ -1797,7 +1900,7 @@ static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction) } fdctrl->fifo[1] = cur_drv->track; - fdctrl_set_fifo(fdctrl, 2); + fdctrl_to_result_phase(fdctrl, 2); fdctrl_reset_irq(fdctrl); fdctrl->status0 = FD_SR0_RDYCHG; } @@ -1808,7 +1911,7 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction) SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); /* The seek command just sends step pulses to the drive and doesn't care if * there is a medium inserted of if it's banging the head against the drive. */ @@ -1825,7 +1928,7 @@ static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction) if (fdctrl->fifo[1] & 0x80) cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; /* No result back */ - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); } static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction) @@ -1833,20 +1936,20 @@ static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction) fdctrl->config = fdctrl->fifo[2]; fdctrl->precomp_trk = fdctrl->fifo[3]; /* No result back */ - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); } static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction) { fdctrl->pwrd = fdctrl->fifo[1]; fdctrl->fifo[0] = fdctrl->fifo[1]; - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); } static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) { /* No result back */ - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); } static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction) @@ -1862,15 +1965,15 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct fdctrl->fifo[0] = fdctrl->fifo[1]; fdctrl->fifo[2] = 0; fdctrl->fifo[3] = 0; - fdctrl_set_fifo(fdctrl, 4); + fdctrl_to_result_phase(fdctrl, 4); } else { - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); } } else if (fdctrl->data_len > 7) { /* ERROR */ fdctrl->fifo[0] = 0x80 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); - fdctrl_set_fifo(fdctrl, 1); + fdctrl_to_result_phase(fdctrl, 1); } } @@ -1887,7 +1990,7 @@ static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction) fd_seek(cur_drv, cur_drv->head, cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1); } - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); /* Raise Interrupt */ fdctrl->status0 |= FD_SR0_SEEK; fdctrl_raise_irq(fdctrl); @@ -1905,20 +2008,25 @@ static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction) fd_seek(cur_drv, cur_drv->head, cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1); } - fdctrl_reset_fifo(fdctrl); + fdctrl_to_command_phase(fdctrl); /* Raise Interrupt */ fdctrl->status0 |= FD_SR0_SEEK; fdctrl_raise_irq(fdctrl); } -static const struct { +/* + * Handlers for the execution phase of each command + */ +typedef struct FDCtrlCommand { uint8_t value; uint8_t mask; const char* name; int parameters; void (*handler)(FDCtrl *fdctrl, int direction); int direction; -} handlers[] = { +} FDCtrlCommand; + +static const FDCtrlCommand handlers[] = { { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ }, { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE }, { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek }, @@ -1955,9 +2063,19 @@ static const struct { /* Associate command to an index in the 'handlers' array */ static uint8_t command_to_handler[256]; +static const FDCtrlCommand *get_command(uint8_t cmd) +{ + int idx; + + idx = command_to_handler[cmd]; + FLOPPY_DPRINTF("%s command\n", handlers[idx].name); + return &handlers[idx]; +} + static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) { FDrive *cur_drv; + const FDCtrlCommand *cmd; uint32_t pos; /* Reset mode */ @@ -1970,12 +2088,27 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) return; } fdctrl->dsr &= ~FD_DSR_PWRDOWN; - /* Is it write command time ? */ - if (fdctrl->msr & FD_MSR_NONDMA) { + + FLOPPY_DPRINTF("%s: %02x\n", __func__, value); + + /* If data_len spans multiple sectors, the current position in the FIFO + * wraps around while fdctrl->data_pos is the real position in the whole + * request. */ + pos = fdctrl->data_pos++; + pos %= FD_SECTOR_LEN; + fdctrl->fifo[pos] = value; + + if (fdctrl->data_pos == fdctrl->data_len) { + fdctrl->msr &= ~FD_MSR_RQM; + } + + switch (fdctrl->phase) { + case FD_PHASE_EXECUTION: + /* For DMA requests, RQM should be cleared during execution phase, so + * we would have errored out above. */ + assert(fdctrl->msr & FD_MSR_NONDMA); + /* FIFO data write */ - pos = fdctrl->data_pos++; - pos %= FD_SECTOR_LEN; - fdctrl->fifo[pos] = value; if (pos == FD_SECTOR_LEN - 1 || fdctrl->data_pos == fdctrl->data_len) { cur_drv = get_cur_drv(fdctrl); @@ -1983,45 +2116,54 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); - return; + break; } if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { FLOPPY_DPRINTF("error seeking to next sector %d\n", fd_sector(cur_drv)); - return; + break; } } - /* Switch from transfer mode to status mode - * then from status mode to command mode - */ - if (fdctrl->data_pos == fdctrl->data_len) + + /* Switch to result phase when done with the transfer */ + if (fdctrl->data_pos == fdctrl->data_len) { fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - return; - } - if (fdctrl->data_pos == 0) { - /* Command */ - pos = command_to_handler[value & 0xff]; - FLOPPY_DPRINTF("%s command\n", handlers[pos].name); - fdctrl->data_len = handlers[pos].parameters + 1; - fdctrl->msr |= FD_MSR_CMDBUSY; - } + } + break; - FLOPPY_DPRINTF("%s: %02x\n", __func__, value); - pos = fdctrl->data_pos++; - pos %= FD_SECTOR_LEN; - fdctrl->fifo[pos] = value; - if (fdctrl->data_pos == fdctrl->data_len) { - /* We now have all parameters - * and will be able to treat the command - */ - if (fdctrl->data_state & FD_STATE_FORMAT) { - fdctrl_format_sector(fdctrl); - return; + case FD_PHASE_COMMAND: + assert(!(fdctrl->msr & FD_MSR_NONDMA)); + assert(fdctrl->data_pos < FD_SECTOR_LEN); + + if (pos == 0) { + /* The first byte specifies the command. Now we start reading + * as many parameters as this command requires. */ + cmd = get_command(value); + fdctrl->data_len = cmd->parameters + 1; + if (cmd->parameters) { + fdctrl->msr |= FD_MSR_RQM; + } + fdctrl->msr |= FD_MSR_CMDBUSY; + } + + if (fdctrl->data_pos == fdctrl->data_len) { + /* We have all parameters now, execute the command */ + fdctrl->phase = FD_PHASE_EXECUTION; + + if (fdctrl->data_state & FD_STATE_FORMAT) { + fdctrl_format_sector(fdctrl); + break; + } + + cmd = get_command(fdctrl->fifo[0]); + FLOPPY_DPRINTF("Calling handler for '%s'\n", cmd->name); + cmd->handler(fdctrl, cmd->direction); } + break; - pos = command_to_handler[fdctrl->fifo[0] & 0xff]; - FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name); - (*handlers[pos].handler)(fdctrl, handlers[pos].direction); + case FD_PHASE_RESULT: + default: + abort(); } } diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index d282695086..2ba6c77293 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -64,6 +64,9 @@ do { \ #define TYPE_CFI_PFLASH01 "cfi.pflash01" #define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01) +#define PFLASH_BE 0 +#define PFLASH_SECURE 1 + struct pflash_t { /*< private >*/ SysBusDevice parent_obj; @@ -75,7 +78,7 @@ struct pflash_t { uint8_t bank_width; uint8_t device_width; /* If 0, device width not specified. */ uint8_t max_device_width; /* max device width in bytes */ - uint8_t be; + uint32_t features; uint8_t wcycle; /* if 0, the flash is read normally */ int ro; uint8_t cmd; @@ -235,12 +238,57 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) return resp; } +static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, + int width, int be) +{ + uint8_t *p; + uint32_t ret; + + p = pfl->storage; + switch (width) { + case 1: + ret = p[offset]; + DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n", + __func__, offset, ret); + break; + case 2: + if (be) { + ret = p[offset] << 8; + ret |= p[offset + 1]; + } else { + ret = p[offset]; + ret |= p[offset + 1] << 8; + } + DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n", + __func__, offset, ret); + break; + case 4: + if (be) { + ret = p[offset] << 24; + ret |= p[offset + 1] << 16; + ret |= p[offset + 2] << 8; + ret |= p[offset + 3]; + } else { + ret = p[offset]; + ret |= p[offset + 1] << 8; + ret |= p[offset + 2] << 16; + ret |= p[offset + 3] << 24; + } + DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n", + __func__, offset, ret); + break; + default: + DPRINTF("BUG in %s\n", __func__); + abort(); + } + return ret; +} + static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, int width, int be) { hwaddr boff; uint32_t ret; - uint8_t *p; ret = -1; @@ -257,43 +305,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, /* fall through to read code */ case 0x00: /* Flash area read */ - p = pfl->storage; - switch (width) { - case 1: - ret = p[offset]; - DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n", - __func__, offset, ret); - break; - case 2: - if (be) { - ret = p[offset] << 8; - ret |= p[offset + 1]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - } - DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n", - __func__, offset, ret); - break; - case 4: - if (be) { - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; - } - DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n", - __func__, offset, ret); - break; - default: - DPRINTF("BUG in %s\n", __func__); - } - + ret = pflash_data_read(pfl, offset, width, be); break; case 0x10: /* Single byte program */ case 0x20: /* Block erase */ @@ -648,101 +660,37 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, } -static uint32_t pflash_readb_be(void *opaque, hwaddr addr) -{ - return pflash_read(opaque, addr, 1, 1); -} - -static uint32_t pflash_readb_le(void *opaque, hwaddr addr) -{ - return pflash_read(opaque, addr, 1, 0); -} - -static uint32_t pflash_readw_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 1); -} - -static uint32_t pflash_readw_le(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 0); -} - -static uint32_t pflash_readl_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 1); -} - -static uint32_t pflash_readl_le(void *opaque, hwaddr addr) +static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value, + unsigned len, MemTxAttrs attrs) { pflash_t *pfl = opaque; + bool be = !!(pfl->features & (1 << PFLASH_BE)); - return pflash_read(pfl, addr, 4, 0); -} - -static void pflash_writeb_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 1); -} - -static void pflash_writeb_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 0); -} - -static void pflash_writew_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 1); -} - -static void pflash_writew_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 0); -} - -static void pflash_writel_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 1); + if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { + *value = pflash_data_read(opaque, addr, len, be); + } else { + *value = pflash_read(opaque, addr, len, be); + } + return MEMTX_OK; } -static void pflash_writel_le(void *opaque, hwaddr addr, - uint32_t value) +static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value, + unsigned len, MemTxAttrs attrs) { pflash_t *pfl = opaque; + bool be = !!(pfl->features & (1 << PFLASH_BE)); - pflash_write(pfl, addr, value, 4, 0); + if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { + return MEMTX_ERROR; + } else { + pflash_write(opaque, addr, value, len, be); + return MEMTX_OK; + } } -static const MemoryRegionOps pflash_cfi01_ops_be = { - .old_mmio = { - .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, }, - .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps pflash_cfi01_ops_le = { - .old_mmio = { - .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, }, - .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, }, - }, +static const MemoryRegionOps pflash_cfi01_ops = { + .read_with_attrs = pflash_mem_read_with_attrs, + .write_with_attrs = pflash_mem_write_with_attrs, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -773,7 +721,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) memory_region_init_rom_device( &pfl->mem, OBJECT(dev), - pfl->be ? &pflash_cfi01_ops_be : &pflash_cfi01_ops_le, pfl, + &pflash_cfi01_ops, + pfl, pfl->name, total_len, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -925,7 +874,8 @@ static Property pflash_cfi01_properties[] = { DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0), - DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), + DEFINE_PROP_BIT("big-endian", struct pflash_t, features, PFLASH_BE, 0), + DEFINE_PROP_BIT("secure", struct pflash_t, features, PFLASH_SECURE, 0), DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), @@ -975,7 +925,7 @@ pflash_t *pflash_cfi01_register(hwaddr base, qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); qdev_prop_set_uint64(dev, "sector-length", sector_len); qdev_prop_set_uint8(dev, "width", bank_width); - qdev_prop_set_uint8(dev, "big-endian", !!be); + qdev_prop_set_bit(dev, "big-endian", !!be); qdev_prop_set_uint16(dev, "id0", id0); qdev_prop_set_uint16(dev, "id1", id1); qdev_prop_set_uint16(dev, "id2", id2); diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 4079554bb9..c2b553f0d1 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -641,28 +641,3 @@ static void parallel_register_types(void) } type_init(parallel_register_types) - -static void parallel_init(ISABus *bus, int index, CharDriverState *chr) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_create(bus, "isa-parallel"); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "index", index); - qdev_prop_set_chr(dev, "chardev", chr); - qdev_init_nofail(dev); -} - -void parallel_hds_isa_init(ISABus *bus, int n) -{ - int i; - - assert(n <= MAX_PARALLEL_PORTS); - - for (i = 0; i < n; i++) { - if (parallel_hds[i]) { - parallel_init(bus, i, parallel_hds[i]); - } - } -} diff --git a/hw/core/machine.c b/hw/core/machine.c index 25c45e6f9d..ac4654e9dd 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -294,6 +294,14 @@ static void machine_init_notify(Notifier *notifier, void *data) foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL); } +static void machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + /* Default 128 MB as guest ram size */ + mc->default_ram_size = 128 * M_BYTE; +} + static void machine_initfn(Object *obj) { MachineState *ms = MACHINE(obj); @@ -463,6 +471,7 @@ static const TypeInfo machine_info = { .parent = TYPE_OBJECT, .abstract = true, .class_size = sizeof(MachineClass), + .class_init = machine_class_init, .instance_size = sizeof(MachineState), .instance_init = machine_initfn, .instance_finalize = machine_finalize, diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index c413226a97..0309fe5767 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -389,7 +389,7 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) nd->instantiated = 1; } -static int qdev_add_one_global(QemuOpts *opts, void *opaque) +static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp) { GlobalProperty *g; @@ -404,5 +404,6 @@ static int qdev_add_one_global(QemuOpts *opts, void *opaque) void qemu_add_globals(void) { - qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0); + qemu_opts_foreach(qemu_find_opts("global"), + qdev_add_one_global, NULL, NULL); } diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 1e6ff2b546..b94e5e0d78 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -106,6 +106,7 @@ static void cg3_update_display(void *opaque) pix = memory_region_get_ram_ptr(&s->vram_mem); data = (uint32_t *)surface_data(surface); + memory_region_sync_dirty_bitmap(&s->vram_mem); for (y = 0; y < height; y++) { int update = s->full_update; @@ -309,6 +310,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp) memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size, &error_abort); + memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); vmstate_register_ram_global(&s->vram_mem); sysbus_init_mmio(sbd, &s->vram_mem); diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 45c62afac1..72b3a1d063 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1109,6 +1109,12 @@ static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w) } } +static void exynos4210_fimd_invalidate(void *opaque) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + s->invalidate = true; +} + /* Updates specified window's MemorySection based on values of WINCON, * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) @@ -1136,7 +1142,11 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) /* TODO: add .exit and unref the region there. Not needed yet since sysbus * does not support hot-unplug. */ - memory_region_unref(w->mem_section.mr); + if (w->mem_section.mr) { + memory_region_set_log(w->mem_section.mr, false, DIRTY_MEMORY_VGA); + memory_region_unref(w->mem_section.mr); + } + w->mem_section = memory_region_find(sysbus_address_space(sbd), fb_start_addr, w->fb_len); assert(w->mem_section.mr); @@ -1162,6 +1172,8 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0); goto error_return; } + memory_region_set_log(w->mem_section.mr, true, DIRTY_MEMORY_VGA); + exynos4210_fimd_invalidate(s); return; error_return: @@ -1224,12 +1236,6 @@ static void exynos4210_fimd_update_irq(Exynos4210fimdState *s) } } -static void exynos4210_fimd_invalidate(void *opaque) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - s->invalidate = true; -} - static void exynos4210_update_resolution(Exynos4210fimdState *s) { DisplaySurface *surface = qemu_console_surface(s->console); diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c index 4546e42654..2cabced208 100644 --- a/hw/display/framebuffer.c +++ b/hw/display/framebuffer.c @@ -63,6 +63,10 @@ void framebuffer_update_display( assert(mem_section.offset_within_address_space == base); memory_region_sync_dirty_bitmap(mem); + if (!memory_region_is_logging(mem, DIRTY_MEMORY_VGA)) { + invalidate = true; + } + src_base = cpu_physical_memory_map(base, &src_len, 0); /* If we can't map the framebuffer then bail. We could try harder, but it's not really worth it as dirty flag tracking will probably diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 46f7b41211..52a9733bfd 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -260,6 +260,7 @@ static void g364fb_update_display(void *opaque) qemu_console_resize(s->con, s->width, s->height); } + memory_region_sync_dirty_bitmap(&s->mem_vram); if (s->ctla & CTLA_FORCE_BLANK) { g364fb_draw_blank(s); } else if (s->depth == 8) { @@ -489,7 +490,7 @@ static void g364fb_init(DeviceState *dev, G364State *s) memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram", s->vram_size, s->vram); vmstate_register_ram(&s->mem_vram, dev); - memory_region_set_coalescing(&s->mem_vram); + memory_region_set_log(&s->mem_vram, true, DIRTY_MEMORY_VGA); } #define TYPE_G364 "sysbus-g364" diff --git a/hw/display/sm501.c b/hw/display/sm501.c index c72154b6f1..15a5ba8000 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1322,6 +1322,7 @@ static void sm501_draw_crt(SM501State * s) } /* draw each line according to conditions */ + memory_region_sync_dirty_bitmap(&s->local_mem_region); for (y = 0; y < height; y++) { int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0; int update = full_update || update_hwc; @@ -1412,6 +1413,7 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local", local_mem_bytes, &error_abort); vmstate_register_ram_global(&s->local_mem_region); + memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA); s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); memory_region_add_subregion(address_space_mem, base, &s->local_mem_region); diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 66b7ade8da..f5f3f3e69d 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -571,7 +571,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) s->irq = irq; s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS); - s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1); + s->l3v = qemu_allocate_irq(tc6393xb_l3v, s, 0); s->blanked = 1; s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS); diff --git a/hw/display/tcx.c b/hw/display/tcx.c index a9f9f66d15..f3faf78bf8 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -353,6 +353,7 @@ static void tcx_update_display(void *opaque) return; } + memory_region_sync_dirty_bitmap(&ts->vram_mem); for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) { if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, DIRTY_MEMORY_VGA)) { @@ -446,6 +447,7 @@ static void tcx24_update_display(void *opaque) dd = surface_stride(surface); ds = 1024; + memory_region_sync_dirty_bitmap(&ts->vram_mem); for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE, page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { if (tcx24_check_dirty(ts, page, page24, cpage)) { @@ -1006,6 +1008,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", s->vram_size * (1 + 4 + 4), &error_abort); vmstate_register_ram_global(&s->vram_mem); + memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); vram_base = memory_region_get_ram_ptr(&s->vram_mem); /* 10/ROM : FCode ROM */ diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index c17ddd1fcd..7f397d3c2e 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1124,7 +1124,7 @@ static void vmsvga_update_display(void *opaque) * Is it more efficient to look at vram VGA-dirty bits or wait * for the driver to issue SVGA_CMD_UPDATE? */ - if (memory_region_is_logging(&s->vga.vram)) { + if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) { vga_sync_dirty_bitmap(&s->vga); dirty = memory_region_get_dirty(&s->vga.vram, 0, surface_stride(surface) * surface_height(surface), diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index bd03e99975..4ba730b476 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -173,7 +173,7 @@ static uint64_t pl061_read(void *opaque, hwaddr offset, case 0x414: /* Raw interrupt status */ return s->istate; case 0x418: /* Masked interrupt status */ - return s->istate | s->im; + return s->istate & s->im; case 0x420: /* Alternate function select */ return s->afsel; case 0x500: /* 2mA drive */ diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 2c7399b9db..5593e41f84 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -240,13 +240,32 @@ static void acpi_get_misc_info(AcpiMiscInfo *info) info->applesmc_io_base = applesmc_port(); } +/* + * Because of the PXB hosts we cannot simply query TYPE_PCI_HOST_BRIDGE. + * On i386 arch we only have two pci hosts, so we can look only for them. + */ +static Object *acpi_get_i386_pci_host(void) +{ + PCIHostState *host; + + host = OBJECT_CHECK(PCIHostState, + object_resolve_path("/machine/i440fx", NULL), + TYPE_PCI_HOST_BRIDGE); + if (!host) { + host = OBJECT_CHECK(PCIHostState, + object_resolve_path("/machine/q35", NULL), + TYPE_PCI_HOST_BRIDGE); + } + + return OBJECT(host); +} + static void acpi_get_pci_info(PcPciInfo *info) { Object *pci_host; - bool ambiguous; - pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); - g_assert(!ambiguous); + + pci_host = acpi_get_i386_pci_host(); g_assert(pci_host); info->w32.begin = object_property_get_int(pci_host, @@ -596,6 +615,291 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, } } aml_append(parent_scope, method); + qobject_decref(bsel); +} + +/* + * initialize_route - Initialize the interrupt routing rule + * through a specific LINK: + * if (lnk_idx == idx) + * route using link 'link_name' + */ +static Aml *initialize_route(Aml *route, const char *link_name, + Aml *lnk_idx, int idx) +{ + Aml *if_ctx = aml_if(aml_equal(lnk_idx, aml_int(idx))); + Aml *pkg = aml_package(4); + + aml_append(pkg, aml_int(0)); + aml_append(pkg, aml_int(0)); + aml_append(pkg, aml_name("%s", link_name)); + aml_append(pkg, aml_int(0)); + aml_append(if_ctx, aml_store(pkg, route)); + + return if_ctx; +} + +/* + * build_prt - Define interrupt rounting rules + * + * Returns an array of 128 routes, one for each device, + * based on device location. + * The main goal is to equaly distribute the interrupts + * over the 4 existing ACPI links (works only for i440fx). + * The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]". + * + */ +static Aml *build_prt(void) +{ + Aml *method, *while_ctx, *pin, *res; + + method = aml_method("_PRT", 0); + res = aml_local(0); + pin = aml_local(1); + aml_append(method, aml_store(aml_package(128), res)); + aml_append(method, aml_store(aml_int(0), pin)); + + /* while (pin < 128) */ + while_ctx = aml_while(aml_lless(pin, aml_int(128))); + { + Aml *slot = aml_local(2); + Aml *lnk_idx = aml_local(3); + Aml *route = aml_local(4); + + /* slot = pin >> 2 */ + aml_append(while_ctx, + aml_store(aml_shiftright(pin, aml_int(2)), slot)); + /* lnk_idx = (slot + pin) & 3 */ + aml_append(while_ctx, + aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx)); + + /* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */ + aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0)); + aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx, 1)); + aml_append(while_ctx, initialize_route(route, "LNKB", lnk_idx, 2)); + aml_append(while_ctx, initialize_route(route, "LNKC", lnk_idx, 3)); + + /* route[0] = 0x[slot]FFFF */ + aml_append(while_ctx, + aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)), + aml_index(route, aml_int(0)))); + /* route[1] = pin & 3 */ + aml_append(while_ctx, + aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1)))); + /* res[pin] = route */ + aml_append(while_ctx, aml_store(route, aml_index(res, pin))); + /* pin++ */ + aml_append(while_ctx, aml_increment(pin)); + } + aml_append(method, while_ctx); + /* return res*/ + aml_append(method, aml_return(res)); + + return method; +} + +typedef struct CrsRangeEntry { + uint64_t base; + uint64_t limit; +} CrsRangeEntry; + +static void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit) +{ + CrsRangeEntry *entry; + + entry = g_malloc(sizeof(*entry)); + entry->base = base; + entry->limit = limit; + + g_ptr_array_add(ranges, entry); +} + +static void crs_range_free(gpointer data) +{ + CrsRangeEntry *entry = (CrsRangeEntry *)data; + g_free(entry); +} + +static gint crs_range_compare(gconstpointer a, gconstpointer b) +{ + CrsRangeEntry *entry_a = *(CrsRangeEntry **)a; + CrsRangeEntry *entry_b = *(CrsRangeEntry **)b; + + return (int64_t)entry_a->base - (int64_t)entry_b->base; +} + +/* + * crs_replace_with_free_ranges - given the 'used' ranges within [start - end] + * interval, computes the 'free' ranges from the same interval. + * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function + * will return { [base - a1], [a2 - b1], [b2 - limit] }. + */ +static void crs_replace_with_free_ranges(GPtrArray *ranges, + uint64_t start, uint64_t end) +{ + GPtrArray *free_ranges = g_ptr_array_new_with_free_func(crs_range_free); + uint64_t free_base = start; + int i; + + g_ptr_array_sort(ranges, crs_range_compare); + for (i = 0; i < ranges->len; i++) { + CrsRangeEntry *used = g_ptr_array_index(ranges, i); + + if (free_base < used->base) { + crs_range_insert(free_ranges, free_base, used->base - 1); + } + + free_base = used->limit + 1; + } + + if (free_base < end) { + crs_range_insert(free_ranges, free_base, end); + } + + g_ptr_array_set_size(ranges, 0); + for (i = 0; i < free_ranges->len; i++) { + g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i)); + } + + g_ptr_array_free(free_ranges, false); +} + +static Aml *build_crs(PCIHostState *host, + GPtrArray *io_ranges, GPtrArray *mem_ranges) +{ + Aml *crs = aml_resource_template(); + uint8_t max_bus = pci_bus_num(host->bus); + uint8_t type; + int devfn; + + for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { + int i; + uint64_t range_base, range_limit; + PCIDevice *dev = host->bus->devices[devfn]; + + if (!dev) { + continue; + } + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + PCIIORegion *r = &dev->io_regions[i]; + + range_base = r->addr; + range_limit = r->addr + r->size - 1; + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (!range_base || range_base > range_limit) { + continue; + } + + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + aml_append(crs, + aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0, + range_base, + range_limit, + 0, + range_limit - range_base + 1)); + crs_range_insert(io_ranges, range_base, range_limit); + } else { /* "memory" */ + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, + range_base, + range_limit, + 0, + range_limit - range_base + 1)); + crs_range_insert(mem_ranges, range_base, range_limit); + } + } + + type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + if (type == PCI_HEADER_TYPE_BRIDGE) { + uint8_t subordinate = dev->config[PCI_SUBORDINATE_BUS]; + if (subordinate > max_bus) { + max_bus = subordinate; + } + + range_base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); + range_limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base || range_base > range_limit) { + aml_append(crs, + aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0, + range_base, + range_limit, + 0, + range_limit - range_base + 1)); + crs_range_insert(io_ranges, range_base, range_limit); + } + + range_base = + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + range_limit = + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base || range_base > range_limit) { + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, + range_base, + range_limit, + 0, + range_limit - range_base + 1)); + crs_range_insert(mem_ranges, range_base, range_limit); + } + + range_base = + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + range_limit = + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base || range_base > range_limit) { + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, + range_base, + range_limit, + 0, + range_limit - range_base + 1)); + crs_range_insert(mem_ranges, range_base, range_limit); + } + } + } + + aml_append(crs, + aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, + 0, + pci_bus_num(host->bus), + max_bus, + 0, + max_bus - pci_bus_num(host->bus) + 1)); + + return crs; } static void @@ -607,6 +911,11 @@ build_ssdt(GArray *table_data, GArray *linker, uint32_t nr_mem = machine->ram_slots; unsigned acpi_cpus = guest_info->apic_id_limit; Aml *ssdt, *sb_scope, *scope, *pkg, *dev, *method, *crs, *field, *ifctx; + PCIBus *bus = NULL; + GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free); + GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeEntry *entry; + int root_bus_limit = 0xFF; int i; ssdt = init_aml_allocator(); @@ -618,31 +927,81 @@ build_ssdt(GArray *table_data, GArray *linker, /* Reserve space for header */ acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); + /* Extra PCI root buses are implemented only for i440fx */ + bus = find_i440fx(); + if (bus) { + QLIST_FOREACH(bus, &bus->child, sibling) { + uint8_t bus_num = pci_bus_num(bus); + uint8_t numa_node = pci_bus_numa_node(bus); + + /* look only for expander root buses */ + if (!pci_bus_is_root(bus)) { + continue; + } + + if (bus_num < root_bus_limit) { + root_bus_limit = bus_num - 1; + } + + scope = aml_scope("\\_SB"); + dev = aml_device("PC%.02X", bus_num); + aml_append(dev, + aml_name_decl("_UID", aml_string("PC%.02X", bus_num))); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A03"))); + aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num))); + + if (numa_node != NUMA_NODE_UNASSIGNED) { + aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node))); + } + + aml_append(dev, build_prt()); + crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), + io_ranges, mem_ranges); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + aml_append(ssdt, scope); + } + } + scope = aml_scope("\\_SB.PCI0"); /* build PCI0._CRS */ crs = aml_resource_template(); aml_append(crs, aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - 0x0000, 0x0000, 0x00FF, 0x0000, 0x0100)); + 0x0000, 0x0, root_bus_limit, + 0x0000, root_bus_limit + 1)); aml_append(crs, aml_io(AML_DECODE16, 0x0CF8, 0x0CF8, 0x01, 0x08)); aml_append(crs, aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, AML_ENTIRE_RANGE, 0x0000, 0x0000, 0x0CF7, 0x0000, 0x0CF8)); - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0x0000, 0x0D00, 0xFFFF, 0x0000, 0xF300)); + + crs_replace_with_free_ranges(io_ranges, 0x0D00, 0xFFFF); + for (i = 0; i < io_ranges->len; i++) { + entry = g_ptr_array_index(io_ranges, i); + aml_append(crs, + aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0x0000, entry->base, entry->limit, + 0x0000, entry->limit - entry->base + 1)); + } + aml_append(crs, aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, AML_CACHEABLE, AML_READ_WRITE, 0, 0x000A0000, 0x000BFFFF, 0, 0x00020000)); - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_NON_CACHEABLE, AML_READ_WRITE, - 0, pci->w32.begin, pci->w32.end - 1, 0, - pci->w32.end - pci->w32.begin)); + + crs_replace_with_free_ranges(mem_ranges, pci->w32.begin, pci->w32.end - 1); + for (i = 0; i < mem_ranges->len; i++) { + entry = g_ptr_array_index(mem_ranges, i); + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, + 0, entry->base, entry->limit, + 0, entry->limit - entry->base + 1)); + } + if (pci->w64.begin) { aml_append(crs, aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, @@ -665,6 +1024,9 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); + g_ptr_array_free(io_ranges, true); + g_ptr_array_free(mem_ranges, true); + /* reserve PCIHP resources */ if (pm->pcihp_io_len) { dev = aml_device("PHPR"); @@ -957,10 +1319,9 @@ build_ssdt(GArray *table_data, GArray *linker, { Object *pci_host; PCIBus *bus = NULL; - bool ambiguous; - pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); - if (!ambiguous && pci_host) { + pci_host = acpi_get_i386_pci_host(); + if (pci_host) { bus = PCI_HOST_BRIDGE(pci_host)->bus; } @@ -1272,10 +1633,8 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) { Object *pci_host; QObject *o; - bool ambiguous; - pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); - g_assert(!ambiguous); + pci_host = acpi_get_i386_pci_host(); g_assert(pci_host); o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 1eb1db0372..3f0d435da9 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -30,6 +30,7 @@ #include "hw/block/fdc.h" #include "hw/ide.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "monitor/monitor.h" #include "hw/nvram/fw_cfg.h" #include "hw/timer/hpet.h" @@ -163,27 +164,6 @@ uint64_t cpu_get_tsc(CPUX86State *env) return cpu_get_ticks(); } -/* SMM support */ - -static cpu_set_smm_t smm_set; -static void *smm_arg; - -void cpu_smm_register(cpu_set_smm_t callback, void *arg) -{ - assert(smm_set == NULL); - assert(smm_arg == NULL); - smm_set = callback; - smm_arg = arg; -} - -void cpu_smm_update(CPUX86State *env) -{ - if (smm_set && smm_arg && CPU(x86_env_get_cpu(env)) == first_cpu) { - smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg); - } -} - - /* IRQ handling */ int cpu_get_pic_interrupt(CPUX86State *env) { @@ -1006,7 +986,6 @@ static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id, } qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc")); - object_unref(OBJECT(cpu)); object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err); object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); @@ -1025,7 +1004,9 @@ static const char *current_cpu_model; void pc_hot_add_cpu(const int64_t id, Error **errp) { DeviceState *icc_bridge; + X86CPU *cpu; int64_t apic_id = x86_cpu_apic_id_from_index(id); + Error *local_err = NULL; if (id < 0) { error_setg(errp, "Invalid CPU id: %" PRIi64, id); @@ -1053,7 +1034,12 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) icc_bridge = DEVICE(object_resolve_path_type("icc-bridge", TYPE_ICC_BRIDGE, NULL)); - pc_new_cpu(current_cpu_model, apic_id, icc_bridge, errp); + cpu = pc_new_cpu(current_cpu_model, apic_id, icc_bridge, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + object_unref(OBJECT(cpu)); } void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) @@ -1087,6 +1073,7 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) error_report_err(error); exit(1); } + object_unref(OBJECT(cpu)); } /* map APIC MMIO area if CPU has APIC */ @@ -1119,6 +1106,25 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data) PcGuestInfoState *guest_info_state = container_of(notifier, PcGuestInfoState, machine_done); + PCIBus *bus = find_i440fx(); + + if (bus) { + int extra_hosts = 0; + + QLIST_FOREACH(bus, &bus->child, sibling) { + /* look for expander root buses */ + if (pci_bus_is_root(bus)) { + extra_hosts++; + } + } + if (extra_hosts && guest_info_state->info.fw_cfg) { + uint64_t *val = g_malloc(sizeof(*val)); + *val = cpu_to_le64(extra_hosts); + fw_cfg_add_file(guest_info_state->info.fw_cfg, + "etc/extra-pci-roots", val, sizeof(*val)); + } + } + acpi_setup(&guest_info_state->info); } @@ -1345,9 +1351,9 @@ FWCfgState *pc_memory_init(MachineState *machine, return fw_cfg; } -qemu_irq *pc_allocate_cpu_irq(void) +qemu_irq pc_allocate_cpu_irq(void) { - return qemu_allocate_irqs(pic_irq_request, NULL, 1); + return qemu_allocate_irq(pic_irq_request, NULL, 0); } DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 6e7fa424b1..5253e6d4fa 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -86,10 +86,9 @@ static void pc_init1(MachineState *machine) ISABus *isa_bus; PCII440FXState *i440fx_state; int piix3_devfn = -1; - qemu_irq *cpu_irq; qemu_irq *gsi; qemu_irq *i8259; - qemu_irq *smi_irq; + qemu_irq smi_irq; GSIState *gsi_state; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; @@ -99,7 +98,6 @@ static void pc_init1(MachineState *machine) MemoryRegion *pci_memory; MemoryRegion *rom_memory; DeviceState *icc_bridge; - FWCfgState *fw_cfg = NULL; PcGuestInfo *guest_info; ram_addr_t lowmem; @@ -180,16 +178,16 @@ static void pc_init1(MachineState *machine) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - fw_cfg = pc_memory_init(machine, system_memory, - below_4g_mem_size, above_4g_mem_size, - rom_memory, &ram_memory, guest_info); + pc_memory_init(machine, system_memory, + below_4g_mem_size, above_4g_mem_size, + rom_memory, &ram_memory, guest_info); } else if (machine->kernel_filename != NULL) { /* For xen HVM direct kernel boot, load linux here */ - fw_cfg = xen_load_linux(machine->kernel_filename, - machine->kernel_cmdline, - machine->initrd_filename, - below_4g_mem_size, - guest_info); + xen_load_linux(machine->kernel_filename, + machine->kernel_cmdline, + machine->initrd_filename, + below_4g_mem_size, + guest_info); } gsi_state = g_malloc0(sizeof(*gsi_state)); @@ -220,13 +218,13 @@ static void pc_init1(MachineState *machine) } else if (xen_enabled()) { i8259 = xen_interrupt_controller_init(); } else { - cpu_irq = pc_allocate_cpu_irq(); - i8259 = i8259_init(isa_bus, cpu_irq[0]); + i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); } for (i = 0; i < ISA_NUM_IRQS; i++) { gsi_state->i8259_irq[i] = i8259[i]; } + g_free(i8259); if (pci_enabled) { ioapic_init_gsi(gsi_state, "i440fx"); } @@ -284,11 +282,11 @@ static void pc_init1(MachineState *machine) DeviceState *piix4_pm; I2CBus *smbus; - smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1); + smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); /* TODO: Populate SPD eeprom data. */ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, - gsi[9], *smi_irq, - kvm_enabled(), fw_cfg, &piix4_pm); + gsi[9], smi_irq, + kvm_enabled(), &piix4_pm); smbus_eeprom_init(smbus, 8, NULL, 0); object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 66220b352b..110dfb78a8 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -79,7 +79,6 @@ static void pc_q35_init(MachineState *machine) GSIState *gsi_state; ISABus *isa_bus; int pci_enabled = 1; - qemu_irq *cpu_irq; qemu_irq *gsi; qemu_irq *i8259; int i; @@ -230,8 +229,7 @@ static void pc_q35_init(MachineState *machine) } else if (xen_enabled()) { i8259 = xen_interrupt_controller_init(); } else { - cpu_irq = pc_allocate_cpu_irq(); - i8259 = i8259_init(isa_bus, cpu_irq[0]); + i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); } for (i = 0; i < ISA_NUM_IRQS; i++) { diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 585a27bd6c..dd52d50732 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -51,8 +51,15 @@ static const int debug_macio = 0; #define MACIO_PAGE_SIZE 4096 +/* + * Unaligned DMA read/write access functions required for OS X/Darwin which + * don't perform DMA transactions on sector boundaries. These functions are + * modelled on bdrv_co_do_preadv()/bdrv_co_do_pwritev() and so should be + * easy to remove if the unaligned block APIs are ever exposed. + */ + static void pmac_dma_read(BlockBackend *blk, - int64_t sector_num, int nb_sectors, + int64_t offset, unsigned int bytes, void (*cb)(void *opaque, int ret), void *opaque) { DBDMA_io *io = opaque; @@ -60,76 +67,48 @@ static void pmac_dma_read(BlockBackend *blk, IDEState *s = idebus_active_if(&m->bus); dma_addr_t dma_addr, dma_len; void *mem; - int nsector, remainder; + int64_t sector_num; + int nsector; + uint64_t align = BDRV_SECTOR_SIZE; + size_t head_bytes, tail_bytes; qemu_iovec_destroy(&io->iov); qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - if (io->remainder_len > 0) { - /* Return remainder of request */ - int transfer = MIN(io->remainder_len, io->len); + sector_num = (offset >> 9); + nsector = (io->len >> 9); - MACIO_DPRINTF("--- DMA read pop - bounce addr: %p addr: %" - HWADDR_PRIx " remainder_len: %x\n", - &io->remainder + (0x200 - transfer), io->addr, - io->remainder_len); + MACIO_DPRINTF("--- DMA read transfer (0x%" HWADDR_PRIx ",0x%x): " + "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, + sector_num, nsector); - cpu_physical_memory_write(io->addr, - &io->remainder + (0x200 - transfer), - transfer); + dma_addr = io->addr; + dma_len = io->len; + mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, + DMA_DIRECTION_FROM_DEVICE); - io->remainder_len -= transfer; - io->len -= transfer; - io->addr += transfer; + if (offset & (align - 1)) { + head_bytes = offset & (align - 1); - s->io_buffer_index += transfer; - s->io_buffer_size -= transfer; + MACIO_DPRINTF("--- DMA unaligned head: sector %" PRId64 ", " + "discarding %zu bytes\n", sector_num, head_bytes); - if (io->remainder_len != 0) { - /* Still waiting for remainder */ - return; - } + qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - if (io->len == 0) { - MACIO_DPRINTF("--- finished all read processing; go and finish\n"); - cb(opaque, 0); - return; - } + bytes += offset & (align - 1); + offset = offset & ~(align - 1); } - if (s->drive_kind == IDE_CD) { - sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9); - } else { - sector_num = ide_get_sector(s) + (s->io_buffer_index >> 9); - } + qemu_iovec_add(&io->iov, mem, io->len); - nsector = ((io->len + 0x1ff) >> 9); - remainder = (nsector << 9) - io->len; + if ((offset + bytes) & (align - 1)) { + tail_bytes = (offset + bytes) & (align - 1); - MACIO_DPRINTF("--- DMA read transfer - addr: %" HWADDR_PRIx " len: %x\n", - io->addr, io->len); + MACIO_DPRINTF("--- DMA unaligned tail: sector %" PRId64 ", " + "discarding bytes %zu\n", sector_num, tail_bytes); - dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_FROM_DEVICE); - - if (!remainder) { - MACIO_DPRINTF("--- DMA read aligned - addr: %" HWADDR_PRIx - " len: %x\n", io->addr, io->len); - qemu_iovec_add(&io->iov, mem, io->len); - } else { - MACIO_DPRINTF("--- DMA read unaligned - addr: %" HWADDR_PRIx - " len: %x\n", io->addr, io->len); - qemu_iovec_add(&io->iov, mem, io->len); - - MACIO_DPRINTF("--- DMA read push - bounce addr: %p " - "remainder_len: %x\n", - &io->remainder + 0x200 - remainder, remainder); - qemu_iovec_add(&io->iov, &io->remainder + 0x200 - remainder, - remainder); - - io->remainder_len = remainder; + qemu_iovec_add(&io->iov, &io->tail_remainder, align - tail_bytes); + bytes = ROUND_UP(bytes, align); } s->io_buffer_size -= io->len; @@ -137,15 +116,15 @@ static void pmac_dma_read(BlockBackend *blk, io->len = 0; - MACIO_DPRINTF("--- Block read transfer - sector_num: %"PRIx64" " - "nsector: %x\n", - sector_num, nsector); + MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " " + "nsector: %x\n", (offset >> 9), (bytes >> 9)); - m->aiocb = blk_aio_readv(blk, sector_num, &io->iov, nsector, cb, io); + m->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, (bytes >> 9), + cb, io); } static void pmac_dma_write(BlockBackend *blk, - int64_t sector_num, int nb_sectors, + int64_t offset, int bytes, void (*cb)(void *opaque, int ret), void *opaque) { DBDMA_io *io = opaque; @@ -153,90 +132,80 @@ static void pmac_dma_write(BlockBackend *blk, IDEState *s = idebus_active_if(&m->bus); dma_addr_t dma_addr, dma_len; void *mem; - int nsector, remainder; - int extra = 0; + int64_t sector_num; + int nsector; + uint64_t align = BDRV_SECTOR_SIZE; + size_t head_bytes, tail_bytes; + bool unaligned_head = false, unaligned_tail = false; qemu_iovec_destroy(&io->iov); qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - if (io->remainder_len > 0) { - /* Return remainder of request */ - int transfer = MIN(io->remainder_len, io->len); + sector_num = (offset >> 9); + nsector = (io->len >> 9); - MACIO_DPRINTF("--- processing write remainder %x\n", transfer); - cpu_physical_memory_read(io->addr, - &io->remainder + (0x200 - transfer), - transfer); + MACIO_DPRINTF("--- DMA write transfer (0x%" HWADDR_PRIx ",0x%x): " + "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, + sector_num, nsector); - io->remainder_len -= transfer; - io->len -= transfer; - io->addr += transfer; + dma_addr = io->addr; + dma_len = io->len; + mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, + DMA_DIRECTION_TO_DEVICE); - s->io_buffer_index += transfer; - s->io_buffer_size -= transfer; + if (offset & (align - 1)) { + head_bytes = offset & (align - 1); + sector_num = ((offset & ~(align - 1)) >> 9); - if (io->remainder_len != 0) { - /* Still waiting for remainder */ - return; - } + MACIO_DPRINTF("--- DMA unaligned head: pre-reading head sector %" + PRId64 "\n", sector_num); - MACIO_DPRINTF("--> prepending bounce buffer with size 0x200\n"); + blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align); - /* Sector transfer complete - prepend to request */ - qemu_iovec_add(&io->iov, &io->remainder, 0x200); - extra = 1; - } + qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); + qemu_iovec_add(&io->iov, mem, io->len); - if (s->drive_kind == IDE_CD) { - sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9); - } else { - sector_num = ide_get_sector(s) + (s->io_buffer_index >> 9); + bytes += offset & (align - 1); + offset = offset & ~(align - 1); + + unaligned_head = true; } - nsector = (io->len >> 9); - remainder = io->len - (nsector << 9); + if ((offset + bytes) & (align - 1)) { + tail_bytes = (offset + bytes) & (align - 1); + sector_num = (((offset + bytes) & ~(align - 1)) >> 9); - MACIO_DPRINTF("--- DMA write transfer - addr: %" HWADDR_PRIx " len: %x\n", - io->addr, io->len); - MACIO_DPRINTF("xxx remainder: %x\n", remainder); - MACIO_DPRINTF("xxx sector_num: %"PRIx64" nsector: %x\n", - sector_num, nsector); + MACIO_DPRINTF("--- DMA unaligned tail: pre-reading tail sector %" + PRId64 "\n", sector_num); - dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_TO_DEVICE); + blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align); - if (!remainder) { - MACIO_DPRINTF("--- DMA write aligned - addr: %" HWADDR_PRIx - " len: %x\n", io->addr, io->len); - qemu_iovec_add(&io->iov, mem, io->len); - } else { - /* Write up to last complete sector */ - MACIO_DPRINTF("--- DMA write unaligned - addr: %" HWADDR_PRIx - " len: %x\n", io->addr, (nsector << 9)); - qemu_iovec_add(&io->iov, mem, (nsector << 9)); + if (!unaligned_head) { + qemu_iovec_add(&io->iov, mem, io->len); + } + + qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes, + align - tail_bytes); - MACIO_DPRINTF("--- DMA write read - bounce addr: %p " - "remainder_len: %x\n", &io->remainder, remainder); - cpu_physical_memory_read(io->addr + (nsector << 9), &io->remainder, - remainder); + bytes = ROUND_UP(bytes, align); - io->remainder_len = 0x200 - remainder; + unaligned_tail = true; + } - MACIO_DPRINTF("xxx remainder_len: %x\n", io->remainder_len); + if (!unaligned_head && !unaligned_tail) { + qemu_iovec_add(&io->iov, mem, io->len); } - s->io_buffer_size -= ((nsector + extra) << 9); - s->io_buffer_index += ((nsector + extra) << 9); + s->io_buffer_size -= io->len; + s->io_buffer_index += io->len; io->len = 0; - MACIO_DPRINTF("--- Block write transfer - sector_num: %"PRIx64" " - "nsector: %x\n", sector_num, nsector + extra); + MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " " + "nsector: %x\n", (offset >> 9), (bytes >> 9)); - m->aiocb = blk_aio_writev(blk, sector_num, &io->iov, nsector + extra, cb, - io); + m->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, (bytes >> 9), + cb, io); } static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) @@ -244,19 +213,12 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); - int64_t sector_num; - int nsector, remainder; + int64_t offset; - MACIO_DPRINTF("\ns is %p\n", s); - MACIO_DPRINTF("io_buffer_index: %x\n", s->io_buffer_index); - MACIO_DPRINTF("io_buffer_size: %x packet_transfer_size: %x\n", - s->io_buffer_size, s->packet_transfer_size); - MACIO_DPRINTF("lba: %x\n", s->lba); - MACIO_DPRINTF("io_addr: %" HWADDR_PRIx " io_len: %x\n", io->addr, - io->len); + MACIO_DPRINTF("pmac_ide_atapi_transfer_cb\n"); if (ret < 0) { - MACIO_DPRINTF("THERE WAS AN ERROR! %d\n", ret); + MACIO_DPRINTF("DMA error: %d\n", ret); ide_atapi_io_error(s, ret); goto done; } @@ -270,6 +232,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) } if (s->io_buffer_size <= 0) { + MACIO_DPRINTF("End of IDE transfer\n"); ide_atapi_cmd_ok(s); m->dma_active = false; goto done; @@ -289,19 +252,13 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) goto done; } - /* Calculate number of sectors */ - sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9); - nsector = (io->len + 0x1ff) >> 9; - remainder = io->len & 0x1ff; - - MACIO_DPRINTF("nsector: %d remainder: %x\n", nsector, remainder); - MACIO_DPRINTF("sector: %"PRIx64" %zx\n", sector_num, io->iov.size / 512); + /* Calculate current offset */ + offset = (int64_t)(s->lba << 11) + s->io_buffer_index; - pmac_dma_read(s->blk, sector_num, nsector, pmac_ide_atapi_transfer_cb, io); + pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io); return; done: - MACIO_DPRINTF("done DMA\n\n"); block_acct_done(blk_get_stats(s->blk), &s->acct); io->dma_end(opaque); @@ -313,16 +270,14 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); - int64_t sector_num; - int nsector, remainder; + int64_t offset; MACIO_DPRINTF("pmac_ide_transfer_cb\n"); if (ret < 0) { - MACIO_DPRINTF("DMA error\n"); + MACIO_DPRINTF("DMA error: %d\n", ret); m->aiocb = NULL; ide_dma_error(s); - io->remainder_len = 0; goto done; } @@ -335,7 +290,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) } if (s->io_buffer_size <= 0) { - MACIO_DPRINTF("end of transfer\n"); + MACIO_DPRINTF("End of IDE transfer\n"); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); m->dma_active = false; @@ -348,24 +303,16 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) } /* Calculate number of sectors */ - sector_num = ide_get_sector(s) + (s->io_buffer_index >> 9); - nsector = (io->len + 0x1ff) >> 9; - remainder = io->len & 0x1ff; - - s->nsector -= nsector; - - MACIO_DPRINTF("nsector: %d remainder: %x\n", nsector, remainder); - MACIO_DPRINTF("sector: %"PRIx64" %x\n", sector_num, nsector); + offset = (ide_get_sector(s) << 9) + s->io_buffer_index; switch (s->dma_cmd) { case IDE_DMA_READ: - pmac_dma_read(s->blk, sector_num, nsector, pmac_ide_transfer_cb, io); + pmac_dma_read(s->blk, offset, io->len, pmac_ide_transfer_cb, io); break; case IDE_DMA_WRITE: - pmac_dma_write(s->blk, sector_num, nsector, pmac_ide_transfer_cb, io); + pmac_dma_write(s->blk, offset, io->len, pmac_ide_transfer_cb, io); break; case IDE_DMA_TRIM: - MACIO_DPRINTF("TRIM command issued!"); break; } @@ -561,15 +508,12 @@ static void ide_dbdma_start(IDEDMA *dma, IDEState *s, BlockCompletionFunc *cb) { MACIOIDEState *m = container_of(dma, MACIOIDEState, dma); - DBDMAState *dbdma = m->dbdma; - DBDMA_io *io; - int i; s->io_buffer_index = 0; if (s->drive_kind == IDE_CD) { s->io_buffer_size = s->packet_transfer_size; } else { - s->io_buffer_size = s->nsector * 0x200; + s->io_buffer_size = s->nsector * BDRV_SECTOR_SIZE; } MACIO_DPRINTF("\n\n------------ IDE transfer\n"); @@ -578,15 +522,6 @@ static void ide_dbdma_start(IDEDMA *dma, IDEState *s, MACIO_DPRINTF("lba: %x size: %x\n", s->lba, s->io_buffer_size); MACIO_DPRINTF("-------------------------\n"); - for (i = 0; i < DBDMA_CHANNELS; i++) { - io = &dbdma->channels[i].io; - - if (io->opaque == m) { - io->remainder_len = 0; - } - } - - MACIO_DPRINTF("\n"); m->dma_active = true; DBDMA_kick(m->dbdma); } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 1b3d1c12ad..4b5e32dcbe 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -452,8 +452,6 @@ static const struct IDEDMAOps bmdma_ops = { void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d) { - qemu_irq *irq; - if (bus->dma == &bm->dma) { return; } @@ -461,8 +459,7 @@ void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d) bm->dma.ops = &bmdma_ops; bus->dma = &bm->dma; bm->irq = bus->irq; - irq = qemu_allocate_irqs(bmdma_irq, bm, 1); - bus->irq = *irq; + bus->irq = qemu_allocate_irq(bmdma_irq, bm, 0); bm->pci_dev = d; } diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 843864a3ef..092d8a80ac 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -11,6 +11,7 @@ common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o common-obj-$(CONFIG_IOAPIC) += ioapic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic.o +common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o common-obj-$(CONFIG_OPENPIC) += openpic.o obj-$(CONFIG_APIC) += apic.o apic_common.o diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 0f97b47925..77b639cce8 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -370,13 +370,14 @@ static int apic_irq_pending(APICCommonState *s) static void apic_update_irq(APICCommonState *s) { CPUState *cpu; + DeviceState *dev = (DeviceState *)s; cpu = CPU(s->cpu); if (!qemu_cpu_is_self(cpu)) { cpu_interrupt(cpu, CPU_INTERRUPT_POLL); } else if (apic_irq_pending(s) > 0) { cpu_interrupt(cpu, CPU_INTERRUPT_HARD); - } else if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) { + } else if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) { cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); } } @@ -549,10 +550,12 @@ static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode, static bool apic_check_pic(APICCommonState *s) { - if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) { + DeviceState *dev = (DeviceState *)s; + + if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) { return false; } - apic_deliver_pic_intr(&s->busdev.qdev, 1); + apic_deliver_pic_intr(dev, 1); return true; } diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c new file mode 100644 index 0000000000..43d1976c49 --- /dev/null +++ b/hw/intc/arm_gicv2m.c @@ -0,0 +1,192 @@ +/* + * GICv2m extension for MSI/MSI-x support with a GICv2-based system + * + * Copyright (C) 2015 Linaro, All rights reserved. + * + * Author: Christoffer Dall <christoffer.dall@linaro.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +/* This file implements an emulated GICv2m widget as described in the ARM + * Server Base System Architecture (SBSA) specification Version 2.2 + * (ARM-DEN-0029 v2.2) pages 35-39 without any optional implementation defined + * identification registers and with a single non-secure MSI register frame. + */ + +#include "hw/sysbus.h" +#include "hw/pci/msi.h" + +#define TYPE_ARM_GICV2M "arm-gicv2m" +#define ARM_GICV2M(obj) OBJECT_CHECK(ARMGICv2mState, (obj), TYPE_ARM_GICV2M) + +#define GICV2M_NUM_SPI_MAX 128 + +#define V2M_MSI_TYPER 0x008 +#define V2M_MSI_SETSPI_NS 0x040 +#define V2M_MSI_IIDR 0xFCC +#define V2M_IIDR0 0xFD0 +#define V2M_IIDR11 0xFFC + +#define PRODUCT_ID_QEMU 0x51 /* ASCII code Q */ + +typedef struct ARMGICv2mState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq spi[GICV2M_NUM_SPI_MAX]; + + uint32_t base_spi; + uint32_t num_spi; +} ARMGICv2mState; + +static void gicv2m_set_irq(void *opaque, int irq) +{ + ARMGICv2mState *s = (ARMGICv2mState *)opaque; + + qemu_irq_pulse(s->spi[irq]); +} + +static uint64_t gicv2m_read(void *opaque, hwaddr offset, + unsigned size) +{ + ARMGICv2mState *s = (ARMGICv2mState *)opaque; + uint32_t val; + + if (size != 4) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_read: bad size %u\n", size); + return 0; + } + + switch (offset) { + case V2M_MSI_TYPER: + val = (s->base_spi + 32) << 16; + val |= s->num_spi; + return val; + case V2M_MSI_IIDR: + /* We don't have any valid implementor so we leave that field as zero + * and we return 0 in the arch revision as per the spec. + */ + return (PRODUCT_ID_QEMU << 20); + case V2M_IIDR0 ... V2M_IIDR11: + /* We do not implement any optional identification registers and the + * mandatory MSI_PIDR2 register reads as 0x0, so we capture all + * implementation defined registers here. + */ + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "gicv2m_read: Bad offset %x\n", (int)offset); + return 0; + } +} + +static void gicv2m_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + ARMGICv2mState *s = (ARMGICv2mState *)opaque; + + if (size != 2 && size != 4) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_write: bad size %u\n", size); + return; + } + + switch (offset) { + case V2M_MSI_SETSPI_NS: { + int spi; + + spi = (value & 0x3ff) - (s->base_spi + 32); + if (spi >= 0 && spi < s->num_spi) { + gicv2m_set_irq(s, spi); + } + return; + } + default: + qemu_log_mask(LOG_GUEST_ERROR, + "gicv2m_write: Bad offset %x\n", (int)offset); + } +} + +static const MemoryRegionOps gicv2m_ops = { + .read = gicv2m_read, + .write = gicv2m_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void gicv2m_realize(DeviceState *dev, Error **errp) +{ + ARMGICv2mState *s = ARM_GICV2M(dev); + int i; + + if (s->num_spi > GICV2M_NUM_SPI_MAX) { + error_setg(errp, + "requested %u SPIs exceeds GICv2m frame maximum %d", + s->num_spi, GICV2M_NUM_SPI_MAX); + return; + } + + if (s->base_spi + 32 > 1020 - s->num_spi) { + error_setg(errp, + "requested base SPI %u+%u exceeds max. number 1020", + s->base_spi + 32, s->num_spi); + return; + } + + for (i = 0; i < s->num_spi; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]); + } + + msi_supported = true; + kvm_gsi_direct_mapping = true; + kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); +} + +static void gicv2m_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARMGICv2mState *s = ARM_GICV2M(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &gicv2m_ops, s, + "gicv2m", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static Property gicv2m_properties[] = { + DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0), + DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64), + DEFINE_PROP_END_OF_LIST(), +}; + +static void gicv2m_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = gicv2m_properties; + dc->realize = gicv2m_realize; +} + +static const TypeInfo gicv2m_info = { + .name = TYPE_ARM_GICV2M, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMGICv2mState), + .instance_init = gicv2m_init, + .class_init = gicv2m_class_init, +}; + +static void gicv2m_register_types(void) +{ + type_register_static(&gicv2m_info); +} + +type_init(gicv2m_register_types) diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index 0590d5dfb8..b2a4950bc3 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -213,9 +213,6 @@ void exynos4210_init_board_irqs(Exynos4210Irq *s) uint32_t grp, bit, irq_id, n; for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) { - s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], - s->ext_combiner_irq[n]); - irq_id = 0; if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) || n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) { @@ -230,8 +227,10 @@ void exynos4210_init_board_irqs(Exynos4210Irq *s) if (irq_id) { s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], s->ext_gic_irq[irq_id-32]); + } else { + s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], + s->ext_combiner_irq[n]); } - } for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) { /* these IDs are passed to Internal Combiner and External GIC */ diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index 9da9dfc4da..fcf97d86ac 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -65,7 +65,6 @@ static void i82378_realize(PCIDevice *pci, Error **errp) uint8_t *pci_conf; ISABus *isabus; ISADevice *isa; - qemu_irq *out0_irq; pci_conf = pci->config; pci_set_word(pci_conf + PCI_COMMAND, @@ -88,11 +87,9 @@ static void i82378_realize(PCIDevice *pci, Error **errp) All devices accept byte access only, except timer */ - /* Workaround the fact that i8259 is not qdev'ified... */ - out0_irq = qemu_allocate_irqs(i82378_request_out0_irq, s, 1); - /* 2 82C59 (irq) */ - s->i8259 = i8259_init(isabus, *out0_irq); + s->i8259 = i8259_init(isabus, + qemu_allocate_irq(i82378_request_out0_irq, s, 0)); isa_bus_irqs(isabus, s->i8259); /* 1 82C54 (pit) */ diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 825aa627df..ec8e7decfc 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -21,6 +21,7 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/isa/isa.h" +#include "hw/i386/pc.h" static ISABus *isabus; @@ -267,3 +268,28 @@ MemoryRegion *isa_address_space_io(ISADevice *dev) } type_init(isabus_register_types) + +static void parallel_init(ISABus *bus, int index, CharDriverState *chr) +{ + DeviceState *dev; + ISADevice *isadev; + + isadev = isa_create(bus, "isa-parallel"); + dev = DEVICE(isadev); + qdev_prop_set_uint32(dev, "index", index); + qdev_prop_set_chr(dev, "chardev", chr); + qdev_init_nofail(dev); +} + +void parallel_hds_isa_init(ISABus *bus, int n) +{ + int i; + + assert(n <= MAX_PARALLEL_PORTS); + + for (i = 0; i < n; i++) { + if (parallel_hds[i]) { + parallel_init(bus, i, parallel_hds[i]); + } + } +} diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index dba758595f..71a9f7a716 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -360,11 +360,8 @@ static void ich9_set_sci(void *opaque, int irq_num, int level) void ich9_lpc_pm_init(PCIDevice *lpc_pci) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci); - qemu_irq *sci_irq; - - sci_irq = qemu_allocate_irqs(ich9_set_sci, lpc, 1); - ich9_pm_init(lpc_pci, &lpc->pm, sci_irq[0]); + ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0)); ich9_lpc_reset(&lpc->d.qdev); } @@ -410,12 +407,28 @@ static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old) } } +/* config:GEN_PMCON* */ +static void +ich9_lpc_pmcon_update(ICH9LPCState *lpc) +{ + uint16_t gen_pmcon_1 = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1); + uint16_t wmask; + + if (gen_pmcon_1 & ICH9_LPC_GEN_PMCON_1_SMI_LOCK) { + wmask = pci_get_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1); + wmask &= ~ICH9_LPC_GEN_PMCON_1_SMI_LOCK; + pci_set_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1, wmask); + lpc->pm.smi_en_wmask &= ~1; + } +} + static int ich9_lpc_post_load(void *opaque, int version_id) { ICH9LPCState *lpc = opaque; ich9_lpc_pmbase_update(lpc); ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */); + ich9_lpc_pmcon_update(lpc); return 0; } @@ -438,6 +451,9 @@ static void ich9_lpc_config_write(PCIDevice *d, if (ranges_overlap(addr, len, ICH9_LPC_PIRQE_ROUT, 4)) { pci_bus_fire_intx_routing_notifier(lpc->d.bus); } + if (ranges_overlap(addr, len, ICH9_LPC_GEN_PMCON_1, 8)) { + ich9_lpc_pmcon_update(lpc); + } } static void ich9_lpc_reset(DeviceState *qdev) @@ -494,7 +510,7 @@ static void ich9_lpc_machine_ready(Notifier *n, void *opaque) /* lpt */ pci_conf[0x82] |= 0x04; } - if (memory_region_present(io_as, 0x3f0)) { + if (memory_region_present(io_as, 0x3f2)) { /* floppy */ pci_conf[0x82] |= 0x08; } diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index b8197b115d..b2ba870427 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -356,7 +356,7 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp) acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io); acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_cnt_init(&s->ar, &s->io, 2); + acpi_pm1_cnt_init(&s->ar, &s->io, false, false, 2); } I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index 14d0efcdd9..70f48d3b1d 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -78,7 +78,7 @@ static void lm32_evr_init(MachineState *machine) DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - qemu_irq *cpu_irq, irq[32]; + qemu_irq irq[32]; ResetInfo *reset_info; int i; @@ -123,8 +123,7 @@ static void lm32_evr_init(MachineState *machine) 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); /* create irq lines */ - cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1); - env->pic_state = lm32_pic_init(*cpu_irq); + env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0)); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(env->pic_state, i); } @@ -173,7 +172,7 @@ static void lm32_uclinux_init(MachineState *machine) DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - qemu_irq *cpu_irq, irq[32]; + qemu_irq irq[32]; HWSetup *hw; ResetInfo *reset_info; int i; @@ -225,8 +224,7 @@ static void lm32_uclinux_init(MachineState *machine) 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); /* create irq lines */ - cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1); - env->pic_state = lm32_pic_init(*cpu_irq); + env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, env, 0)); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(env->pic_state, i); } diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index e0cec7dc41..e755f5b24f 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -86,7 +86,7 @@ milkymist_init(MachineState *machine) DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *phys_sdram = g_new(MemoryRegion, 1); - qemu_irq irq[32], *cpu_irq; + qemu_irq irq[32]; int i; char *bios_filename; ResetInfo *reset_info; @@ -130,8 +130,7 @@ milkymist_init(MachineState *machine) 2, 0x00, 0x89, 0x00, 0x1d, 1); /* create irq lines */ - cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1); - env->pic_state = lm32_pic_init(*cpu_irq); + env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0)); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(env->pic_state, i); } diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 39f0c97c0c..e70633d29f 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -211,7 +211,6 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, uint64_t address_space_end = address_space_start + address_space_size; g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); - g_assert(QEMU_ALIGN_UP(address_space_size, align) == address_space_size); if (!address_space_size) { error_setg(errp, "memory hotplug is not enabled, " diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 482250d85d..5140882c00 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1161,7 +1161,7 @@ void mips_malta_init(MachineState *machine) pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1); pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci"); smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, - isa_get_irq(NULL, 9), NULL, 0, NULL, NULL); + isa_get_irq(NULL, 9), NULL, 0, NULL); smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); g_free(smbus_eeprom_buf); pit = pit_init(isa_bus, 0x40, 0, NULL); diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 063ad80412..e9037b0c39 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -126,17 +126,18 @@ static void macio_bar_setup(MacIOState *macio_state) } } -static int macio_common_initfn(PCIDevice *d) +static void macio_common_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); SysBusDevice *sysbus_dev; - int ret; + Error *err = NULL; d->config[0x3d] = 0x01; // interrupt on pin 1 - ret = qdev_init(DEVICE(&s->cuda)); - if (ret < 0) { - return ret; + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); memory_region_add_subregion(&s->bar, 0x16000, @@ -144,12 +145,11 @@ static int macio_common_initfn(PCIDevice *d) macio_bar_setup(s); pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); - - return 0; } -static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0, - qemu_irq irq1, int dmaid) +static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide, + qemu_irq irq0, qemu_irq irq1, int dmaid, + Error **errp) { SysBusDevice *sysbus_dev; @@ -157,27 +157,31 @@ static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0, sysbus_connect_irq(sysbus_dev, 0, irq0); sysbus_connect_irq(sysbus_dev, 1, irq1); macio_ide_register_dma(ide, s->dbdma, dmaid); - return qdev_init(DEVICE(ide)); + object_property_set_bool(OBJECT(ide), true, "realized", errp); } -static int macio_oldworld_initfn(PCIDevice *d) +static void macio_oldworld_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); OldWorldMacIOState *os = OLDWORLD_MACIO(d); + Error *err = NULL; SysBusDevice *sysbus_dev; int i; int cur_irq = 0; - int ret = macio_common_initfn(d); - if (ret < 0) { - return ret; + + macio_common_realize(d, &err); + if (err) { + error_propagate(errp, err); + return; } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); - ret = qdev_init(DEVICE(&os->nvram)); - if (ret < 0) { - return ret; + object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } sysbus_dev = SYS_BUS_DEVICE(&os->nvram); memory_region_add_subregion(&s->bar, 0x60000, @@ -194,13 +198,12 @@ static int macio_oldworld_initfn(PCIDevice *d) qemu_irq irq0 = os->irqs[cur_irq++]; qemu_irq irq1 = os->irqs[cur_irq++]; - ret = macio_initfn_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4)); - if (ret < 0) { - return ret; + macio_realize_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4), &err); + if (err) { + error_propagate(errp, err); + return; } } - - return 0; } static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size, @@ -268,17 +271,20 @@ static const MemoryRegionOps timer_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int macio_newworld_initfn(PCIDevice *d) +static void macio_newworld_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); + Error *err = NULL; SysBusDevice *sysbus_dev; MemoryRegion *timer_memory = NULL; int i; int cur_irq = 0; - int ret = macio_common_initfn(d); - if (ret < 0) { - return ret; + + macio_common_realize(d, &err); + if (err) { + error_propagate(errp, err); + return; } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); @@ -294,9 +300,10 @@ static int macio_newworld_initfn(PCIDevice *d) qemu_irq irq0 = ns->irqs[cur_irq++]; qemu_irq irq1 = ns->irqs[cur_irq++]; - ret = macio_initfn_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4)); - if (ret < 0) { - return ret; + macio_realize_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4), &err); + if (err) { + error_propagate(errp, err); + return; } } @@ -305,8 +312,6 @@ static int macio_newworld_initfn(PCIDevice *d) memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", 0x1000); memory_region_add_subregion(&s->bar, 0x15000, timer_memory); - - return 0; } static void macio_newworld_init(Object *obj) @@ -352,7 +357,7 @@ static void macio_oldworld_class_init(ObjectClass *oc, void *data) PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - pdc->init = macio_oldworld_initfn; + pdc->realize = macio_oldworld_realize; pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201; dc->vmsd = &vmstate_macio_oldworld; } @@ -372,7 +377,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - pdc->init = macio_newworld_initfn; + pdc->realize = macio_newworld_realize; pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; dc->vmsd = &vmstate_macio_newworld; } diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index dafe91421b..494a346cf6 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -155,7 +155,7 @@ #define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */ #define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */ -#define GEM_DMACFG_RBUFSZ_M 0x007F0000 /* DMA RX Buffer Size mask */ +#define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */ #define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */ #define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ #define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */ diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 012ab7fae8..0d3bf0fb89 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -511,7 +511,7 @@ static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) return virtio_net_guest_offloads_by_features(vdev->guest_features); } -static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) +static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) { VirtIONet *n = VIRTIO_NET(vdev); int i; diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs index 96c596eb31..f2adfe348c 100644 --- a/hw/pci-bridge/Makefile.objs +++ b/hw/pci-bridge/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += pci_bridge_dev.o +common-obj-y += pci_expander_bridge.o common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o common-obj-$(CONFIG_IOH3420) += ioh3420.o common-obj-$(CONFIG_I82801B11) += i82801b11.o diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c new file mode 100644 index 0000000000..ec2bb458f7 --- /dev/null +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -0,0 +1,231 @@ +/* + * PCI Expander Bridge Device Emulation + * + * Copyright (C) 2015 Red Hat Inc + * + * Authors: + * Marcel Apfelbaum <marcel@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci_bus.h" +#include "hw/i386/pc.h" +#include "qemu/range.h" +#include "qemu/error-report.h" +#include "sysemu/numa.h" + +#define TYPE_PXB_BUS "pxb-bus" +#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS) + +typedef struct PXBBus { + /*< private >*/ + PCIBus parent_obj; + /*< public >*/ + + char bus_path[8]; +} PXBBus; + +#define TYPE_PXB_DEVICE "pxb" +#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE) + +typedef struct PXBDev { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + uint8_t bus_nr; + uint16_t numa_node; +} PXBDev; + +#define TYPE_PXB_HOST "pxb-host" + +static int pxb_bus_num(PCIBus *bus) +{ + PXBDev *pxb = PXB_DEV(bus->parent_dev); + + return pxb->bus_nr; +} + +static bool pxb_is_root(PCIBus *bus) +{ + return true; /* by definition */ +} + +static uint16_t pxb_bus_numa_node(PCIBus *bus) +{ + PXBDev *pxb = PXB_DEV(bus->parent_dev); + + return pxb->numa_node; +} + +static void pxb_bus_class_init(ObjectClass *class, void *data) +{ + PCIBusClass *pbc = PCI_BUS_CLASS(class); + + pbc->bus_num = pxb_bus_num; + pbc->is_root = pxb_is_root; + pbc->numa_node = pxb_bus_numa_node; +} + +static const TypeInfo pxb_bus_info = { + .name = TYPE_PXB_BUS, + .parent = TYPE_PCI_BUS, + .instance_size = sizeof(PXBBus), + .class_init = pxb_bus_class_init, +}; + +static const char *pxb_host_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + PXBBus *bus = PXB_BUS(rootbus); + + snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus)); + return bus->bus_path; +} + +static void pxb_host_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); + + dc->fw_name = "pci"; + hc->root_bus_path = pxb_host_root_bus_path; +} + +static const TypeInfo pxb_host_info = { + .name = TYPE_PXB_HOST, + .parent = TYPE_PCI_HOST_BRIDGE, + .class_init = pxb_host_class_init, +}; + +/* + * Registers the PXB bus as a child of the i440fx root bus. + * + * Returns 0 on successs, -1 if i440fx host was not + * found or the bus number is already in use. + */ +static int pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus) +{ + PCIBus *bus = dev->bus; + int pxb_bus_num = pci_bus_num(pxb_bus); + + if (bus->parent_dev) { + error_report("PXB devices can be attached only to root bus."); + return -1; + } + + QLIST_FOREACH(bus, &bus->child, sibling) { + if (pci_bus_num(bus) == pxb_bus_num) { + error_report("Bus %d is already in use.", pxb_bus_num); + return -1; + } + } + QLIST_INSERT_HEAD(&dev->bus->child, pxb_bus, sibling); + + return 0; +} + +static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) +{ + PCIDevice *pxb = pci_dev->bus->parent_dev; + + /* + * The bios does not index the pxb slot number when + * it computes the IRQ because it resides on bus 0 + * and not on the current bus. + * However QEMU routes the irq through bus 0 and adds + * the pxb slot to the IRQ computation of the PXB + * device. + * + * Synchronize between bios and QEMU by canceling + * pxb's effect. + */ + return pin - PCI_SLOT(pxb->devfn); +} + +static int pxb_dev_initfn(PCIDevice *dev) +{ + PXBDev *pxb = PXB_DEV(dev); + DeviceState *ds, *bds; + PCIBus *bus; + const char *dev_name = NULL; + + if (pxb->numa_node != NUMA_NODE_UNASSIGNED && + pxb->numa_node >= nb_numa_nodes) { + error_report("Illegal numa node %d.", pxb->numa_node); + return -EINVAL; + } + + if (dev->qdev.id && *dev->qdev.id) { + dev_name = dev->qdev.id; + } + + ds = qdev_create(NULL, TYPE_PXB_HOST); + bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + + bus->parent_dev = dev; + bus->address_space_mem = dev->bus->address_space_mem; + bus->address_space_io = dev->bus->address_space_io; + bus->map_irq = pxb_map_irq_fn; + + bds = qdev_create(BUS(bus), "pci-bridge"); + bds->id = dev_name; + qdev_prop_set_uint8(bds, "chassis_nr", pxb->bus_nr); + + PCI_HOST_BRIDGE(ds)->bus = bus; + + if (pxb_register_bus(dev, bus)) { + return -EINVAL; + } + + qdev_init_nofail(ds); + qdev_init_nofail(bds); + + pci_word_test_and_set_mask(dev->config + PCI_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); + pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); + + return 0; +} + +static Property pxb_dev_properties[] = { + /* Note: 0 is not a legal a PXB bus number. */ + DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), + DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pxb_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = pxb_dev_initfn; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_PXB; + k->class_id = PCI_CLASS_BRIDGE_HOST; + + dc->desc = "PCI Expander Bridge"; + dc->props = pxb_dev_properties; +} + +static const TypeInfo pxb_dev_info = { + .name = TYPE_PXB_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PXBDev), + .class_init = pxb_dev_class_init, +}; + +static void pxb_register_types(void) +{ + type_register_static(&pxb_bus_info); + type_register_static(&pxb_host_info); + type_register_static(&pxb_dev_info); +} + +type_init(pxb_register_types) diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c index 8272de3f28..17d826cba5 100644 --- a/hw/pci-host/pam.c +++ b/hw/pci-host/pam.c @@ -31,26 +31,6 @@ #include "sysemu/sysemu.h" #include "hw/pci-host/pam.h" -void smram_update(MemoryRegion *smram_region, uint8_t smram, - uint8_t smm_enabled) -{ - bool smram_enabled; - - smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) || - (smram & SMRAM_D_OPEN)); - memory_region_set_enabled(smram_region, !smram_enabled); -} - -void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram, - MemoryRegion *smram_region) -{ - uint8_t smm_enabled = (smm != 0); - if (*host_smm_enabled != smm_enabled) { - *host_smm_enabled = smm_enabled; - smram_update(smram_region, smram, *host_smm_enabled); - } -} - void init_pam(DeviceState *dev, MemoryRegion *ram_memory, MemoryRegion *system_memory, MemoryRegion *pci_address_space, PAMMemoryRegion *mem, uint32_t start, uint32_t size) diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 723836fb0e..f1712b86fe 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -105,7 +105,7 @@ struct PCII440FXState { MemoryRegion *ram_memory; PAMMemoryRegion pam_regions[13]; MemoryRegion smram_region; - uint8_t smm_enabled; + MemoryRegion smram, low_smram; }; @@ -138,18 +138,10 @@ static void i440fx_update_memory_mappings(PCII440FXState *d) pam_update(&d->pam_regions[i], i, pd->config[I440FX_PAM + ((i + 1) / 2)]); } - smram_update(&d->smram_region, pd->config[I440FX_SMRAM], d->smm_enabled); - memory_region_transaction_commit(); -} - -static void i440fx_set_smm(int val, void *arg) -{ - PCII440FXState *d = arg; - PCIDevice *pd = PCI_DEVICE(d); - - memory_region_transaction_begin(); - smram_set_smm(&d->smm_enabled, val, pd->config[I440FX_SMRAM], - &d->smram_region); + memory_region_set_enabled(&d->smram_region, + !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN)); + memory_region_set_enabled(&d->smram, + pd->config[I440FX_SMRAM] & SMRAM_G_SMRAME); memory_region_transaction_commit(); } @@ -172,12 +164,13 @@ static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) PCII440FXState *d = opaque; PCIDevice *pd = PCI_DEVICE(d); int ret, i; + uint8_t smm_enabled; ret = pci_device_load(pd, f); if (ret < 0) return ret; i440fx_update_memory_mappings(d); - qemu_get_8s(f, &d->smm_enabled); + qemu_get_8s(f, &smm_enabled); if (version_id == 2) { for (i = 0; i < PIIX_NUM_PIRQS; i++) { @@ -205,7 +198,10 @@ static const VMStateDescription vmstate_i440fx = { .post_load = i440fx_post_load, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState), - VMSTATE_UINT8(smm_enabled, PCII440FXState), + /* Used to be smm_enabled, which was basically always zero because + * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. + */ + VMSTATE_UNUSED(1), VMSTATE_END_OF_LIST() } }; @@ -297,11 +293,7 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) static void i440fx_realize(PCIDevice *dev, Error **errp) { - PCII440FXState *d = I440FX_PCI_DEVICE(dev); - dev->config[I440FX_SMRAM] = 0x02; - - cpu_smm_register(&i440fx_set_smm, d); } PCIBus *i440fx_init(PCII440FXState **pi440fx_state, @@ -346,11 +338,23 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, pc_pci_as_mapping_init(OBJECT(f), f->system_memory, f->pci_address_space); + /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region", f->pci_address_space, 0xa0000, 0x20000); memory_region_add_subregion_overlap(f->system_memory, 0xa0000, &f->smram_region, 1); - memory_region_set_enabled(&f->smram_region, false); + memory_region_set_enabled(&f->smram_region, true); + + /* smram, as seen by SMM CPUs */ + memory_region_init(&f->smram, OBJECT(d), "smram", 1ull << 32); + memory_region_set_enabled(&f->smram, true); + memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low", + f->ram_memory, 0xa0000, 0x20000); + memory_region_set_enabled(&f->low_smram, true); + memory_region_add_subregion(&f->smram, 0xa0000, &f->low_smram); + object_property_add_const_link(qdev_get_machine(), "smram", + OBJECT(&f->smram), &error_abort); + init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); for (i = 0; i < 12; ++i) { diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index c8827cc000..bd7409456f 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -198,6 +198,28 @@ static const TypeInfo q35_host_info = { * MCH D0:F0 */ +static uint64_t tseg_blackhole_read(void *ptr, hwaddr reg, unsigned size) +{ + return 0xffffffff; +} + +static void tseg_blackhole_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + /* nothing */ +} + +static const MemoryRegionOps tseg_blackhole_ops = { + .read = tseg_blackhole_read, + .write = tseg_blackhole_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + /* PCIe MMCFG */ static void mch_update_pciexbar(MCHPCIState *mch) { @@ -266,21 +288,70 @@ static void mch_update_pam(MCHPCIState *mch) static void mch_update_smram(MCHPCIState *mch) { PCIDevice *pd = PCI_DEVICE(mch); + bool h_smrame = (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME); + uint32_t tseg_size; + + /* implement SMRAM.D_LCK */ + if (pd->config[MCH_HOST_BRIDGE_SMRAM] & MCH_HOST_BRIDGE_SMRAM_D_LCK) { + pd->config[MCH_HOST_BRIDGE_SMRAM] &= ~MCH_HOST_BRIDGE_SMRAM_D_OPEN; + pd->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK_LCK; + pd->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK_LCK; + } memory_region_transaction_begin(); - smram_update(&mch->smram_region, pd->config[MCH_HOST_BRIDGE_SMRAM], - mch->smm_enabled); - memory_region_transaction_commit(); -} -static void mch_set_smm(int smm, void *arg) -{ - MCHPCIState *mch = arg; - PCIDevice *pd = PCI_DEVICE(mch); + if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_D_OPEN) { + /* Hide (!) low SMRAM if H_SMRAME = 1 */ + memory_region_set_enabled(&mch->smram_region, h_smrame); + /* Show high SMRAM if H_SMRAME = 1 */ + memory_region_set_enabled(&mch->open_high_smram, h_smrame); + } else { + /* Hide high SMRAM and low SMRAM */ + memory_region_set_enabled(&mch->smram_region, true); + memory_region_set_enabled(&mch->open_high_smram, false); + } + + if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME) { + memory_region_set_enabled(&mch->low_smram, !h_smrame); + memory_region_set_enabled(&mch->high_smram, h_smrame); + } else { + memory_region_set_enabled(&mch->low_smram, false); + memory_region_set_enabled(&mch->high_smram, false); + } + + if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) { + switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & + MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { + case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB: + tseg_size = 1024 * 1024; + break; + case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB: + tseg_size = 1024 * 1024 * 2; + break; + case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB: + tseg_size = 1024 * 1024 * 8; + break; + default: + tseg_size = 0; + break; + } + } else { + tseg_size = 0; + } + memory_region_del_subregion(mch->system_memory, &mch->tseg_blackhole); + memory_region_set_enabled(&mch->tseg_blackhole, tseg_size); + memory_region_set_size(&mch->tseg_blackhole, tseg_size); + memory_region_add_subregion_overlap(mch->system_memory, + mch->below_4g_mem_size - tseg_size, + &mch->tseg_blackhole, 1); + + memory_region_set_enabled(&mch->tseg_window, tseg_size); + memory_region_set_size(&mch->tseg_window, tseg_size); + memory_region_set_address(&mch->tseg_window, + mch->below_4g_mem_size - tseg_size); + memory_region_set_alias_offset(&mch->tseg_window, + mch->below_4g_mem_size - tseg_size); - memory_region_transaction_begin(); - smram_set_smm(&mch->smm_enabled, smm, pd->config[MCH_HOST_BRIDGE_SMRAM], - &mch->smram_region); memory_region_transaction_commit(); } @@ -289,7 +360,6 @@ static void mch_write_config(PCIDevice *d, { MCHPCIState *mch = MCH_PCI_DEVICE(d); - /* XXX: implement SMRAM.D_LOCK */ pci_default_write_config(d, address, val, len); if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PAM0, @@ -329,7 +399,10 @@ static const VMStateDescription vmstate_mch = { .post_load = mch_post_load, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, MCHPCIState), - VMSTATE_UINT8(smm_enabled, MCHPCIState), + /* Used to be smm_enabled, which was basically always zero because + * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. + */ + VMSTATE_UNUSED(1), VMSTATE_END_OF_LIST() } }; @@ -343,6 +416,9 @@ static void mch_reset(DeviceState *qdev) MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT); d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; + d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT; + d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK; + d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK; mch_update(mch); } @@ -399,13 +475,47 @@ static void mch_realize(PCIDevice *d, Error **errp) pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory, mch->pci_address_space); - /* smram */ - cpu_smm_register(&mch_set_smm, mch); + /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", mch->pci_address_space, 0xa0000, 0x20000); memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, &mch->smram_region, 1); - memory_region_set_enabled(&mch->smram_region, false); + memory_region_set_enabled(&mch->smram_region, true); + + memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", + mch->ram_memory, 0xa0000, 0x20000); + memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000, + &mch->open_high_smram, 1); + memory_region_set_enabled(&mch->open_high_smram, false); + + /* smram, as seen by SMM CPUs */ + memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32); + memory_region_set_enabled(&mch->smram, true); + memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low", + mch->ram_memory, 0xa0000, 0x20000); + memory_region_set_enabled(&mch->low_smram, true); + memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram); + memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high", + mch->ram_memory, 0xa0000, 0x20000); + memory_region_set_enabled(&mch->high_smram, true); + memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); + + memory_region_init_io(&mch->tseg_blackhole, OBJECT(mch), + &tseg_blackhole_ops, NULL, + "tseg-blackhole", 0); + memory_region_set_enabled(&mch->tseg_blackhole, false); + memory_region_add_subregion_overlap(mch->system_memory, + mch->below_4g_mem_size, + &mch->tseg_blackhole, 1); + + memory_region_init_alias(&mch->tseg_window, OBJECT(mch), "tseg-window", + mch->ram_memory, mch->below_4g_mem_size, 0); + memory_region_set_enabled(&mch->tseg_window, false); + memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size, + &mch->tseg_window); + object_property_add_const_link(qdev_get_machine(), "smram", + OBJECT(&mch->smram), &error_abort); + init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory, mch->pci_address_space, &mch->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); 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/pci.c b/hw/pci/pci.c index 48f19a306d..750f3dacea 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -88,9 +88,28 @@ static void pci_bus_unrealize(BusState *qbus, Error **errp) vmstate_unregister(NULL, &vmstate_pcibus, bus); } +static bool pcibus_is_root(PCIBus *bus) +{ + return !bus->parent_dev; +} + +static int pcibus_num(PCIBus *bus) +{ + if (pcibus_is_root(bus)) { + return 0; /* pci host bridge */ + } + return bus->parent_dev->config[PCI_SECONDARY_BUS]; +} + +static uint16_t pcibus_numa_node(PCIBus *bus) +{ + return NUMA_NODE_UNASSIGNED; +} + static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + PCIBusClass *pbc = PCI_BUS_CLASS(klass); k->print_dev = pcibus_dev_print; k->get_dev_path = pcibus_get_dev_path; @@ -98,12 +117,17 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) k->realize = pci_bus_realize; k->unrealize = pci_bus_unrealize; k->reset = pcibus_reset; + + pbc->is_root = pcibus_is_root; + pbc->bus_num = pcibus_num; + pbc->numa_node = pcibus_numa_node; } static const TypeInfo pci_bus_info = { .name = TYPE_PCI_BUS, .parent = TYPE_BUS, .instance_size = sizeof(PCIBus), + .class_size = sizeof(PCIBusClass), .class_init = pci_bus_class_init, }; @@ -123,7 +147,7 @@ static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU; static QLIST_HEAD(, PCIHostState) pci_host_bridges; -static int pci_bar(PCIDevice *d, int reg) +int pci_bar(PCIDevice *d, int reg) { uint8_t type; @@ -278,7 +302,10 @@ PCIBus *pci_device_root_bus(const PCIDevice *d) { PCIBus *bus = d->bus; - while ((d = bus->parent_dev) != NULL) { + while (!pci_bus_is_root(bus)) { + d = bus->parent_dev; + assert(d != NULL); + bus = d->bus; } @@ -291,7 +318,6 @@ const char *pci_root_bus_path(PCIDevice *dev) PCIHostState *host_bridge = PCI_HOST_BRIDGE(rootbus->qbus.parent); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_GET_CLASS(host_bridge); - assert(!rootbus->parent_dev); assert(host_bridge->bus == rootbus); if (hc->root_bus_path) { @@ -325,7 +351,7 @@ bool pci_bus_is_express(PCIBus *bus) bool pci_bus_is_root(PCIBus *bus) { - return !bus->parent_dev; + return PCI_BUS_GET_CLASS(bus)->is_root(bus); } void pci_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent, @@ -379,9 +405,12 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, int pci_bus_num(PCIBus *s) { - if (pci_bus_is_root(s)) - return 0; /* pci host bridge */ - return s->parent_dev->config[PCI_SECONDARY_BUS]; + return PCI_BUS_GET_CLASS(s)->bus_num(s); +} + +int pci_bus_numa_node(PCIBus *bus) +{ + return PCI_BUS_GET_CLASS(bus)->numa_node(bus); } static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) @@ -1576,7 +1605,8 @@ PciInfoList *qmp_query_pci(Error **errp) QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { info = g_malloc0(sizeof(*info)); - info->value = qmp_query_pci_bus(host_bridge->bus, 0); + info->value = qmp_query_pci_bus(host_bridge->bus, + pci_bus_num(host_bridge->bus)); /* XXX: waiting for the qapi to support GSList */ if (!cur_item) { @@ -1681,10 +1711,28 @@ static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num) { return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) & PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ && - dev->config[PCI_SECONDARY_BUS] < bus_num && + dev->config[PCI_SECONDARY_BUS] <= bus_num && bus_num <= dev->config[PCI_SUBORDINATE_BUS]; } +/* Whether a given bus number is in a range of a root bus */ +static bool pci_root_bus_in_range(PCIBus *bus, int bus_num) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + PCIDevice *dev = bus->devices[i]; + + if (dev && PCI_DEVICE_GET_CLASS(dev)->is_bridge) { + if (pci_secondary_bus_in_range(dev, bus_num)) { + return true; + } + } + } + + return false; +} + static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) { PCIBus *sec; @@ -1706,12 +1754,18 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) /* try child bus */ for (; bus; bus = sec) { QLIST_FOREACH(sec, &bus->child, sibling) { - assert(!pci_bus_is_root(sec)); - if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) { + if (pci_bus_num(sec) == bus_num) { return sec; } - if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { - break; + /* PXB buses assumed to be children of bus 0 */ + if (pci_bus_is_root(sec)) { + if (pci_root_bus_in_range(sec, bus_num)) { + break; + } + } else { + if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { + break; + } } } } 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/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 437955d1d5..c8ab06e7f3 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o -obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o +obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index c10e1b57b6..d300846c3d 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -1030,6 +1030,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) exit(1); } } + g_free(filename); /* Reserve space for dtb */ dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 7f52662d76..998ee2d16b 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -528,7 +528,6 @@ static void ppc_prep_init(MachineState *machine) PCIDevice *pci; ISABus *isa_bus; ISADevice *isa; - qemu_irq *cpu_exit_irq; int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; @@ -625,11 +624,11 @@ static void ppc_prep_init(MachineState *machine) /* PCI -> ISA bridge */ pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378"); - cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); cpu = POWERPC_CPU(first_cpu); qdev_connect_gpio_out(&pci->qdev, 0, cpu->env.irq_inputs[PPC6xx_INPUT_INT]); - qdev_connect_gpio_out(&pci->qdev, 1, *cpu_exit_irq); + qdev_connect_gpio_out(&pci->qdev, 1, + qemu_allocate_irq(cpu_request_exit, NULL, 0)); sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9)); sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11)); sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9)); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a15fa3c965..f174e5a0f3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -533,6 +533,8 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, refpoints, sizeof(refpoints)))); _FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX))); + _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate", + RTAS_EVENT_SCAN_RATE))); /* * According to PAPR, rtas ibm,os-term does not guarantee a return @@ -794,8 +796,8 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, _FDT((fdt_pack(fdt))); if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { - hw_error("FDT too big ! 0x%x bytes (max is 0x%x)\n", - fdt_totalsize(fdt), FDT_MAX_SIZE); + error_report("FDT too big ! 0x%x bytes (max is 0x%x)", + fdt_totalsize(fdt), FDT_MAX_SIZE); exit(1); } @@ -899,7 +901,7 @@ static int spapr_check_htab_fd(sPAPREnvironment *spapr) 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)); + "%s", strerror(errno)); rc = -1; } spapr->htab_fd_stale = false; @@ -1419,7 +1421,7 @@ static void ppc_spapr_init(MachineState *machine) rma_alloc_size = kvmppc_alloc_rma(&rma); if (rma_alloc_size == -1) { - hw_error("qemu: Unable to create RMA\n"); + error_report("Unable to create RMA"); exit(1); } @@ -1504,6 +1506,11 @@ static void ppc_spapr_init(MachineState *machine) qemu_register_reset(spapr_cpu_reset, cpu); } + if (kvm_enabled()) { + /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */ + kvmppc_enable_logical_ci_hcalls(); + } + /* allocate RAM */ spapr->ram_limit = ram_size; memory_region_allocate_system_memory(ram, NULL, "ppc_spapr.ram", @@ -1520,18 +1527,18 @@ static void ppc_spapr_init(MachineState *machine) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); if (!filename) { - hw_error("Could not find LPAR rtas '%s'\n", "spapr-rtas.bin"); + error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin"); exit(1); } spapr->rtas_size = get_image_size(filename); spapr->rtas_blob = g_malloc(spapr->rtas_size); if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) { - hw_error("qemu: could not load LPAR rtas '%s'\n", filename); + error_report("Could not load LPAR rtas '%s'", filename); exit(1); } if (spapr->rtas_size > RTAS_MAX_SIZE) { - hw_error("RTAS too big ! 0x%zx bytes (max is 0x%x)\n", - (size_t)spapr->rtas_size, RTAS_MAX_SIZE); + error_report("RTAS too big ! 0x%zx bytes (max is 0x%x)", + (size_t)spapr->rtas_size, RTAS_MAX_SIZE); exit(1); } g_free(filename); @@ -1641,12 +1648,12 @@ static void ppc_spapr_init(MachineState *machine) } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (!filename) { - hw_error("Could not find LPAR rtas '%s'\n", bios_name); + error_report("Could not find LPAR firmware '%s'", bios_name); exit(1); } fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); - if (fw_size < 0) { - hw_error("qemu: could not load LPAR rtas '%s'\n", filename); + if (fw_size <= 0) { + error_report("Could not load LPAR firmware '%s'", filename); exit(1); } g_free(filename); @@ -1660,9 +1667,14 @@ static void ppc_spapr_init(MachineState *machine) /* Prepare the device tree */ spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, kernel_size, kernel_le, - kernel_cmdline, spapr->epow_irq); + kernel_cmdline, + spapr->check_exception_irq); assert(spapr->fdt_skel != NULL); + /* used by RTAS */ + QTAILQ_INIT(&spapr->ccs_list); + qemu_register_reset(spapr_ccs_reset_hook, spapr); + qemu_register_boot_set(spapr_boot_set, spapr); } @@ -1794,6 +1806,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS; mc->no_parallel = 1; mc->default_boot_order = ""; + mc->default_ram_size = 512 * M_BYTE; mc->kvm_type = spapr_kvm_type; mc->has_dynamic_sysbus = true; @@ -1816,7 +1829,12 @@ static const TypeInfo spapr_machine_info = { }; #define SPAPR_COMPAT_2_3 \ - HW_COMPAT_2_3 + HW_COMPAT_2_3 \ + {\ + .driver = "spapr-pci-host-bridge",\ + .property = "dynamic-reconfiguration",\ + .value = "off",\ + }, #define SPAPR_COMPAT_2_2 \ SPAPR_COMPAT_2_3 \ @@ -1905,10 +1923,15 @@ static const TypeInfo spapr_machine_2_2_info = { static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data) { + static GlobalProperty compat_props[] = { + SPAPR_COMPAT_2_3 + { /* end of list */ } + }; MachineClass *mc = MACHINE_CLASS(oc); mc->name = "pseries-2.3"; mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3"; + mc->compat_props = compat_props; } static const TypeInfo spapr_machine_2_3_info = { diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c new file mode 100644 index 0000000000..ef985381cb --- /dev/null +++ b/hw/ppc/spapr_drc.c @@ -0,0 +1,744 @@ +/* + * QEMU SPAPR Dynamic Reconfiguration Connector Implementation + * + * Copyright IBM Corp. 2014 + * + * Authors: + * Michael Roth <mdroth@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "hw/ppc/spapr_drc.h" +#include "qom/object.h" +#include "hw/qdev.h" +#include "qapi/visitor.h" +#include "qemu/error-report.h" + +/* #define DEBUG_SPAPR_DRC */ + +#ifdef DEBUG_SPAPR_DRC +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#define DPRINTFN(fmt, ...) \ + do { DPRINTF(fmt, ## __VA_ARGS__); fprintf(stderr, "\n"); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#define DPRINTFN(fmt, ...) \ + do { } while (0) +#endif + +#define DRC_CONTAINER_PATH "/dr-connector" +#define DRC_INDEX_TYPE_SHIFT 28 +#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT)) + +static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type) +{ + uint32_t shift = 0; + + /* make sure this isn't SPAPR_DR_CONNECTOR_TYPE_ANY, or some + * other wonky value. + */ + g_assert(is_power_of_2(type)); + + while (type != (1 << shift)) { + shift++; + } + return shift; +} + +static uint32_t get_index(sPAPRDRConnector *drc) +{ + /* no set format for a drc index: it only needs to be globally + * unique. this is how we encode the DRC type on bare-metal + * however, so might as well do that here + */ + return (get_type_shift(drc->type) << DRC_INDEX_TYPE_SHIFT) | + (drc->id & DRC_INDEX_ID_MASK); +} + +static int set_isolation_state(sPAPRDRConnector *drc, + sPAPRDRIsolationState state) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state); + + drc->isolation_state = state; + + if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { + /* if we're awaiting release, but still in an unconfigured state, + * it's likely the guest is still in the process of configuring + * the device and is transitioning the devices to an ISOLATED + * state as a part of that process. so we only complete the + * removal when this transition happens for a device in a + * configured state, as suggested by the state diagram from + * PAPR+ 2.7, 13.4 + */ + if (drc->awaiting_release) { + if (drc->configured) { + DPRINTFN("finalizing device removal"); + drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, + drc->detach_cb_opaque, NULL); + } else { + DPRINTFN("deferring device removal on unconfigured device\n"); + } + } + drc->configured = false; + } + + return 0; +} + +static int set_indicator_state(sPAPRDRConnector *drc, + sPAPRDRIndicatorState state) +{ + DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state); + drc->indicator_state = state; + return 0; +} + +static int set_allocation_state(sPAPRDRConnector *drc, + sPAPRDRAllocationState state) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state); + + if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) { + drc->allocation_state = state; + if (drc->awaiting_release && + drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { + DPRINTFN("finalizing device removal"); + drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, + drc->detach_cb_opaque, NULL); + } + } + return 0; +} + +static uint32_t get_type(sPAPRDRConnector *drc) +{ + return drc->type; +} + +static const char *get_name(sPAPRDRConnector *drc) +{ + return drc->name; +} + +static const void *get_fdt(sPAPRDRConnector *drc, int *fdt_start_offset) +{ + if (fdt_start_offset) { + *fdt_start_offset = drc->fdt_start_offset; + } + return drc->fdt; +} + +static void set_configured(sPAPRDRConnector *drc) +{ + DPRINTFN("drc: %x, set_configured", get_index(drc)); + + if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_UNISOLATED) { + /* guest should be not configuring an isolated device */ + DPRINTFN("drc: %x, set_configured: skipping isolated device", + get_index(drc)); + return; + } + drc->configured = true; +} + +/* + * dr-entity-sense sensor value + * returned via get-sensor-state RTAS calls + * as expected by state diagram in PAPR+ 2.7, 13.4 + * based on the current allocation/indicator/power states + * for the DR connector. + */ +static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) +{ + sPAPRDREntitySense state; + + if (drc->dev) { + if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && + drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { + /* for logical DR, we return a state of UNUSABLE + * iff the allocation state UNUSABLE. + * Otherwise, report the state as USABLE/PRESENT, + * as we would for PCI. + */ + state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; + } else { + /* this assumes all PCI devices are assigned to + * a 'live insertion' power domain, where QEMU + * manages power state automatically as opposed + * to the guest. present, non-PCI resources are + * unaffected by power state. + */ + state = SPAPR_DR_ENTITY_SENSE_PRESENT; + } + } else { + if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { + /* PCI devices, and only PCI devices, use EMPTY + * in cases where we'd otherwise use UNUSABLE + */ + state = SPAPR_DR_ENTITY_SENSE_EMPTY; + } else { + state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; + } + } + + DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state); + return state; +} + +static void prop_get_index(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + uint32_t value = (uint32_t)drck->get_index(drc); + visit_type_uint32(v, &value, name, errp); +} + +static void prop_get_type(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + uint32_t value = (uint32_t)drck->get_type(drc); + visit_type_uint32(v, &value, name, errp); +} + +static char *prop_get_name(Object *obj, Error **errp) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + return g_strdup(drck->get_name(drc)); +} + +static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + uint32_t value = (uint32_t)drck->entity_sense(drc); + visit_type_uint32(v, &value, name, errp); +} + +static void prop_get_fdt(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + int fdt_offset_next, fdt_offset, fdt_depth; + void *fdt; + + if (!drc->fdt) { + return; + } + + fdt = drc->fdt; + fdt_offset = drc->fdt_start_offset; + fdt_depth = 0; + + do { + const char *name = NULL; + const struct fdt_property *prop = NULL; + int prop_len = 0, name_len = 0; + uint32_t tag; + + tag = fdt_next_tag(fdt, fdt_offset, &fdt_offset_next); + switch (tag) { + case FDT_BEGIN_NODE: + fdt_depth++; + name = fdt_get_name(fdt, fdt_offset, &name_len); + visit_start_struct(v, NULL, NULL, name, 0, NULL); + break; + case FDT_END_NODE: + /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */ + g_assert(fdt_depth > 0); + visit_end_struct(v, NULL); + fdt_depth--; + break; + case FDT_PROP: { + int i; + prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + visit_start_list(v, name, NULL); + for (i = 0; i < prop_len; i++) { + visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL); + + } + visit_end_list(v, NULL); + break; + } + default: + error_setg(&error_abort, "device FDT in unexpected state: %d", tag); + } + fdt_offset = fdt_offset_next; + } while (fdt_depth != 0); +} + +static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, + int fdt_start_offset, bool coldplug, Error **errp) +{ + DPRINTFN("drc: %x, attach", get_index(drc)); + + if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { + error_setg(errp, "an attached device is still awaiting release"); + return; + } + if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { + g_assert(drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE); + } + g_assert(fdt || coldplug); + + /* NOTE: setting initial isolation state to UNISOLATED means we can't + * detach unless guest has a userspace/kernel that moves this state + * back to ISOLATED in response to an unplug event, or this is done + * manually by the admin prior. if we force things while the guest + * may be accessing the device, we can easily crash the guest, so we + * we defer completion of removal in such cases to the reset() hook. + */ + if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { + drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED; + } + drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE; + + drc->dev = d; + drc->fdt = fdt; + drc->fdt_start_offset = fdt_start_offset; + drc->configured = false; + + object_property_add_link(OBJECT(drc), "device", + object_get_typename(OBJECT(drc->dev)), + (Object **)(&drc->dev), + NULL, 0, NULL); +} + +static void detach(sPAPRDRConnector *drc, DeviceState *d, + spapr_drc_detach_cb *detach_cb, + void *detach_cb_opaque, Error **errp) +{ + DPRINTFN("drc: %x, detach", get_index(drc)); + + drc->detach_cb = detach_cb; + drc->detach_cb_opaque = detach_cb_opaque; + + if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { + DPRINTFN("awaiting transition to isolated state before removal"); + drc->awaiting_release = true; + return; + } + + if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && + drc->allocation_state != SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { + DPRINTFN("awaiting transition to unusable state before removal"); + drc->awaiting_release = true; + return; + } + + drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE; + + if (drc->detach_cb) { + drc->detach_cb(drc->dev, drc->detach_cb_opaque); + } + + drc->awaiting_release = false; + g_free(drc->fdt); + drc->fdt = NULL; + drc->fdt_start_offset = 0; + object_property_del(OBJECT(drc), "device", NULL); + drc->dev = NULL; + drc->detach_cb = NULL; + drc->detach_cb_opaque = NULL; +} + +static bool release_pending(sPAPRDRConnector *drc) +{ + return drc->awaiting_release; +} + +static void reset(DeviceState *d) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + DPRINTFN("drc reset: %x", drck->get_index(drc)); + /* immediately upon reset we can safely assume DRCs whose devices + * are pending removal can be safely removed, and that they will + * subsequently be left in an ISOLATED state. move the DRC to this + * state in these cases (which will in turn complete any pending + * device removals) + */ + if (drc->awaiting_release) { + drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_ISOLATED); + /* generally this should also finalize the removal, but if the device + * hasn't yet been configured we normally defer removal under the + * assumption that this transition is taking place as part of device + * configuration. so check if we're still waiting after this, and + * force removal if we are + */ + if (drc->awaiting_release) { + drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, + drc->detach_cb_opaque, NULL); + } + + /* non-PCI devices may be awaiting a transition to UNUSABLE */ + if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && + drc->awaiting_release) { + drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE); + } + } +} + +static void realize(DeviceState *d, Error **errp) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + Object *root_container; + char link_name[256]; + gchar *child_name; + Error *err = NULL; + + DPRINTFN("drc realize: %x", drck->get_index(drc)); + /* NOTE: we do this as part of realize/unrealize due to the fact + * that the guest will communicate with the DRC via RTAS calls + * referencing the global DRC index. By unlinking the DRC + * from DRC_CONTAINER_PATH/<drc_index> we effectively make it + * inaccessible by the guest, since lookups rely on this path + * existing in the composition tree + */ + root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + snprintf(link_name, sizeof(link_name), "%x", drck->get_index(drc)); + child_name = object_get_canonical_path_component(OBJECT(drc)); + DPRINTFN("drc child name: %s", child_name); + object_property_add_alias(root_container, link_name, + drc->owner, child_name, &err); + if (err) { + error_report("%s", error_get_pretty(err)); + error_free(err); + object_unref(OBJECT(drc)); + } + DPRINTFN("drc realize complete"); +} + +static void unrealize(DeviceState *d, Error **errp) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + Object *root_container; + char name[256]; + Error *err = NULL; + + DPRINTFN("drc unrealize: %x", drck->get_index(drc)); + root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + snprintf(name, sizeof(name), "%x", drck->get_index(drc)); + object_property_del(root_container, name, &err); + if (err) { + error_report("%s", error_get_pretty(err)); + error_free(err); + object_unref(OBJECT(drc)); + } +} + +sPAPRDRConnector *spapr_dr_connector_new(Object *owner, + sPAPRDRConnectorType type, + uint32_t id) +{ + sPAPRDRConnector *drc = + SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR)); + + g_assert(type); + + drc->type = type; + drc->id = id; + drc->owner = owner; + object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL); + object_property_set_bool(OBJECT(drc), true, "realized", NULL); + + /* human-readable name for a DRC to encode into the DT + * description. this is mainly only used within a guest in place + * of the unique DRC index. + * + * in the case of VIO/PCI devices, it corresponds to a + * "location code" that maps a logical device/function (DRC index) + * to a physical (or virtual in the case of VIO) location in the + * system by chaining together the "location label" for each + * encapsulating component. + * + * since this is more to do with diagnosing physical hardware + * issues than guest compatibility, we choose location codes/DRC + * names that adhere to the documented format, but avoid encoding + * the entire topology information into the label/code, instead + * just using the location codes based on the labels for the + * endpoints (VIO/PCI adaptor connectors), which is basically + * just "C" followed by an integer ID. + * + * DRC names as documented by PAPR+ v2.7, 13.5.2.4 + * location codes as documented by PAPR+ v2.7, 12.3.1.5 + */ + switch (drc->type) { + case SPAPR_DR_CONNECTOR_TYPE_CPU: + drc->name = g_strdup_printf("CPU %d", id); + break; + case SPAPR_DR_CONNECTOR_TYPE_PHB: + drc->name = g_strdup_printf("PHB %d", id); + break; + case SPAPR_DR_CONNECTOR_TYPE_VIO: + case SPAPR_DR_CONNECTOR_TYPE_PCI: + drc->name = g_strdup_printf("C%d", id); + break; + case SPAPR_DR_CONNECTOR_TYPE_LMB: + drc->name = g_strdup_printf("LMB %d", id); + break; + default: + g_assert(false); + } + + /* PCI slot always start in a USABLE state, and stay there */ + if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { + drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE; + } + + return drc; +} + +static void spapr_dr_connector_instance_init(Object *obj) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + + object_property_add_uint32_ptr(obj, "isolation-state", + &drc->isolation_state, NULL); + object_property_add_uint32_ptr(obj, "indicator-state", + &drc->indicator_state, NULL); + object_property_add_uint32_ptr(obj, "allocation-state", + &drc->allocation_state, NULL); + object_property_add_uint32_ptr(obj, "id", &drc->id, NULL); + object_property_add(obj, "index", "uint32", prop_get_index, + NULL, NULL, NULL, NULL); + object_property_add(obj, "connector_type", "uint32", prop_get_type, + NULL, NULL, NULL, NULL); + object_property_add_str(obj, "name", prop_get_name, NULL, NULL); + object_property_add(obj, "entity-sense", "uint32", prop_get_entity_sense, + NULL, NULL, NULL, NULL); + object_property_add(obj, "fdt", "struct", prop_get_fdt, + NULL, NULL, NULL, NULL); +} + +static void spapr_dr_connector_class_init(ObjectClass *k, void *data) +{ + DeviceClass *dk = DEVICE_CLASS(k); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + dk->reset = reset; + dk->realize = realize; + dk->unrealize = unrealize; + drck->set_isolation_state = set_isolation_state; + drck->set_indicator_state = set_indicator_state; + drck->set_allocation_state = set_allocation_state; + drck->get_index = get_index; + drck->get_type = get_type; + drck->get_name = get_name; + drck->get_fdt = get_fdt; + drck->set_configured = set_configured; + drck->entity_sense = entity_sense; + drck->attach = attach; + drck->detach = detach; + drck->release_pending = release_pending; +} + +static const TypeInfo spapr_dr_connector_info = { + .name = TYPE_SPAPR_DR_CONNECTOR, + .parent = TYPE_DEVICE, + .instance_size = sizeof(sPAPRDRConnector), + .instance_init = spapr_dr_connector_instance_init, + .class_size = sizeof(sPAPRDRConnectorClass), + .class_init = spapr_dr_connector_class_init, +}; + +static void spapr_drc_register_types(void) +{ + type_register_static(&spapr_dr_connector_info); +} + +type_init(spapr_drc_register_types) + +/* helper functions for external users */ + +sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index) +{ + Object *obj; + char name[256]; + + snprintf(name, sizeof(name), "%s/%x", DRC_CONTAINER_PATH, index); + obj = object_resolve_path(name, NULL); + + return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); +} + +sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type, + uint32_t id) +{ + return spapr_dr_connector_by_index( + (get_type_shift(type) << DRC_INDEX_TYPE_SHIFT) | + (id & DRC_INDEX_ID_MASK)); +} + +/* generate a string the describes the DRC to encode into the + * device tree. + * + * as documented by PAPR+ v2.7, 13.5.2.6 and C.6.1 + */ +static const char *spapr_drc_get_type_str(sPAPRDRConnectorType type) +{ + switch (type) { + case SPAPR_DR_CONNECTOR_TYPE_CPU: + return "CPU"; + case SPAPR_DR_CONNECTOR_TYPE_PHB: + return "PHB"; + case SPAPR_DR_CONNECTOR_TYPE_VIO: + return "SLOT"; + case SPAPR_DR_CONNECTOR_TYPE_PCI: + return "28"; + case SPAPR_DR_CONNECTOR_TYPE_LMB: + return "MEM"; + default: + g_assert(false); + } + + return NULL; +} + +/** + * spapr_drc_populate_dt + * + * @fdt: libfdt device tree + * @path: path in the DT to generate properties + * @owner: parent Object/DeviceState for which to generate DRC + * descriptions for + * @drc_type_mask: mask of sPAPRDRConnectorType values corresponding + * to the types of DRCs to generate entries for + * + * generate OF properties to describe DRC topology/indices to guests + * + * as documented in PAPR+ v2.1, 13.5.2 + */ +int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, + uint32_t drc_type_mask) +{ + Object *root_container; + ObjectProperty *prop; + uint32_t drc_count = 0; + GArray *drc_indexes, *drc_power_domains; + GString *drc_names, *drc_types; + int ret; + + /* the first entry of each properties is a 32-bit integer encoding + * the number of elements in the array. we won't know this until + * we complete the iteration through all the matching DRCs, but + * reserve the space now and set the offsets accordingly so we + * can fill them in later. + */ + drc_indexes = g_array_new(false, true, sizeof(uint32_t)); + drc_indexes = g_array_set_size(drc_indexes, 1); + drc_power_domains = g_array_new(false, true, sizeof(uint32_t)); + drc_power_domains = g_array_set_size(drc_power_domains, 1); + drc_names = g_string_set_size(g_string_new(NULL), sizeof(uint32_t)); + drc_types = g_string_set_size(g_string_new(NULL), sizeof(uint32_t)); + + /* aliases for all DRConnector objects will be rooted in QOM + * composition tree at DRC_CONTAINER_PATH + */ + root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + + QTAILQ_FOREACH(prop, &root_container->properties, node) { + Object *obj; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint32_t drc_index, drc_power_domain; + + if (!strstart(prop->type, "link<", NULL)) { + continue; + } + + obj = object_property_get_link(root_container, prop->name, NULL); + drc = SPAPR_DR_CONNECTOR(obj); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + if (owner && (drc->owner != owner)) { + continue; + } + + if ((drc->type & drc_type_mask) == 0) { + continue; + } + + drc_count++; + + /* ibm,drc-indexes */ + drc_index = cpu_to_be32(drck->get_index(drc)); + g_array_append_val(drc_indexes, drc_index); + + /* ibm,drc-power-domains */ + drc_power_domain = cpu_to_be32(-1); + g_array_append_val(drc_power_domains, drc_power_domain); + + /* ibm,drc-names */ + drc_names = g_string_append(drc_names, drck->get_name(drc)); + drc_names = g_string_insert_len(drc_names, -1, "\0", 1); + + /* ibm,drc-types */ + drc_types = g_string_append(drc_types, + spapr_drc_get_type_str(drc->type)); + drc_types = g_string_insert_len(drc_types, -1, "\0", 1); + } + + /* now write the drc count into the space we reserved at the + * beginning of the arrays previously + */ + *(uint32_t *)drc_indexes->data = cpu_to_be32(drc_count); + *(uint32_t *)drc_power_domains->data = cpu_to_be32(drc_count); + *(uint32_t *)drc_names->str = cpu_to_be32(drc_count); + *(uint32_t *)drc_types->str = cpu_to_be32(drc_count); + + ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-indexes", + drc_indexes->data, + drc_indexes->len * sizeof(uint32_t)); + if (ret) { + fprintf(stderr, "Couldn't create ibm,drc-indexes property\n"); + goto out; + } + + ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-power-domains", + drc_power_domains->data, + drc_power_domains->len * sizeof(uint32_t)); + if (ret) { + fprintf(stderr, "Couldn't finalize ibm,drc-power-domains property\n"); + goto out; + } + + ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-names", + drc_names->str, drc_names->len); + if (ret) { + fprintf(stderr, "Couldn't finalize ibm,drc-names property\n"); + goto out; + } + + ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-types", + drc_types->str, drc_types->len); + if (ret) { + fprintf(stderr, "Couldn't finalize ibm,drc-types property\n"); + goto out; + } + +out: + g_array_free(drc_indexes, true); + g_array_free(drc_power_domains, true); + g_string_free(drc_names, true); + g_string_free(drc_types, true); + + return ret; +} diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 283e96bca1..fda9e3590a 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -32,6 +32,9 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" +#include "hw/pci/pci.h" +#include "hw/pci-host/spapr.h" +#include "hw/ppc/spapr_drc.h" #include <libfdt.h> @@ -77,6 +80,7 @@ struct rtas_error_log { #define RTAS_LOG_TYPE_ECC_UNCORR 0x00000009 #define RTAS_LOG_TYPE_ECC_CORR 0x0000000a #define RTAS_LOG_TYPE_EPOW 0x00000040 +#define RTAS_LOG_TYPE_HOTPLUG 0x000000e5 uint32_t extended_length; } QEMU_PACKED; @@ -166,6 +170,38 @@ struct epow_log_full { struct rtas_event_log_v6_epow epow; } QEMU_PACKED; +struct rtas_event_log_v6_hp { +#define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */ + struct rtas_event_log_v6_section_header hdr; + uint8_t hotplug_type; +#define RTAS_LOG_V6_HP_TYPE_CPU 1 +#define RTAS_LOG_V6_HP_TYPE_MEMORY 2 +#define RTAS_LOG_V6_HP_TYPE_SLOT 3 +#define RTAS_LOG_V6_HP_TYPE_PHB 4 +#define RTAS_LOG_V6_HP_TYPE_PCI 5 + uint8_t hotplug_action; +#define RTAS_LOG_V6_HP_ACTION_ADD 1 +#define RTAS_LOG_V6_HP_ACTION_REMOVE 2 + uint8_t hotplug_identifier; +#define RTAS_LOG_V6_HP_ID_DRC_NAME 1 +#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 +#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 + uint8_t reserved; + union { + uint32_t index; + uint32_t count; + char name[1]; + } drc; +} QEMU_PACKED; + +struct hp_log_full { + struct rtas_error_log hdr; + struct rtas_event_log_v6 v6hdr; + struct rtas_event_log_v6_maina maina; + struct rtas_event_log_v6_mainb mainb; + struct rtas_event_log_v6_hp hp; +} QEMU_PACKED; + #define EVENT_MASK_INTERNAL_ERRORS 0x80000000 #define EVENT_MASK_EPOW 0x40000000 #define EVENT_MASK_HOTPLUG 0x10000000 @@ -181,67 +217,105 @@ struct epow_log_full { } \ } while (0) -void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq) +void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq) { - uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)}; - uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0}; + uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)}; + uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0}; _FDT((fdt_begin_node(fdt, "event-sources"))); _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); _FDT((fdt_property(fdt, "interrupt-ranges", - epow_irq_ranges, sizeof(epow_irq_ranges)))); + irq_ranges, sizeof(irq_ranges)))); _FDT((fdt_begin_node(fdt, "epow-events"))); - _FDT((fdt_property(fdt, "interrupts", - epow_interrupts, sizeof(epow_interrupts)))); + _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts)))); _FDT((fdt_end_node(fdt))); _FDT((fdt_end_node(fdt))); } -static struct epow_log_full *pending_epow; -static uint32_t next_plid; +static void rtas_event_log_queue(int log_type, void *data, bool exception) +{ + sPAPREventLogEntry *entry = g_new(sPAPREventLogEntry, 1); -static void spapr_powerdown_req(Notifier *n, void *opaque) + g_assert(data); + entry->log_type = log_type; + entry->exception = exception; + entry->data = data; + QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next); +} + +static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask, + bool exception) { - sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier); - struct rtas_error_log *hdr; - struct rtas_event_log_v6 *v6hdr; - struct rtas_event_log_v6_maina *maina; - struct rtas_event_log_v6_mainb *mainb; - struct rtas_event_log_v6_epow *epow; - struct tm tm; - int year; + sPAPREventLogEntry *entry = NULL; - if (pending_epow) { - /* For now, we just throw away earlier events if two come - * along before any are consumed. This is sufficient for our - * powerdown messages, but we'll need more if we do more - * general error/event logging */ - g_free(pending_epow); + /* we only queue EPOW events atm. */ + if ((event_mask & EVENT_MASK_EPOW) == 0) { + return NULL; } - pending_epow = g_malloc0(sizeof(*pending_epow)); - hdr = &pending_epow->hdr; - v6hdr = &pending_epow->v6hdr; - maina = &pending_epow->maina; - mainb = &pending_epow->mainb; - epow = &pending_epow->epow; - hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6 - | RTAS_LOG_SEVERITY_EVENT - | RTAS_LOG_DISPOSITION_NOT_RECOVERED - | RTAS_LOG_OPTIONAL_PART_PRESENT - | RTAS_LOG_TYPE_EPOW); - hdr->extended_length = cpu_to_be32(sizeof(*pending_epow) - - sizeof(pending_epow->hdr)); + QTAILQ_FOREACH(entry, &spapr->pending_events, next) { + if (entry->exception != exception) { + continue; + } + + /* EPOW and hotplug events are surfaced in the same manner */ + if (entry->log_type == RTAS_LOG_TYPE_EPOW || + entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { + break; + } + } + + if (entry) { + QTAILQ_REMOVE(&spapr->pending_events, entry, next); + } + return entry; +} + +static bool rtas_event_log_contains(uint32_t event_mask, bool exception) +{ + sPAPREventLogEntry *entry = NULL; + + /* we only queue EPOW events atm. */ + if ((event_mask & EVENT_MASK_EPOW) == 0) { + return false; + } + + QTAILQ_FOREACH(entry, &spapr->pending_events, next) { + if (entry->exception != exception) { + continue; + } + + /* EPOW and hotplug events are surfaced in the same manner */ + if (entry->log_type == RTAS_LOG_TYPE_EPOW || + entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { + return true; + } + } + + return false; +} + +static uint32_t next_plid; + +static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr) +{ v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG | RTAS_LOG_V6_B0_BIGENDIAN; v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT; v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM); +} + +static void spapr_init_maina(struct rtas_event_log_v6_maina *maina, + int section_count) +{ + struct tm tm; + int year; maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA); maina->hdr.section_length = cpu_to_be16(sizeof(*maina)); @@ -256,8 +330,37 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) | (to_bcd(tm.tm_min) << 16) | (to_bcd(tm.tm_sec) << 8)); maina->creator_id = 'H'; /* Hypervisor */ - maina->section_count = 3; /* Main-A, Main-B and EPOW */ + maina->section_count = section_count; maina->plid = next_plid++; +} + +static void spapr_powerdown_req(Notifier *n, void *opaque) +{ + sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier); + struct rtas_error_log *hdr; + struct rtas_event_log_v6 *v6hdr; + struct rtas_event_log_v6_maina *maina; + struct rtas_event_log_v6_mainb *mainb; + struct rtas_event_log_v6_epow *epow; + struct epow_log_full *new_epow; + + new_epow = g_malloc0(sizeof(*new_epow)); + hdr = &new_epow->hdr; + v6hdr = &new_epow->v6hdr; + maina = &new_epow->maina; + mainb = &new_epow->mainb; + epow = &new_epow->epow; + + hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6 + | RTAS_LOG_SEVERITY_EVENT + | RTAS_LOG_DISPOSITION_NOT_RECOVERED + | RTAS_LOG_OPTIONAL_PART_PRESENT + | RTAS_LOG_TYPE_EPOW); + hdr->extended_length = cpu_to_be32(sizeof(*new_epow) + - sizeof(new_epow->hdr)); + + spapr_init_v6hdr(v6hdr); + spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */); mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB); mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb)); @@ -274,7 +377,80 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL; epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC; - qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq)); + rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true); + + qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); +} + +static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) +{ + struct hp_log_full *new_hp; + struct rtas_error_log *hdr; + struct rtas_event_log_v6 *v6hdr; + struct rtas_event_log_v6_maina *maina; + struct rtas_event_log_v6_mainb *mainb; + struct rtas_event_log_v6_hp *hp; + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sPAPRDRConnectorType drc_type = drck->get_type(drc); + + new_hp = g_malloc0(sizeof(struct hp_log_full)); + hdr = &new_hp->hdr; + v6hdr = &new_hp->v6hdr; + maina = &new_hp->maina; + mainb = &new_hp->mainb; + hp = &new_hp->hp; + + hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6 + | RTAS_LOG_SEVERITY_EVENT + | RTAS_LOG_DISPOSITION_NOT_RECOVERED + | RTAS_LOG_OPTIONAL_PART_PRESENT + | RTAS_LOG_INITIATOR_HOTPLUG + | RTAS_LOG_TYPE_HOTPLUG); + hdr->extended_length = cpu_to_be32(sizeof(*new_hp) + - sizeof(new_hp->hdr)); + + spapr_init_v6hdr(v6hdr); + spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */); + + mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB); + mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb)); + mainb->subsystem_id = 0x80; /* External environment */ + mainb->event_severity = 0x00; /* Informational / non-error */ + mainb->event_subtype = 0x00; /* Normal shutdown */ + + hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG); + hp->hdr.section_length = cpu_to_be16(sizeof(*hp)); + hp->hdr.section_version = 1; /* includes extended modifier */ + hp->hotplug_action = hp_action; + + + switch (drc_type) { + case SPAPR_DR_CONNECTOR_TYPE_PCI: + hp->drc.index = cpu_to_be32(drck->get_index(drc)); + hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX; + hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI; + break; + default: + /* we shouldn't be signaling hotplug events for resources + * that don't support them + */ + g_assert(false); + return; + } + + rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true); + + qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); +} + +void spapr_hotplug_req_add_event(sPAPRDRConnector *drc) +{ + spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD); +} + +void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc) +{ + spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE); } static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -282,8 +458,10 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong args, uint32_t nret, target_ulong rets) { - uint32_t mask, buf, len; + uint32_t mask, buf, len, event_len; uint64_t xinfo; + sPAPREventLogEntry *event; + struct rtas_error_log *hdr; if ((nargs < 6) || (nargs > 7) || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -298,25 +476,85 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr, xinfo |= (uint64_t)rtas_ld(args, 6) << 32; } - if ((mask & EVENT_MASK_EPOW) && pending_epow) { - if (sizeof(*pending_epow) < len) { - len = sizeof(*pending_epow); - } + event = rtas_event_log_dequeue(mask, true); + if (!event) { + goto out_no_events; + } + + hdr = event->data; + event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr); + + if (event_len < len) { + len = event_len; + } + + cpu_physical_memory_write(buf, event->data, len); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + g_free(event->data); + g_free(event); + + /* according to PAPR+, the IRQ must be left asserted, or re-asserted, if + * there are still pending events to be fetched via check-exception. We + * do the latter here, since our code relies on edge-triggered + * interrupts. + */ + if (rtas_event_log_contains(mask, true)) { + qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); + } - cpu_physical_memory_write(buf, pending_epow, len); - g_free(pending_epow); - pending_epow = NULL; - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - } else { - rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); + return; + +out_no_events: + rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); +} + +static void event_scan(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t mask, buf, len, event_len; + sPAPREventLogEntry *event; + struct rtas_error_log *hdr; + + if (nargs != 4 || nret != 1) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; } + + mask = rtas_ld(args, 0); + buf = rtas_ld(args, 2); + len = rtas_ld(args, 3); + + event = rtas_event_log_dequeue(mask, false); + if (!event) { + goto out_no_events; + } + + hdr = event->data; + event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr); + + if (event_len < len) { + len = event_len; + } + + cpu_physical_memory_write(buf, event->data, len); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + g_free(event->data); + g_free(event); + return; + +out_no_events: + rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); } void spapr_events_init(sPAPREnvironment *spapr) { - spapr->epow_irq = xics_alloc(spapr->icp, 0, 0, false); + QTAILQ_INIT(&spapr->pending_events); + spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false); spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception", check_exception); + spapr_rtas_register(RTAS_EVENT_SCAN, "event-scan", event_scan); } diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index f3990fdc32..8cd9dba9ac 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -41,7 +41,7 @@ enum sPAPRTCEAccess { static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; -static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn) +sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn) { sPAPRTCETable *tcet; @@ -52,7 +52,7 @@ static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn) } QLIST_FOREACH(tcet, &spapr_tce_tables, list) { - if (tcet->liobn == liobn) { + if (tcet->liobn == (uint32_t)liobn) { return tcet; } } @@ -126,11 +126,11 @@ static MemoryRegionIOMMUOps spapr_iommu_ops = { static int spapr_tce_table_realize(DeviceState *dev) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); + uint64_t window_size = (uint64_t)tcet->nb_table << tcet->page_shift; - if (kvm_enabled()) { + if (kvm_enabled() && !(window_size >> 32)) { tcet->table = kvmppc_create_spapr_tce(tcet->liobn, - tcet->nb_table << - tcet->page_shift, + window_size, &tcet->fd, tcet->vfio_accel); } @@ -161,6 +161,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, bool vfio_accel) { sPAPRTCETable *tcet; + char tmp[64]; if (spapr_tce_find_by_liobn(liobn)) { fprintf(stderr, "Attempted to create TCE table with duplicate" @@ -179,7 +180,8 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, tcet->nb_table = nb_table; tcet->vfio_accel = vfio_accel; - object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL); + snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn); + object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL); object_property_set_bool(OBJECT(tcet), true, "realized", NULL); @@ -247,7 +249,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, target_ulong ioba1 = ioba; target_ulong tce_list = args[2]; target_ulong npages = args[3]; - target_ulong ret = H_PARAMETER; + target_ulong ret = H_PARAMETER, tce = 0; sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); CPUState *cs = CPU(cpu); hwaddr page_mask, page_size; @@ -267,7 +269,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, for (i = 0; i < npages; ++i, ioba += page_size) { target_ulong off = (tce_list & ~SPAPR_TCE_RW) + i * sizeof(target_ulong); - target_ulong tce = ldq_phys(cs->as, off); + tce = ldq_be_phys(cs->as, off); ret = put_tce_emu(tcet, ioba, tce); if (ret) { @@ -277,11 +279,11 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, /* Trace last successful or the first problematic entry */ i = i ? (i - 1) : 0; - trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, - ldq_phys(cs->as, - tce_list + i * sizeof(target_ulong)), - ret); - + if (SPAPR_IS_PCI_LIOBN(liobn)) { + trace_spapr_iommu_pci_indirect(liobn, ioba1, tce_list, i, tce, ret); + } else { + trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, tce, ret); + } return ret; } @@ -315,7 +317,11 @@ static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, break; } } - trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret); + if (SPAPR_IS_PCI_LIOBN(liobn)) { + trace_spapr_iommu_pci_stuff(liobn, ioba, tce_value, npages, ret); + } else { + trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret); + } return ret; } @@ -336,7 +342,11 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, ret = put_tce_emu(tcet, ioba, tce); } - trace_spapr_iommu_put(liobn, ioba, tce, ret); + if (SPAPR_IS_PCI_LIOBN(liobn)) { + trace_spapr_iommu_pci_put(liobn, ioba, tce, ret); + } else { + trace_spapr_iommu_put(liobn, ioba, tce, ret); + } return ret; } @@ -376,7 +386,11 @@ static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, args[0] = tce; } } - trace_spapr_iommu_get(liobn, ioba, ret, tce); + if (SPAPR_IS_PCI_LIOBN(liobn)) { + trace_spapr_iommu_pci_get(liobn, ioba, ret, tce); + } else { + trace_spapr_iommu_get(liobn, ioba, ret, tce); + } return ret; } diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 05f4faca6e..4df3a33db4 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -33,8 +33,11 @@ #include <libfdt.h> #include "trace.h" #include "qemu/error-report.h" +#include "qapi/qmp/qerror.h" #include "hw/pci/pci_bus.h" +#include "hw/ppc/spapr_drc.h" +#include "sysemu/device_tree.h" /* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ #define RTAS_QUERY_FN 0 @@ -47,7 +50,15 @@ #define RTAS_TYPE_MSI 1 #define RTAS_TYPE_MSIX 2 -static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + return ret; \ + } \ + } while (0) + +sPAPRPHBState *spapr_pci_find_phb(sPAPREnvironment *spapr, uint64_t buid) { sPAPRPHBState *sphb; @@ -61,10 +72,10 @@ static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) return NULL; } -static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid, - uint32_t config_addr) +PCIDevice *spapr_pci_find_dev(sPAPREnvironment *spapr, uint64_t buid, + uint32_t config_addr) { - sPAPRPHBState *sphb = find_phb(spapr, buid); + sPAPRPHBState *sphb = spapr_pci_find_phb(spapr, buid); PCIHostState *phb = PCI_HOST_BRIDGE(sphb); int bus_num = (config_addr >> 16) & 0xFF; int devfn = (config_addr >> 8) & 0xFF; @@ -95,7 +106,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid, return; } - pci_dev = find_dev(spapr, buid, addr); + pci_dev = spapr_pci_find_dev(spapr, buid, addr); addr = rtas_pci_cfgaddr(addr); if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { @@ -162,7 +173,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid, return; } - pci_dev = find_dev(spapr, buid, addr); + pci_dev = spapr_pci_find_dev(spapr, buid, addr); addr = rtas_pci_cfgaddr(addr); if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { @@ -280,9 +291,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, } /* Fins sPAPRPHBState */ - phb = find_phb(spapr, buid); + phb = spapr_pci_find_phb(spapr, buid); if (phb) { - pdev = find_dev(spapr, buid, config_addr); + pdev = spapr_pci_find_dev(spapr, buid, config_addr); } if (!phb || !pdev) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -381,9 +392,9 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, spapr_pci_msi *msi; /* Find sPAPRPHBState */ - phb = find_phb(spapr, buid); + phb = spapr_pci_find_phb(spapr, buid); if (phb) { - pdev = find_dev(spapr, buid, config_addr); + pdev = spapr_pci_find_dev(spapr, buid, config_addr); } if (!phb || !pdev) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -426,7 +437,7 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, addr = rtas_ld(args, 0); option = rtas_ld(args, 3); - sphb = find_phb(spapr, buid); + sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } @@ -461,7 +472,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, } buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - sphb = find_phb(spapr, buid); + sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } @@ -479,7 +490,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, switch (option) { case RTAS_GET_PE_ADDR: addr = rtas_ld(args, 0); - pdev = find_dev(spapr, buid, addr); + pdev = spapr_pci_find_dev(spapr, buid, addr); if (!pdev) { goto param_error_exit; } @@ -516,7 +527,7 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, } buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - sphb = find_phb(spapr, buid); + sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } @@ -562,7 +573,7 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); option = rtas_ld(args, 3); - sphb = find_phb(spapr, buid); + sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } @@ -596,7 +607,7 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu, } buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - sphb = find_phb(spapr, buid); + sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } @@ -631,7 +642,7 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, } buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - sphb = find_phb(spapr, buid); + sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } @@ -731,6 +742,372 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &phb->iommu_as; } +/* Macros to operate with address in OF binding to PCI */ +#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p)) +#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */ +#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */ +#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */ +#define b_ss(x) b_x((x), 24, 2) /* the space code */ +#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */ +#define b_ddddd(x) b_x((x), 11, 5) /* device number */ +#define b_fff(x) b_x((x), 8, 3) /* function number */ +#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ + +/* for 'reg'/'assigned-addresses' OF properties */ +#define RESOURCE_CELLS_SIZE 2 +#define RESOURCE_CELLS_ADDRESS 3 + +typedef struct ResourceFields { + uint32_t phys_hi; + uint32_t phys_mid; + uint32_t phys_lo; + uint32_t size_hi; + uint32_t size_lo; +} QEMU_PACKED ResourceFields; + +typedef struct ResourceProps { + ResourceFields reg[8]; + ResourceFields assigned[7]; + uint32_t reg_len; + uint32_t assigned_len; +} ResourceProps; + +/* fill in the 'reg'/'assigned-resources' OF properties for + * a PCI device. 'reg' describes resource requirements for a + * device's IO/MEM regions, 'assigned-addresses' describes the + * actual resource assignments. + * + * the properties are arrays of ('phys-addr', 'size') pairs describing + * the addressable regions of the PCI device, where 'phys-addr' is a + * RESOURCE_CELLS_ADDRESS-tuple of 32-bit integers corresponding to + * (phys.hi, phys.mid, phys.lo), and 'size' is a + * RESOURCE_CELLS_SIZE-tuple corresponding to (size.hi, size.lo). + * + * phys.hi = 0xYYXXXXZZ, where: + * 0xYY = npt000ss + * ||| | + * ||| +-- space code: 1 if IO region, 2 if MEM region + * ||+------ for non-relocatable IO: 1 if aliased + * || for relocatable IO: 1 if below 64KB + * || for MEM: 1 if below 1MB + * |+------- 1 if region is prefetchable + * +-------- 1 if region is non-relocatable + * 0xXXXX = bbbbbbbb dddddfff, encoding bus, slot, and function + * bits respectively + * 0xZZ = rrrrrrrr, the register number of the BAR corresponding + * to the region + * + * phys.mid and phys.lo correspond respectively to the hi/lo portions + * of the actual address of the region. + * + * how the phys-addr/size values are used differ slightly between + * 'reg' and 'assigned-addresses' properties. namely, 'reg' has + * an additional description for the config space region of the + * device, and in the case of QEMU has n=0 and phys.mid=phys.lo=0 + * to describe the region as relocatable, with an address-mapping + * that corresponds directly to the PHB's address space for the + * resource. 'assigned-addresses' always has n=1 set with an absolute + * address assigned for the resource. in general, 'assigned-addresses' + * won't be populated, since addresses for PCI devices are generally + * unmapped initially and left to the guest to assign. + * + * note also that addresses defined in these properties are, at least + * for PAPR guests, relative to the PHBs IO/MEM windows, and + * correspond directly to the addresses in the BARs. + * + * in accordance with PCI Bus Binding to Open Firmware, + * IEEE Std 1275-1994, section 4.1.1, as implemented by PAPR+ v2.7, + * Appendix C. + */ +static void populate_resource_props(PCIDevice *d, ResourceProps *rp) +{ + int bus_num = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(d)))); + uint32_t dev_id = (b_bbbbbbbb(bus_num) | + b_ddddd(PCI_SLOT(d->devfn)) | + b_fff(PCI_FUNC(d->devfn))); + ResourceFields *reg, *assigned; + int i, reg_idx = 0, assigned_idx = 0; + + /* config space region */ + reg = &rp->reg[reg_idx++]; + reg->phys_hi = cpu_to_be32(dev_id); + reg->phys_mid = 0; + reg->phys_lo = 0; + reg->size_hi = 0; + reg->size_lo = 0; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + if (!d->io_regions[i].size) { + continue; + } + + reg = &rp->reg[reg_idx++]; + + reg->phys_hi = cpu_to_be32(dev_id | b_rrrrrrrr(pci_bar(d, i))); + if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) { + reg->phys_hi |= cpu_to_be32(b_ss(1)); + } else { + reg->phys_hi |= cpu_to_be32(b_ss(2)); + } + reg->phys_mid = 0; + reg->phys_lo = 0; + reg->size_hi = cpu_to_be32(d->io_regions[i].size >> 32); + reg->size_lo = cpu_to_be32(d->io_regions[i].size); + + if (d->io_regions[i].addr == PCI_BAR_UNMAPPED) { + continue; + } + + assigned = &rp->assigned[assigned_idx++]; + assigned->phys_hi = cpu_to_be32(reg->phys_hi | b_n(1)); + assigned->phys_mid = cpu_to_be32(d->io_regions[i].addr >> 32); + assigned->phys_lo = cpu_to_be32(d->io_regions[i].addr); + assigned->size_hi = reg->size_hi; + assigned->size_lo = reg->size_lo; + } + + rp->reg_len = reg_idx * sizeof(ResourceFields); + rp->assigned_len = assigned_idx * sizeof(ResourceFields); +} + +static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, + int phb_index, int drc_index, + const char *drc_name) +{ + ResourceProps rp; + bool is_bridge = false; + int pci_status; + + if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) == + PCI_HEADER_TYPE_BRIDGE) { + is_bridge = true; + } + + /* in accordance with PAPR+ v2.7 13.6.3, Table 181 */ + _FDT(fdt_setprop_cell(fdt, offset, "vendor-id", + pci_default_read_config(dev, PCI_VENDOR_ID, 2))); + _FDT(fdt_setprop_cell(fdt, offset, "device-id", + pci_default_read_config(dev, PCI_DEVICE_ID, 2))); + _FDT(fdt_setprop_cell(fdt, offset, "revision-id", + pci_default_read_config(dev, PCI_REVISION_ID, 1))); + _FDT(fdt_setprop_cell(fdt, offset, "class-code", + pci_default_read_config(dev, PCI_CLASS_DEVICE, 2) + << 8)); + if (pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)) { + _FDT(fdt_setprop_cell(fdt, offset, "interrupts", + pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1))); + } + + if (!is_bridge) { + _FDT(fdt_setprop_cell(fdt, offset, "min-grant", + pci_default_read_config(dev, PCI_MIN_GNT, 1))); + _FDT(fdt_setprop_cell(fdt, offset, "max-latency", + pci_default_read_config(dev, PCI_MAX_LAT, 1))); + } + + if (pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)) { + _FDT(fdt_setprop_cell(fdt, offset, "subsystem-id", + pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2))); + } + + if (pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)) { + _FDT(fdt_setprop_cell(fdt, offset, "subsystem-vendor-id", + pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2))); + } + + _FDT(fdt_setprop_cell(fdt, offset, "cache-line-size", + pci_default_read_config(dev, PCI_CACHE_LINE_SIZE, 1))); + + /* the following fdt cells are masked off the pci status register */ + pci_status = pci_default_read_config(dev, PCI_STATUS, 2); + _FDT(fdt_setprop_cell(fdt, offset, "devsel-speed", + PCI_STATUS_DEVSEL_MASK & pci_status)); + + if (pci_status & PCI_STATUS_FAST_BACK) { + _FDT(fdt_setprop(fdt, offset, "fast-back-to-back", NULL, 0)); + } + if (pci_status & PCI_STATUS_66MHZ) { + _FDT(fdt_setprop(fdt, offset, "66mhz-capable", NULL, 0)); + } + if (pci_status & PCI_STATUS_UDF) { + _FDT(fdt_setprop(fdt, offset, "udf-supported", NULL, 0)); + } + + /* NOTE: this is normally generated by firmware via path/unit name, + * but in our case we must set it manually since it does not get + * processed by OF beforehand + */ + _FDT(fdt_setprop_string(fdt, offset, "name", "pci")); + _FDT(fdt_setprop(fdt, offset, "ibm,loc-code", drc_name, strlen(drc_name))); + _FDT(fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index)); + + _FDT(fdt_setprop_cell(fdt, offset, "#address-cells", + RESOURCE_CELLS_ADDRESS)); + _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", + RESOURCE_CELLS_SIZE)); + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", + RESOURCE_CELLS_SIZE)); + + populate_resource_props(dev, &rp); + _FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len)); + _FDT(fdt_setprop(fdt, offset, "assigned-addresses", + (uint8_t *)rp.assigned, rp.assigned_len)); + + return 0; +} + +/* create OF node for pci device and required OF DT properties */ +static void *spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev, + int drc_index, const char *drc_name, + int *dt_offset) +{ + void *fdt; + int offset, ret, fdt_size; + int slot = PCI_SLOT(dev->devfn); + int func = PCI_FUNC(dev->devfn); + char nodename[512]; + + fdt = create_device_tree(&fdt_size); + if (func != 0) { + sprintf(nodename, "pci@%d,%d", slot, func); + } else { + sprintf(nodename, "pci@%d", slot); + } + offset = fdt_add_subnode(fdt, 0, nodename); + ret = spapr_populate_pci_child_dt(dev, fdt, offset, phb->index, drc_index, + drc_name); + g_assert(!ret); + + *dt_offset = offset; + return fdt; +} + +static void spapr_phb_add_pci_device(sPAPRDRConnector *drc, + sPAPRPHBState *phb, + PCIDevice *pdev, + Error **errp) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + DeviceState *dev = DEVICE(pdev); + int drc_index = drck->get_index(drc); + const char *drc_name = drck->get_name(drc); + void *fdt = NULL; + int fdt_start_offset = 0; + + /* boot-time devices get their device tree node created by SLOF, but for + * hotplugged devices we need QEMU to generate it so the guest can fetch + * it via RTAS + */ + if (dev->hotplugged) { + fdt = spapr_create_pci_child_dt(phb, pdev, drc_index, drc_name, + &fdt_start_offset); + } + + drck->attach(drc, DEVICE(pdev), + fdt, fdt_start_offset, !dev->hotplugged, errp); + if (*errp) { + g_free(fdt); + } +} + +static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque) +{ + /* some version guests do not wait for completion of a device + * cleanup (generally done asynchronously by the kernel) before + * signaling to QEMU that the device is safe, but instead sleep + * for some 'safe' period of time. unfortunately on a busy host + * this sleep isn't guaranteed to be long enough, resulting in + * bad things like IRQ lines being left asserted during final + * device removal. to deal with this we call reset just prior + * to finalizing the device, which will put the device back into + * an 'idle' state, as the device cleanup code expects. + */ + pci_device_reset(PCI_DEVICE(dev)); + object_unparent(OBJECT(dev)); +} + +static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc, + sPAPRPHBState *phb, + PCIDevice *pdev, + Error **errp) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp); +} + +static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb, + PCIDevice *pdev) +{ + uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)))); + return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI, + (phb->index << 16) | + (busnr << 8) | + pdev->devfn); +} + +static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp) +{ + sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); + PCIDevice *pdev = PCI_DEVICE(plugged_dev); + sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); + Error *local_err = NULL; + + /* if DR is disabled we don't need to do anything in the case of + * hotplug or coldplug callbacks + */ + if (!phb->dr_enabled) { + /* if this is a hotplug operation initiated by the user + * we need to let them know it's not enabled + */ + if (plugged_dev->hotplugged) { + error_set(errp, QERR_BUS_NO_HOTPLUG, + object_get_typename(OBJECT(phb))); + } + return; + } + + g_assert(drc); + + spapr_phb_add_pci_device(drc, phb, pdev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + if (plugged_dev->hotplugged) { + spapr_hotplug_req_add_event(drc); + } +} + +static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp) +{ + sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); + PCIDevice *pdev = PCI_DEVICE(plugged_dev); + sPAPRDRConnectorClass *drck; + sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); + Error *local_err = NULL; + + if (!phb->dr_enabled) { + error_set(errp, QERR_BUS_NO_HOTPLUG, + object_get_typename(OBJECT(phb))); + return; + } + + g_assert(drc); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + if (!drck->release_pending(drc)) { + spapr_phb_remove_pci_device(drc, phb, pdev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + spapr_hotplug_req_remove_event(drc); + } +} + static void spapr_phb_realize(DeviceState *dev, Error **errp) { SysBusDevice *s = SYS_BUS_DEVICE(dev); @@ -742,12 +1119,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) PCIBus *bus; uint64_t msi_window_size = 4096; - if (sphb->index != -1) { + if (sphb->index != (uint32_t)-1) { hwaddr windows_base; - if ((sphb->buid != -1) || (sphb->dma_liobn != -1) - || (sphb->mem_win_addr != -1) - || (sphb->io_win_addr != -1)) { + if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1) + || (sphb->mem_win_addr != (hwaddr)-1) + || (sphb->io_win_addr != (hwaddr)-1)) { error_setg(errp, "Either \"index\" or other parameters must" " be specified for PAPR PHB, not both"); return; @@ -760,7 +1137,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; - sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index; + sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0); windows_base = SPAPR_PCI_WINDOW_BASE + sphb->index * SPAPR_PCI_WINDOW_SPACING; @@ -768,27 +1145,27 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF; } - if (sphb->buid == -1) { + if (sphb->buid == (uint64_t)-1) { error_setg(errp, "BUID not specified for PHB"); return; } - if (sphb->dma_liobn == -1) { + if (sphb->dma_liobn == (uint32_t)-1) { error_setg(errp, "LIOBN not specified for PHB"); return; } - if (sphb->mem_win_addr == -1) { + if (sphb->mem_win_addr == (hwaddr)-1) { error_setg(errp, "Memory window address not specified for PHB"); return; } - if (sphb->io_win_addr == -1) { + if (sphb->io_win_addr == (hwaddr)-1) { error_setg(errp, "IO window address not specified for PHB"); return; } - if (find_phb(spapr, sphb->buid)) { + if (spapr_pci_find_phb(spapr, sphb->buid)) { error_setg(errp, "PCI host bridges must have unique BUIDs"); return; } @@ -824,6 +1201,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); phb->bus = bus; + qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL); /* * Initialize PHB address space. @@ -880,6 +1258,15 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) sphb->lsi_table[i].irq = irq; } + /* allocate connectors for child PCI devices */ + if (sphb->dr_enabled) { + for (i = 0; i < PCI_SLOT_MAX * 8; i++) { + spapr_dr_connector_new(OBJECT(phb), + SPAPR_DR_CONNECTOR_TYPE_PCI, + (sphb->index << 16) | i); + } + } + if (!info->finish_realize) { error_setg(errp, "finish_realize not defined"); return; @@ -893,11 +1280,11 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) { sPAPRTCETable *tcet; + uint32_t nb_table; + nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT; tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, - 0, - SPAPR_TCE_PAGE_SHIFT, - 0x40000000 >> SPAPR_TCE_PAGE_SHIFT, false); + 0, SPAPR_TCE_PAGE_SHIFT, nb_table, false); if (!tcet) { error_setg(errp, "Unable to create TCE table for %s", sphb->dtbusname); @@ -936,6 +1323,8 @@ static Property spapr_phb_properties[] = { DEFINE_PROP_UINT64("io_win_addr", sPAPRPHBState, io_win_addr, -1), DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size, SPAPR_PCI_IO_WIN_SIZE), + DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled, + true), DEFINE_PROP_END_OF_LIST(), }; @@ -1049,6 +1438,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass); + HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass); hc->root_bus_path = spapr_phb_root_bus_path; dc->realize = spapr_phb_realize; @@ -1058,6 +1448,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->cannot_instantiate_with_device_add_yet = false; spc->finish_realize = spapr_phb_finish_realize; + hp->plug = spapr_phb_hot_plug_child; + hp->unplug = spapr_phb_hot_unplug_child; } static const TypeInfo spapr_phb_info = { @@ -1066,6 +1458,10 @@ static const TypeInfo spapr_phb_info = { .instance_size = sizeof(sPAPRPHBState), .class_init = spapr_phb_class_init, .class_size = sizeof(sPAPRPHBClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) @@ -1079,45 +1475,11 @@ PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) return PCI_HOST_BRIDGE(dev); } -/* Macros to operate with address in OF binding to PCI */ -#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p)) -#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */ -#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */ -#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */ -#define b_ss(x) b_x((x), 24, 2) /* the space code */ -#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */ -#define b_ddddd(x) b_x((x), 11, 5) /* device number */ -#define b_fff(x) b_x((x), 8, 3) /* function number */ -#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ - -typedef struct sPAPRTCEDT { - void *fdt; - int node_off; -} sPAPRTCEDT; - -static int spapr_phb_children_dt(Object *child, void *opaque) -{ - sPAPRTCEDT *p = opaque; - sPAPRTCETable *tcet; - - tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); - if (!tcet) { - return 0; - } - - spapr_dma_dt(p->fdt, p->node_off, "ibm,dma-window", - tcet->liobn, tcet->bus_offset, - tcet->nb_table << tcet->page_shift); - /* Stop after the first window */ - - return 1; -} - int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, void *fdt) { - int bus_off, i, j; + int bus_off, i, j, ret; char nodename[256]; uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) }; const uint64_t mmiosize = memory_region_size(&phb->memwindow); @@ -1151,6 +1513,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t interrupt_map_mask[] = { cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)}; uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7]; + sPAPRTCETable *tcet; /* Start populating the FDT */ sprintf(nodename, "pci@%" PRIx64, phb->buid); @@ -1159,14 +1522,6 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, return bus_off; } -#define _FDT(exp) \ - do { \ - int ret = (exp); \ - if (ret < 0) { \ - return ret; \ - } \ - } while (0) - /* Write PHB properties */ _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); _FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB")); @@ -1203,8 +1558,16 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, sizeof(interrupt_map))); - object_child_foreach(OBJECT(phb), spapr_phb_children_dt, - &((sPAPRTCEDT){ .fdt = fdt, .node_off = bus_off })); + tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0)); + spapr_dma_dt(fdt, bus_off, "ibm,dma-window", + tcet->liobn, tcet->bus_offset, + tcet->nb_table << tcet->page_shift); + + ret = spapr_drc_populate_dt(fdt, bus_off, OBJECT(phb), + SPAPR_DR_CONNECTOR_TYPE_PCI); + if (ret) { + return ret; + } return 0; } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 0f1ae55828..fa28d43f81 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -35,6 +35,55 @@ #include "qapi-event.h" #include <libfdt.h> +#include "hw/ppc/spapr_drc.h" + +/* #define DEBUG_SPAPR */ + +#ifdef DEBUG_SPAPR +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPREnvironment *spapr, + uint32_t drc_index) +{ + sPAPRConfigureConnectorState *ccs = NULL; + + QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) { + if (ccs->drc_index == drc_index) { + break; + } + } + + return ccs; +} + +static void spapr_ccs_add(sPAPREnvironment *spapr, + sPAPRConfigureConnectorState *ccs) +{ + g_assert(!spapr_ccs_find(spapr, ccs->drc_index)); + QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next); +} + +static void spapr_ccs_remove(sPAPREnvironment *spapr, + sPAPRConfigureConnectorState *ccs) +{ + QTAILQ_REMOVE(&spapr->ccs_list, ccs, next); + g_free(ccs); +} + +void spapr_ccs_reset_hook(void *opaque) +{ + sPAPREnvironment *spapr = opaque; + sPAPRConfigureConnectorState *ccs, *ccs_tmp; + + QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) { + spapr_ccs_remove(spapr, ccs); + } +} static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, @@ -245,6 +294,308 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu, rtas_st(rets, 0, ret); } +static void rtas_set_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + int32_t power_domain; + + if (nargs != 2 || nret != 2) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + /* we currently only use a single, "live insert" powerdomain for + * hotplugged/dlpar'd resources, so the power is always live/full (100) + */ + power_domain = rtas_ld(args, 0); + if (power_domain != -1) { + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); + return; + } + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + rtas_st(rets, 1, 100); +} + +static void rtas_get_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + int32_t power_domain; + + if (nargs != 1 || nret != 2) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + /* we currently only use a single, "live insert" powerdomain for + * hotplugged/dlpar'd resources, so the power is always live/full (100) + */ + power_domain = rtas_ld(args, 0); + if (power_domain != -1) { + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); + return; + } + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + rtas_st(rets, 1, 100); +} + +static bool sensor_type_is_dr(uint32_t sensor_type) +{ + switch (sensor_type) { + case RTAS_SENSOR_TYPE_ISOLATION_STATE: + case RTAS_SENSOR_TYPE_DR: + case RTAS_SENSOR_TYPE_ALLOCATION_STATE: + return true; + } + + return false; +} + +static void rtas_set_indicator(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint32_t sensor_type; + uint32_t sensor_index; + uint32_t sensor_state; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + + if (nargs != 3 || nret != 1) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + sensor_type = rtas_ld(args, 0); + sensor_index = rtas_ld(args, 1); + sensor_state = rtas_ld(args, 2); + + if (!sensor_type_is_dr(sensor_type)) { + goto out_unimplemented; + } + + /* if this is a DR sensor we can assume sensor_index == drc_index */ + drc = spapr_dr_connector_by_index(sensor_index); + if (!drc) { + DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n", + sensor_index); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + switch (sensor_type) { + case RTAS_SENSOR_TYPE_ISOLATION_STATE: + /* if the guest is configuring a device attached to this + * DRC, we should reset the configuration state at this + * point since it may no longer be reliable (guest released + * device and needs to start over, or unplug occurred so + * the FDT is no longer valid) + */ + if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { + sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr, + sensor_index); + if (ccs) { + spapr_ccs_remove(spapr, ccs); + } + } + drck->set_isolation_state(drc, sensor_state); + break; + case RTAS_SENSOR_TYPE_DR: + drck->set_indicator_state(drc, sensor_state); + break; + case RTAS_SENSOR_TYPE_ALLOCATION_STATE: + drck->set_allocation_state(drc, sensor_state); + break; + default: + goto out_unimplemented; + } + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + return; + +out_unimplemented: + /* currently only DR-related sensors are implemented */ + DPRINTF("rtas_set_indicator: sensor/indicator not implemented: %d\n", + sensor_type); + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); +} + +static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint32_t sensor_type; + uint32_t sensor_index; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint32_t entity_sense; + + if (nargs != 2 || nret != 2) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + sensor_type = rtas_ld(args, 0); + sensor_index = rtas_ld(args, 1); + + if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) { + /* currently only DR-related sensors are implemented */ + DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n", + sensor_type); + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); + return; + } + + drc = spapr_dr_connector_by_index(sensor_index); + if (!drc) { + DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n", + sensor_index); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + entity_sense = drck->entity_sense(drc); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + rtas_st(rets, 1, entity_sense); +} + +/* configure-connector work area offsets, int32_t units for field + * indexes, bytes for field offset/len values. + * + * as documented by PAPR+ v2.7, 13.5.3.5 + */ +#define CC_IDX_NODE_NAME_OFFSET 2 +#define CC_IDX_PROP_NAME_OFFSET 2 +#define CC_IDX_PROP_LEN 3 +#define CC_IDX_PROP_DATA_OFFSET 4 +#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4) +#define CC_WA_LEN 4096 + +static void rtas_ibm_configure_connector(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint64_t wa_addr; + uint64_t wa_offset; + uint32_t drc_index; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + sPAPRConfigureConnectorState *ccs; + sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; + int rc; + const void *fdt; + + if (nargs != 2 || nret != 1) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0); + + drc_index = rtas_ld(wa_addr, 0); + drc = spapr_dr_connector_by_index(drc_index); + if (!drc) { + DPRINTF("rtas_ibm_configure_connector: invalid DRC index: %xh\n", + drc_index); + rc = RTAS_OUT_PARAM_ERROR; + goto out; + } + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + fdt = drck->get_fdt(drc, NULL); + + ccs = spapr_ccs_find(spapr, drc_index); + if (!ccs) { + ccs = g_new0(sPAPRConfigureConnectorState, 1); + (void)drck->get_fdt(drc, &ccs->fdt_offset); + ccs->drc_index = drc_index; + spapr_ccs_add(spapr, ccs); + } + + do { + uint32_t tag; + const char *name; + const struct fdt_property *prop; + int fdt_offset_next, prop_len; + + tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next); + + switch (tag) { + case FDT_BEGIN_NODE: + ccs->fdt_depth++; + name = fdt_get_name(fdt, ccs->fdt_offset, NULL); + + /* provide the name of the next OF node */ + wa_offset = CC_VAL_DATA_OFFSET; + rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset); + rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, + (uint8_t *)name, strlen(name) + 1); + resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; + break; + case FDT_END_NODE: + ccs->fdt_depth--; + if (ccs->fdt_depth == 0) { + /* done sending the device tree, don't need to track + * the state anymore + */ + drck->set_configured(drc); + spapr_ccs_remove(spapr, ccs); + ccs = NULL; + resp = SPAPR_DR_CC_RESPONSE_SUCCESS; + } else { + resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT; + } + break; + case FDT_PROP: + prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset, + &prop_len); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + + /* provide the name of the next OF property */ + wa_offset = CC_VAL_DATA_OFFSET; + rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset); + rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, + (uint8_t *)name, strlen(name) + 1); + + /* provide the length and value of the OF property. data gets + * placed immediately after NULL terminator of the OF property's + * name string + */ + wa_offset += strlen(name) + 1, + rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len); + rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset); + rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, + (uint8_t *)((struct fdt_property *)prop)->data, + prop_len); + resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; + break; + case FDT_END: + resp = SPAPR_DR_CC_RESPONSE_ERROR; + default: + /* keep seeking for an actionable tag */ + break; + } + if (ccs) { + ccs->fdt_offset = fdt_offset_next; + } + } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE); + + rc = resp; +out: + rtas_st(rets, 0, rc); +} + static struct rtas_call { const char *name; spapr_rtas_fn fn; @@ -370,6 +721,16 @@ static void core_rtas_register_types(void) rtas_ibm_set_system_parameter); spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term", rtas_ibm_os_term); + spapr_rtas_register(RTAS_SET_POWER_LEVEL, "set-power-level", + rtas_set_power_level); + spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level", + rtas_get_power_level); + spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", + rtas_set_indicator); + spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state", + rtas_get_sensor_state); + spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector", + rtas_ibm_configure_connector); } type_init(core_rtas_register_types) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 1360b97ab0..174033dd41 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -469,7 +469,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) } if (pc->rtce_window_size) { - uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg; + uint32_t liobn = SPAPR_VIO_LIOBN(dev->reg); memory_region_init(&dev->mrroot, OBJECT(dev), "iommu-spapr-root", ram_size); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index a69bf2da45..8a3599c403 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -897,7 +897,6 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, espdma_irq, ledma_irq; qemu_irq esp_reset, dma_enable; qemu_irq fdc_tc; - qemu_irq *cpu_halt; unsigned long kernel_size; DriveInfo *fd[MAX_FD]; FWCfgState *fw_cfg; @@ -1024,9 +1023,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, escc_init(hwdef->serial_base, slavio_irq[15], slavio_irq[15], serial_hds[0], serial_hds[1], ESCC_CLOCK, 1); - cpu_halt = qemu_allocate_irqs(cpu_halt_signal, NULL, 1); if (hwdef->apc_base) { - apc_init(hwdef->apc_base, cpu_halt[0]); + apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0)); } if (hwdef->fd_base) { @@ -1036,7 +1034,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd, &fdc_tc); } else { - fdc_tc = *qemu_allocate_irqs(dummy_fdc_tc, NULL, 1); + fdc_tc = qemu_allocate_irq(dummy_fdc_tc, NULL, 0); } slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base, diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 145291016b..d53f39ad62 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -280,14 +280,12 @@ static int sp804_init(SysBusDevice *sbd) { DeviceState *dev = DEVICE(sbd); SP804State *s = SP804(dev); - qemu_irq *qi; - qi = qemu_allocate_irqs(sp804_set_irq, s, 2); sysbus_init_irq(sbd, &s->irq); s->timer[0] = arm_timer_init(s->freq0); s->timer[1] = arm_timer_init(s->freq1); - s->timer[0]->irq = qi[0]; - s->timer[1]->irq = qi[1]; + s->timer[0]->irq = qemu_allocate_irq(sp804_set_irq, s, 0); + s->timer[1]->irq = qemu_allocate_irq(sp804_set_irq, s, 1); memory_region_init_io(&s->iomem, OBJECT(s), &sp804_ops, s, "sp804", 0x1000); sysbus_init_mmio(sbd, &s->iomem); diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index cc9a21a712..703e29d6d3 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -40,15 +40,15 @@ static void puv3_intc_cpu_handler(void *opaque, int irq, int level) static void puv3_soc_init(CPUUniCore32State *env) { - qemu_irq *cpu_intc, irqs[PUV3_IRQS_NR]; + qemu_irq cpu_intc, irqs[PUV3_IRQS_NR]; DeviceState *dev; MemoryRegion *i8042 = g_new(MemoryRegion, 1); int i; /* Initialize interrupt controller */ - cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler, - uc32_env_get_cpu(env), 1); - dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, *cpu_intc); + cpu_intc = qemu_allocate_irq(puv3_intc_cpu_handler, + uc32_env_get_cpu(env), 0); + dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, cpu_intc); for (i = 0; i < PUV3_IRQS_NR; i++) { irqs[i] = qdev_get_gpio_in(dev, i); } diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 5c7b8c20fa..e37873384f 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -42,7 +42,7 @@ static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len, } /* Ignore regions with dirty logging, we cannot mark them dirty */ - if (memory_region_is_logging(section.mr)) { + if (memory_region_get_dirty_log_mask(section.mr)) { goto out; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 54851b7614..a7fe3c5104 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -22,15 +22,19 @@ #include "hw/virtio/virtio-bus.h" #include "migration/migration.h" +static struct vhost_log *vhost_log; + static void vhost_dev_sync_region(struct vhost_dev *dev, MemoryRegionSection *section, uint64_t mfirst, uint64_t mlast, uint64_t rfirst, uint64_t rlast) { + vhost_log_chunk_t *log = dev->log->log; + uint64_t start = MAX(mfirst, rfirst); uint64_t end = MIN(mlast, rlast); - vhost_log_chunk_t *from = dev->log + start / VHOST_LOG_CHUNK; - vhost_log_chunk_t *to = dev->log + end / VHOST_LOG_CHUNK + 1; + vhost_log_chunk_t *from = log + start / VHOST_LOG_CHUNK; + vhost_log_chunk_t *to = log + end / VHOST_LOG_CHUNK + 1; uint64_t addr = (start / VHOST_LOG_CHUNK) * VHOST_LOG_CHUNK; if (end < start) { @@ -280,22 +284,57 @@ static uint64_t vhost_get_log_size(struct vhost_dev *dev) } return log_size; } +static struct vhost_log *vhost_log_alloc(uint64_t size) +{ + struct vhost_log *log = g_malloc0(sizeof *log + size * sizeof(*(log->log))); + + log->size = size; + log->refcnt = 1; + + return log; +} + +static struct vhost_log *vhost_log_get(uint64_t size) +{ + if (!vhost_log || vhost_log->size != size) { + vhost_log = vhost_log_alloc(size); + } else { + ++vhost_log->refcnt; + } + + return vhost_log; +} + +static void vhost_log_put(struct vhost_dev *dev, bool sync) +{ + struct vhost_log *log = dev->log; + + if (!log) { + return; + } + + --log->refcnt; + if (log->refcnt == 0) { + /* Sync only the range covered by the old log */ + if (dev->log_size && sync) { + vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1); + } + if (vhost_log == log) { + vhost_log = NULL; + } + g_free(log); + } +} static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size) { - vhost_log_chunk_t *log; - uint64_t log_base; + struct vhost_log *log = vhost_log_get(size); + uint64_t log_base = (uintptr_t)log->log; int r; - log = g_malloc0(size * sizeof *log); - log_base = (uintptr_t)log; r = dev->vhost_ops->vhost_call(dev, VHOST_SET_LOG_BASE, &log_base); assert(r >= 0); - /* Sync only the range covered by the old log */ - if (dev->log_size) { - vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1); - } - g_free(dev->log); + vhost_log_put(dev, true); dev->log = log; dev->log_size = size; } @@ -377,7 +416,8 @@ static void vhost_set_memory(MemoryListener *listener, memory_listener); hwaddr start_addr = section->offset_within_address_space; ram_addr_t size = int128_get64(section->size); - bool log_dirty = memory_region_is_logging(section->mr); + bool log_dirty = + memory_region_get_dirty_log_mask(section->mr) & ~(1 << DIRTY_MEMORY_MIGRATION); int s = offsetof(struct vhost_memory, regions) + (dev->mem->nregions + 1) * sizeof dev->mem->regions[0]; void *ram; @@ -601,7 +641,7 @@ static int vhost_migration_log(MemoryListener *listener, int enable) if (r < 0) { return r; } - g_free(dev->log); + vhost_log_put(dev, false); dev->log = NULL; dev->log_size = 0; } else { @@ -636,13 +676,15 @@ static void vhost_log_global_stop(MemoryListener *listener) } static void vhost_log_start(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { /* FIXME: implement */ } static void vhost_log_stop(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { /* FIXME: implement */ } @@ -1060,10 +1102,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) uint64_t log_base; hdev->log_size = vhost_get_log_size(hdev); - hdev->log = hdev->log_size ? - g_malloc0(hdev->log_size * sizeof *hdev->log) : NULL; - log_base = (uintptr_t)hdev->log; - r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, &log_base); + hdev->log = vhost_log_get(hdev->log_size); + log_base = (uintptr_t)hdev->log->log; + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, + hdev->log_size ? &log_base : NULL); if (r < 0) { r = -errno; goto fail_log; @@ -1072,6 +1114,9 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) return 0; fail_log: + if (hdev->log_size) { + vhost_log_put(hdev, false); + } fail_vq: while (--i >= 0) { vhost_virtqueue_stop(hdev, @@ -1098,10 +1143,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->vqs + i, hdev->vq_index + i); } - vhost_log_sync_range(hdev, 0, ~0x0ull); + vhost_log_put(hdev, true); hdev->started = false; - g_free(hdev->log); hdev->log = NULL; hdev->log_size = 0; } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index d1ddc39b6f..6d4f64e282 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -632,21 +632,26 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector, if (!virtio_queue_get_num(vdev, index)) { break; } - ret = virtio_pci_vq_vector_unmask(proxy, index, vector, msg); - if (ret < 0) { - goto undo; + if (index < proxy->nvqs_with_notifiers) { + ret = virtio_pci_vq_vector_unmask(proxy, index, vector, msg); + if (ret < 0) { + goto undo; + } + ++unmasked; } vq = virtio_vector_next_queue(vq); - ++unmasked; } return 0; undo: vq = virtio_vector_first_queue(vdev, vector); - while (vq && --unmasked >= 0) { + while (vq && unmasked >= 0) { index = virtio_get_queue_index(vq); - virtio_pci_vq_vector_mask(proxy, index, vector); + if (index < proxy->nvqs_with_notifiers) { + virtio_pci_vq_vector_mask(proxy, index, vector); + --unmasked; + } vq = virtio_vector_next_queue(vq); } return ret; @@ -664,7 +669,9 @@ static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector) if (!virtio_queue_get_num(vdev, index)) { break; } - virtio_pci_vq_vector_mask(proxy, index, vector); + if (index < proxy->nvqs_with_notifiers) { + virtio_pci_vq_vector_mask(proxy, index, vector); + } vq = virtio_vector_next_queue(vq); } } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 596e3d8aaf..8ac6156861 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1003,7 +1003,7 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) vmstate_save_state(f, &vmstate_virtio, vdev, NULL); } -int virtio_set_features(VirtIODevice *vdev, uint32_t val) +int virtio_set_features(VirtIODevice *vdev, uint64_t val) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); bool bad = (val & ~(vdev->host_features)) != 0; diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index 3f56546066..d5aecaf49e 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -27,6 +27,7 @@ #include <inttypes.h> #include "qemu/osdep.h" #include "qemu/queue.h" +#include "tcg-target.h" #ifndef CONFIG_USER_ONLY #include "exec/hwaddr.h" #endif @@ -70,8 +71,6 @@ typedef uint64_t target_ulong; #define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE) #if !defined(CONFIG_USER_ONLY) -#define CPU_TLB_BITS 8 -#define CPU_TLB_SIZE (1 << CPU_TLB_BITS) /* use a fully associative victim tlb of 8 entries */ #define CPU_VTLB_SIZE 8 @@ -81,6 +80,38 @@ typedef uint64_t target_ulong; #define CPU_TLB_ENTRY_BITS 5 #endif +/* TCG_TARGET_TLB_DISPLACEMENT_BITS is used in CPU_TLB_BITS to ensure that + * the TLB is not unnecessarily small, but still small enough for the + * TLB lookup instruction sequence used by the TCG target. + * + * TCG will have to generate an operand as large as the distance between + * env and the tlb_table[NB_MMU_MODES - 1][0].addend. For simplicity, + * the TCG targets just round everything up to the next power of two, and + * count bits. This works because: 1) the size of each TLB is a largish + * power of two, 2) and because the limit of the displacement is really close + * to a power of two, 3) the offset of tlb_table[0][0] inside env is smaller + * than the size of a TLB. + * + * For example, the maximum displacement 0xFFF0 on PPC and MIPS, but TCG + * just says "the displacement is 16 bits". TCG_TARGET_TLB_DISPLACEMENT_BITS + * then ensures that tlb_table at least 0x8000 bytes large ("not unnecessarily + * small": 2^15). The operand then will come up smaller than 0xFFF0 without + * any particular care, because the TLB for a single MMU mode is larger than + * 0x10000-0xFFF0=16 bytes. In the end, the maximum value of the operand + * could be something like 0xC000 (the offset of the last TLB table) plus + * 0x18 (the offset of the addend field in each TLB entry) plus the offset + * of tlb_table inside env (which is non-trivial but not huge). + */ +#define CPU_TLB_BITS \ + MIN(8, \ + TCG_TARGET_TLB_DISPLACEMENT_BITS - CPU_TLB_ENTRY_BITS - \ + (NB_MMU_MODES <= 1 ? 0 : \ + NB_MMU_MODES <= 2 ? 1 : \ + NB_MMU_MODES <= 4 ? 2 : \ + NB_MMU_MODES <= 8 ? 3 : 4)) + +#define CPU_TLB_SIZE (1 << CPU_TLB_BITS) + typedef struct CPUTLBEntry { /* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address bit TARGET_PAGE_BITS-1..4 : Nonzero for accesses that should not diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 1673287189..0ec398c0f8 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -263,12 +263,104 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #undef MEMSUFFIX #endif /* (NB_MMU_MODES >= 7) */ -#if (NB_MMU_MODES > 7) -/* Note that supporting NB_MMU_MODES == 9 would require - * changes to at least the ARM TCG backend. - */ -#error "NB_MMU_MODES > 7 is not supported for now" -#endif /* (NB_MMU_MODES > 7) */ +#if (NB_MMU_MODES >= 8) && defined(MMU_MODE7_SUFFIX) + +#define CPU_MMU_INDEX 7 +#define MEMSUFFIX MMU_MODE7_SUFFIX +#define DATA_SIZE 1 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_template.h" +#undef CPU_MMU_INDEX +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 8) */ + +#if (NB_MMU_MODES >= 9) && defined(MMU_MODE8_SUFFIX) + +#define CPU_MMU_INDEX 8 +#define MEMSUFFIX MMU_MODE8_SUFFIX +#define DATA_SIZE 1 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_template.h" +#undef CPU_MMU_INDEX +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 9) */ + +#if (NB_MMU_MODES >= 10) && defined(MMU_MODE9_SUFFIX) + +#define CPU_MMU_INDEX 9 +#define MEMSUFFIX MMU_MODE9_SUFFIX +#define DATA_SIZE 1 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_template.h" +#undef CPU_MMU_INDEX +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 10) */ + +#if (NB_MMU_MODES >= 11) && defined(MMU_MODE10_SUFFIX) + +#define CPU_MMU_INDEX 10 +#define MEMSUFFIX MMU_MODE10_SUFFIX +#define DATA_SIZE 1 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_template.h" +#undef CPU_MMU_INDEX +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 11) */ + +#if (NB_MMU_MODES >= 12) && defined(MMU_MODE11_SUFFIX) + +#define CPU_MMU_INDEX 11 +#define MEMSUFFIX MMU_MODE11_SUFFIX +#define DATA_SIZE 1 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_template.h" +#undef CPU_MMU_INDEX +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 12) */ + +#if (NB_MMU_MODES > 12) +#error "NB_MMU_MODES > 12 is not supported for now" +#endif /* (NB_MMU_MODES > 12) */ /* these access are slower, they must be as rare as possible */ #define CPU_MMU_INDEX (cpu_mmu_index(env)) diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index e0da9d7ad3..360815e1b4 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -22,8 +22,7 @@ #if !defined(CONFIG_USER_ONLY) /* cputlb.c */ void tlb_protect_code(ram_addr_t ram_addr); -void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr, - target_ulong vaddr); +void tlb_unprotect_code(ram_addr_t ram_addr); void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, uintptr_t length); void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index b58cd47ced..2f7a4f1700 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -90,11 +90,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, int cflags); void cpu_exec_init(CPUArchState *env); void QEMU_NORETURN cpu_loop_exit(CPUState *cpu); -int page_unprotect(target_ulong address, uintptr_t pc, void *puc); -void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access); -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access); + #if !defined(CONFIG_USER_ONLY) bool qemu_in_vcpu_thread(void); void cpu_reload_memory_map(CPUState *cpu); diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 96dc440423..f8537a8d91 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -29,7 +29,9 @@ typedef struct MemTxAttrs { * "didn't specify" if necessary. */ unsigned int unspecified:1; - /* ARM/AMBA TrustZone Secure access */ + /* ARM/AMBA: TrustZone Secure access + * x86: System Management Mode access + */ unsigned int secure:1; /* Memory access is usermode (unprivileged) */ unsigned int user:1; diff --git a/include/exec/memory.h b/include/exec/memory.h index b61c84f62a..8ae004eb06 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -206,8 +206,10 @@ struct MemoryListener { void (*region_add)(MemoryListener *listener, MemoryRegionSection *section); void (*region_del)(MemoryListener *listener, MemoryRegionSection *section); void (*region_nop)(MemoryListener *listener, MemoryRegionSection *section); - void (*log_start)(MemoryListener *listener, MemoryRegionSection *section); - void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_start)(MemoryListener *listener, MemoryRegionSection *section, + int old, int new); + void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section, + int old, int new); void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section); void (*log_global_start)(MemoryListener *listener); void (*log_global_stop)(MemoryListener *listener); @@ -591,11 +593,23 @@ const char *memory_region_name(const MemoryRegion *mr); /** * memory_region_is_logging: return whether a memory region is logging writes * - * Returns %true if the memory region is logging writes + * Returns %true if the memory region is logging writes for the given client * * @mr: the memory region being queried + * @client: the client being queried */ -bool memory_region_is_logging(MemoryRegion *mr); +bool memory_region_is_logging(MemoryRegion *mr, uint8_t client); + +/** + * memory_region_get_dirty_log_mask: return the clients for which a + * memory region is logging writes. + * + * Returns a bitmap of clients, in which the DIRTY_MEMORY_* constants + * are the bit indices. + * + * @mr: the memory region being queried + */ +uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr); /** * memory_region_is_rom: check whether a memory region is ROM @@ -647,8 +661,7 @@ void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, * * @mr: the memory region being updated. * @log: whether dirty logging is to be enabled or disabled. - * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or - * %DIRTY_MEMORY_VGA. + * @client: the user of the logging information; %DIRTY_MEMORY_VGA only. */ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client); diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index ff558a4734..c113f21140 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -41,6 +41,9 @@ void qemu_ram_free_from_ptr(ram_addr_t addr); int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp); +#define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) +#define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) + static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) @@ -56,7 +59,7 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, return next < end; } -static inline bool cpu_physical_memory_get_clean(ram_addr_t start, +static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { @@ -68,7 +71,7 @@ static inline bool cpu_physical_memory_get_clean(ram_addr_t start, page = start >> TARGET_PAGE_BITS; next = find_next_zero_bit(ram_list.dirty_memory[client], end, page); - return next < end; + return next >= end; } static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, @@ -86,44 +89,52 @@ static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) return !(vga && code && migration); } -static inline bool cpu_physical_memory_range_includes_clean(ram_addr_t start, - ram_addr_t length) +static inline uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, + ram_addr_t length, + uint8_t mask) { - bool vga = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_VGA); - bool code = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_CODE); - bool migration = - cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_MIGRATION); - return vga || code || migration; + uint8_t ret = 0; + + if (mask & (1 << DIRTY_MEMORY_VGA) && + !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_VGA)) { + ret |= (1 << DIRTY_MEMORY_VGA); + } + if (mask & (1 << DIRTY_MEMORY_CODE) && + !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_CODE)) { + ret |= (1 << DIRTY_MEMORY_CODE); + } + if (mask & (1 << DIRTY_MEMORY_MIGRATION) && + !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_MIGRATION)) { + ret |= (1 << DIRTY_MEMORY_MIGRATION); + } + return ret; } static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) { assert(client < DIRTY_MEMORY_NUM); - set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); -} - -static inline void cpu_physical_memory_set_dirty_range_nocode(ram_addr_t start, - ram_addr_t length) -{ - unsigned long end, page; - - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); + set_bit_atomic(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, - ram_addr_t length) + ram_addr_t length, + uint8_t mask) { unsigned long end, page; + unsigned long **d = ram_list.dirty_memory; end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page); + if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { + bitmap_set_atomic(d[DIRTY_MEMORY_MIGRATION], page, end - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { + bitmap_set_atomic(d[DIRTY_MEMORY_VGA], page, end - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { + bitmap_set_atomic(d[DIRTY_MEMORY_CODE], page, end - page); + } xen_modified_memory(start, length); } @@ -149,14 +160,18 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, for (k = 0; k < nr; k++) { if (bitmap[k]) { unsigned long temp = leul_to_cpu(bitmap[k]); + unsigned long **d = ram_list.dirty_memory; - ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION][page + k] |= temp; - ram_list.dirty_memory[DIRTY_MEMORY_VGA][page + k] |= temp; - ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp; + atomic_or(&d[DIRTY_MEMORY_MIGRATION][page + k], temp); + atomic_or(&d[DIRTY_MEMORY_VGA][page + k], temp); + if (tcg_enabled()) { + atomic_or(&d[DIRTY_MEMORY_CODE][page + k], temp); + } } } - xen_modified_memory(start, pages); + xen_modified_memory(start, pages << TARGET_PAGE_BITS); } else { + uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; /* * bitmap-traveling is faster than memory-traveling (for addr...) * especially when most of the memory is not dirty. @@ -171,7 +186,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, addr = page_number * TARGET_PAGE_SIZE; ram_addr = start + addr; cpu_physical_memory_set_dirty_range(ram_addr, - TARGET_PAGE_SIZE * hpratio); + TARGET_PAGE_SIZE * hpratio, clients); } while (c != 0); } } @@ -179,29 +194,60 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } #endif /* not _WIN32 */ -static inline void cpu_physical_memory_clear_dirty_range_type(ram_addr_t start, - ram_addr_t length, - unsigned client) -{ - unsigned long end, page; - - assert(client < DIRTY_MEMORY_NUM); - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - bitmap_clear(ram_list.dirty_memory[client], page, end - page); -} +bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client); static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, ram_addr_t length) { - cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_MIGRATION); - cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_VGA); - cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_CODE); + cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_VGA); + cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_CODE); } -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, - unsigned client); +static inline +uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, + ram_addr_t start, + ram_addr_t length) +{ + ram_addr_t addr; + unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); + uint64_t num_dirty = 0; + + /* start address is aligned at the start of a word? */ + if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { + int k; + int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); + unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]; + + for (k = page; k < page + nr; k++) { + if (src[k]) { + unsigned long bits = atomic_xchg(&src[k], 0); + unsigned long new_dirty; + new_dirty = ~dest[k]; + dest[k] |= bits; + new_dirty &= bits; + num_dirty += ctpopl(new_dirty); + } + } + } else { + for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_test_and_clear_dirty( + start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { + long k = (start + addr) >> TARGET_PAGE_BITS; + if (!test_and_set_bit(k, dest)) { + num_dirty++; + } + } + } + } + + return num_dirty; +} #endif #endif diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index 1f678b4bf2..b20bd55a67 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -91,6 +91,13 @@ /* PM2_CNT */ #define ACPI_BITMASK_ARB_DISABLE 0x0001 +/* These values are part of guest ABI, and can not be changed */ +typedef enum { + ACPI_PCI_HOTPLUG_STATUS = 2, + ACPI_CPU_HOTPLUG_STATUS = 4, + ACPI_MEMORY_HOTPLUG_STATUS = 8, +} AcpiGPEStatusBits; + /* structs */ typedef struct ACPIPMTimer ACPIPMTimer; typedef struct ACPIPM1EVT ACPIPM1EVT; @@ -160,7 +167,8 @@ void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, MemoryRegion *parent); /* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */ -void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val); +void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, + bool disable_s3, bool disable_s4, uint8_t s4_val); void acpi_pm1_cnt_update(ACPIREGS *ar, bool sci_enable, bool sci_disable); void acpi_pm1_cnt_reset(ACPIREGS *ar); @@ -172,6 +180,9 @@ void acpi_gpe_reset(ACPIREGS *ar); void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val); uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr); +void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq, + AcpiGPEStatusBits status); + void acpi_update_sci(ACPIREGS *acpi_regs, qemu_irq irq); /* acpi.c */ diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index c2d3dba0c7..77cc65cbc2 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -39,6 +39,7 @@ typedef struct ICH9LPCPMRegs { MemoryRegion io_smi; uint32_t smi_en; + uint32_t smi_en_wmask; uint32_t smi_sts; qemu_irq irq; /* SCI */ diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h index 986223b16c..1342adb08f 100644 --- a/include/hw/acpi/memory_hotplug.h +++ b/include/hw/acpi/memory_hotplug.h @@ -5,8 +5,6 @@ #include "hw/acpi/acpi.h" #include "migration/vmstate.h" -#define ACPI_MEMORY_HOTPLUG_STATUS 8 - /** * MemStatus: * @is_removing: the memory device in slot has been requested to be ejected. diff --git a/include/hw/acpi/pc-hotplug.h b/include/hw/acpi/pc-hotplug.h index 0513c1c158..77b1569007 100644 --- a/include/hw/acpi/pc-hotplug.h +++ b/include/hw/acpi/pc-hotplug.h @@ -16,7 +16,6 @@ * ONLY DEFINEs are permited in this file since it's shared * between C and ASL code. */ -#define ACPI_CPU_HOTPLUG_STATUS 4 /* Limit for CPU arch IDs for CPU hotplug. All hotpluggable CPUs should * have CPUClass.get_arch_id() < ACPI_CPU_HOTPLUG_ID_LIMIT. diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index 5c940eb412..760804cc46 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -13,11 +13,21 @@ #include "exec/memory.h" #include "hw/irq.h" +#include "qemu/notify.h" /* armv7m.c */ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, const char *kernel_filename, const char *cpu_model); +/* + * struct used as a parameter of the arm_load_kernel machine init + * done notifier + */ +typedef struct { + Notifier notifier; /* actual notifier */ + ARMCPU *cpu; /* handle to the first cpu object */ +} ArmLoadKernelNotifier; + /* arm_boot.c */ struct arm_boot_info { uint64_t ram_size; @@ -64,6 +74,8 @@ struct arm_boot_info { * the user it should implement this hook. */ void (*modify_dtb)(const struct arm_boot_info *info, void *fdt); + /* machine init done notifier executing arm_load_dtb */ + ArmLoadKernelNotifier load_kernel_notifier; /* Used internally by arm_boot.c */ int is_linux; hwaddr initrd_start; @@ -75,6 +87,22 @@ struct arm_boot_info { */ bool firmware_loaded; }; + +/** + * arm_load_kernel - Loads memory with everything needed to boot + * + * @cpu: handle to the first CPU object + * @info: handle to the boot info struct + * Registers a machine init done notifier that copies to memory + * everything needed to boot, depending on machine and user options: + * kernel image, boot loaders, initrd, dtb. Also registers the CPU + * reset handler. + * + * In case the machine file supports the platform bus device and its + * dynamically instantiable sysbus devices, this function must be called + * before sysbus-fdt arm_register_platform_bus_fdt_creator. Indeed the + * machine init done notifiers are called in registration reverse order. + */ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); /* Multiplication factor to convert from system clock ticks to qemu timer diff --git a/include/hw/arm/sysbus-fdt.h b/include/hw/arm/sysbus-fdt.h new file mode 100644 index 0000000000..e15bb81807 --- /dev/null +++ b/include/hw/arm/sysbus-fdt.h @@ -0,0 +1,60 @@ +/* + * Dynamic sysbus device tree node generation API + * + * Copyright Linaro Limited, 2014 + * + * Authors: + * Alex Graf <agraf@suse.de> + * Eric Auger <eric.auger@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef HW_ARM_SYSBUS_FDT_H +#define HW_ARM_SYSBUS_FDT_H + +#include "hw/arm/arm.h" +#include "qemu-common.h" +#include "hw/sysbus.h" + +/* + * struct that contains dimensioning parameters of the platform bus + */ +typedef struct { + hwaddr platform_bus_base; /* start address of the bus */ + hwaddr platform_bus_size; /* size of the bus */ + int platform_bus_first_irq; /* first hwirq assigned to the bus */ + int platform_bus_num_irqs; /* number of hwirq assigned to the bus */ +} ARMPlatformBusSystemParams; + +/* + * struct that contains all relevant info to build the fdt nodes of + * platform bus and attached dynamic sysbus devices + * in the future might be augmented with additional info + * such as PHY, CLK handles ... + */ +typedef struct { + const ARMPlatformBusSystemParams *system_params; + struct arm_boot_info *binfo; + const char *intc; /* parent interrupt controller name */ +} ARMPlatformBusFDTParams; + +/** + * arm_register_platform_bus_fdt_creator - register a machine init done + * notifier that creates the device tree nodes of the platform bus and + * associated dynamic sysbus devices + */ +void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params); + +#endif diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index ceec8b3664..d22fd8e508 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -32,6 +32,7 @@ #include "qemu-common.h" +#define NUM_GICV2M_SPIS 64 #define NUM_VIRTIO_TRANSPORTS 32 #define ARCH_TIMER_VIRT_IRQ 11 @@ -53,6 +54,8 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PCIE_ECAM, + VIRT_GIC_V2M, + VIRT_PLATFORM_BUS, }; typedef struct MemMapEntry { diff --git a/include/hw/boards.h b/include/hw/boards.h index ff79797ce4..6379901528 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -106,6 +106,7 @@ struct MachineClass { const char *default_display; GlobalProperty *compat_props; const char *hw_version; + ram_addr_t default_ram_size; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index f4e522cc1f..a2cc15c915 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -152,6 +152,12 @@ Object *ich9_lpc_find(void); #define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0) #define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80 +#define ICH9_LPC_GEN_PMCON_1 0xa0 +#define ICH9_LPC_GEN_PMCON_1_SMI_LOCK (1 << 4) +#define ICH9_LPC_GEN_PMCON_2 0xa2 +#define ICH9_LPC_GEN_PMCON_3 0xa4 +#define ICH9_LPC_GEN_PMCON_LOCK 0xa6 + #define ICH9_LPC_RCBA 0xf0 #define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14) #define ICH9_LPC_RCBA_EN 0x1 diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 27bd748eab..86c565147c 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -193,7 +193,7 @@ FWCfgState *pc_memory_init(MachineState *machine, MemoryRegion *rom_memory, MemoryRegion **ram_memory, PcGuestInfo *guest_info); -qemu_irq *pc_allocate_cpu_irq(void); +qemu_irq pc_allocate_cpu_irq(void); DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus); void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, @@ -210,7 +210,6 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus); void pc_pci_device_init(PCIBus *pci_bus); typedef void (*cpu_set_smm_t)(int smm, void *arg); -void cpu_smm_register(cpu_set_smm_t callback, void *arg); void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); @@ -218,8 +217,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, qemu_irq sci_irq, qemu_irq smi_irq, - int kvm_enabled, FWCfgState *fw_cfg, - DeviceState **piix4_pm); + int kvm_enabled, DeviceState **piix4_pm); void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr); /* hpet.c */ diff --git a/include/hw/pci-host/pam.h b/include/hw/pci-host/pam.h index 4d03e4bf18..6116c638f9 100644 --- a/include/hw/pci-host/pam.h +++ b/include/hw/pci-host/pam.h @@ -86,10 +86,6 @@ typedef struct PAMMemoryRegion { unsigned current; } PAMMemoryRegion; -void smram_update(MemoryRegion *smram_region, uint8_t smram, - uint8_t smm_enabled); -void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram, - MemoryRegion *smram_region); void init_pam(DeviceState *dev, MemoryRegion *ram, MemoryRegion *system, MemoryRegion *pci, PAMMemoryRegion *mem, uint32_t start, uint32_t size); void pam_update(PAMMemoryRegion *mem, int idx, uint8_t val); diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index 96d4cdc713..dbe6dc05b5 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -52,9 +52,10 @@ typedef struct MCHPCIState { MemoryRegion *system_memory; MemoryRegion *address_space_io; PAMMemoryRegion pam_regions[13]; - MemoryRegion smram_region; + MemoryRegion smram_region, open_high_smram; + MemoryRegion smram, low_smram, high_smram; + MemoryRegion tseg_blackhole, tseg_window; PcPciInfo pci_info; - uint8_t smm_enabled; ram_addr_t below_4g_mem_size; ram_addr_t above_4g_mem_size; uint64_t pci_hole64_size; @@ -127,8 +128,7 @@ typedef struct Q35PCIHost { #define MCH_HOST_BRIDGE_PAM_MASK ((uint8_t)0x3) #define MCH_HOST_BRIDGE_SMRAM 0x9d -#define MCH_HOST_BRIDGE_SMRAM_SIZE 1 -#define MCH_HOST_BRIDGE_SMRAM_DEFAULT ((uint8_t)0x2) +#define MCH_HOST_BRIDGE_SMRAM_SIZE 2 #define MCH_HOST_BRIDGE_SMRAM_D_OPEN ((uint8_t)(1 << 6)) #define MCH_HOST_BRIDGE_SMRAM_D_CLS ((uint8_t)(1 << 5)) #define MCH_HOST_BRIDGE_SMRAM_D_LCK ((uint8_t)(1 << 4)) @@ -139,18 +139,36 @@ typedef struct Q35PCIHost { #define MCH_HOST_BRIDGE_SMRAM_C_END 0xc0000 #define MCH_HOST_BRIDGE_SMRAM_C_SIZE 0x20000 #define MCH_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END 0x100000 +#define MCH_HOST_BRIDGE_SMRAM_DEFAULT \ + MCH_HOST_BRIDGE_SMRAM_C_BASE_SEG +#define MCH_HOST_BRIDGE_SMRAM_WMASK \ + (MCH_HOST_BRIDGE_SMRAM_D_OPEN | \ + MCH_HOST_BRIDGE_SMRAM_D_CLS | \ + MCH_HOST_BRIDGE_SMRAM_D_LCK | \ + MCH_HOST_BRIDGE_SMRAM_G_SMRAME) +#define MCH_HOST_BRIDGE_SMRAM_WMASK_LCK \ + MCH_HOST_BRIDGE_SMRAM_D_CLS #define MCH_HOST_BRIDGE_ESMRAMC 0x9e -#define MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME ((uint8_t)(1 << 6)) -#define MCH_HOST_BRIDGE_ESMRAMC_E_SMERR ((uint8_t)(1 << 5)) -#define MCH_HOST_BRIDGE_ESMRAMC_SM_CACHE ((uint8_t)(1 << 4)) -#define MCH_HOST_BRIDGE_ESMRAMC_SM_L1 ((uint8_t)(1 << 3)) -#define MCH_HOST_BRIDGE_ESMRAMC_SM_L2 ((uint8_t)(1 << 2)) +#define MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME ((uint8_t)(1 << 7)) +#define MCH_HOST_BRIDGE_ESMRAMC_E_SMERR ((uint8_t)(1 << 6)) +#define MCH_HOST_BRIDGE_ESMRAMC_SM_CACHE ((uint8_t)(1 << 5)) +#define MCH_HOST_BRIDGE_ESMRAMC_SM_L1 ((uint8_t)(1 << 4)) +#define MCH_HOST_BRIDGE_ESMRAMC_SM_L2 ((uint8_t)(1 << 3)) #define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK ((uint8_t)(0x3 << 1)) #define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB ((uint8_t)(0x0 << 1)) #define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB ((uint8_t)(0x1 << 1)) #define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB ((uint8_t)(0x2 << 1)) #define MCH_HOST_BRIDGE_ESMRAMC_T_EN ((uint8_t)1) +#define MCH_HOST_BRIDGE_ESMRAMC_DEFAULT \ + (MCH_HOST_BRIDGE_ESMRAMC_SM_CACHE | \ + MCH_HOST_BRIDGE_ESMRAMC_SM_L1 | \ + MCH_HOST_BRIDGE_ESMRAMC_SM_L2) +#define MCH_HOST_BRIDGE_ESMRAMC_WMASK \ + (MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME | \ + MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK | \ + MCH_HOST_BRIDGE_ESMRAMC_T_EN) +#define MCH_HOST_BRIDGE_ESMRAMC_WMASK_LCK 0 /* D1:F0 PCIE* port*/ #define MCH_PCIE_DEV 1 diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 895d273fee..9dca38837b 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -71,6 +71,7 @@ struct sPAPRPHBState { uint32_t index; uint64_t buid; char *dtbusname; + bool dr_enabled; MemoryRegion memspace, iospace; hwaddr mem_win_addr, mem_win_size, io_win_addr, io_win_size; @@ -114,6 +115,8 @@ struct sPAPRPHBVFIOState { #define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL +#define SPAPR_PCI_DMA32_SIZE 0x40000000 + static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) { return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); @@ -129,4 +132,8 @@ void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr); void spapr_pci_rtas_init(void); +sPAPRPHBState *spapr_pci_find_phb(sPAPREnvironment *spapr, uint64_t buid); +PCIDevice *spapr_pci_find_dev(sPAPREnvironment *spapr, uint64_t buid, + uint32_t config_addr); + #endif /* __HW_SPAPR_PCI_H__ */ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 5d050c830f..d44bc84d1e 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -91,6 +91,7 @@ #define PCI_DEVICE_ID_REDHAT_ROCKER 0x0006 #define PCI_DEVICE_ID_REDHAT_SDHCI 0x0007 #define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008 +#define PCI_DEVICE_ID_REDHAT_PXB 0x0009 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 @@ -334,12 +335,20 @@ int pci_device_load(PCIDevice *s, QEMUFile *f); MemoryRegion *pci_address_space(PCIDevice *dev); MemoryRegion *pci_address_space_io(PCIDevice *dev); +/* + * Should not normally be used by devices. For use by sPAPR target + * where QEMU emulates firmware. + */ +int pci_bar(PCIDevice *d, int reg); + typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); #define TYPE_PCI_BUS "PCI" #define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) +#define PCI_BUS_CLASS(klass) OBJECT_CLASS_CHECK(PCIBusClass, (klass), TYPE_PCI_BUS) +#define PCI_BUS_GET_CLASS(obj) OBJECT_GET_CLASS(PCIBusClass, (obj), TYPE_PCI_BUS) #define TYPE_PCIE_BUS "PCIE" bool pci_bus_is_express(PCIBus *bus); @@ -379,6 +388,7 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, PCIDevice *pci_vga_init(PCIBus *bus); int pci_bus_num(PCIBus *s); +int pci_bus_numa_node(PCIBus *bus); void pci_for_each_device(PCIBus *bus, int bus_num, void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), void *opaque); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index fabaeee86b..403fec6e58 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -8,6 +8,16 @@ * use accessor functions in pci.h, pci_bridge.h */ +typedef struct PCIBusClass { + /*< private >*/ + BusClass parent_class; + /*< public >*/ + + bool (*is_root)(PCIBus *bus); + int (*bus_num)(PCIBus *bus); + uint16_t (*numa_node)(PCIBus *bus); +} PCIBusClass; + struct PCIBus { BusState qbus; PCIIOMMUFunc iommu_fn; diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index c5803279da..c6870212e9 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -40,8 +40,8 @@ struct DBDMA_io { /* DMA is in progress, don't start another one */ bool processing; /* unaligned last sector of a request */ - uint8_t remainder[0x200]; - int remainder_len; + uint8_t head_remainder[0x200]; + uint8_t tail_remainder[0x200]; QEMUIOVector iov; }; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index af71e8b0d5..7b4b1bb3d7 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -3,10 +3,13 @@ #include "sysemu/dma.h" #include "hw/ppc/xics.h" +#include "hw/ppc/spapr_drc.h" struct VIOsPAPRBus; struct sPAPRPHBState; struct sPAPRNVRAM; +typedef struct sPAPRConfigureConnectorState sPAPRConfigureConnectorState; +typedef struct sPAPREventLogEntry sPAPREventLogEntry; #define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL @@ -31,14 +34,18 @@ typedef struct sPAPREnvironment { struct PPCTimebase tb; bool has_graphics; - uint32_t epow_irq; + uint32_t check_exception_irq; Notifier epow_notifier; + QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; /* Migration state */ int htab_save_index; bool htab_first_pass; int htab_fd; bool htab_fd_stale; + + /* RTAS state */ + QTAILQ_HEAD(, sPAPRConfigureConnectorState) ccs_list; } sPAPREnvironment; #define H_SUCCESS 0 @@ -430,6 +437,17 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); #define RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE 42 #define RTAS_SYSPARM_UUID 48 +/* RTAS indicator/sensor types + * + * as defined by PAPR+ 2.7 7.3.5.4, Table 41 + * + * NOTE: currently only DR-related sensors are implemented here + */ +#define RTAS_SENSOR_TYPE_ISOLATION_STATE 9001 +#define RTAS_SENSOR_TYPE_DR 9002 +#define RTAS_SENSOR_TYPE_ALLOCATION_STATE 9003 +#define RTAS_SENSOR_TYPE_ENTITY_SENSE RTAS_SENSOR_TYPE_ALLOCATION_STATE + /* Possible values for the platform-processor-diagnostics-run-mode parameter * of the RTAS ibm,get-system-parameter call. */ @@ -453,6 +471,13 @@ static inline void rtas_st(target_ulong phys, int n, uint32_t val) stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n), val); } +static inline void rtas_st_buffer_direct(target_ulong phys, + target_ulong phys_len, + uint8_t *buffer, uint16_t buffer_len) +{ + cpu_physical_memory_write(ppc64_phys_to_real(phys), buffer, + MIN(buffer_len, phys_len)); +} static inline void rtas_st_buffer(target_ulong phys, target_ulong phys_len, uint8_t *buffer, uint16_t buffer_len) @@ -462,8 +487,7 @@ static inline void rtas_st_buffer(target_ulong phys, target_ulong phys_len, } stw_be_phys(&address_space_memory, ppc64_phys_to_real(phys), buffer_len); - cpu_physical_memory_write(ppc64_phys_to_real(phys + 2), - buffer, MIN(buffer_len, phys_len - 2)); + rtas_st_buffer_direct(phys + 2, phys_len - 2, buffer, buffer_len); } typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -482,10 +506,16 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, #define SPAPR_TCE_PAGE_MASK (SPAPR_TCE_PAGE_SIZE - 1) #define SPAPR_VIO_BASE_LIOBN 0x00000000 -#define SPAPR_PCI_BASE_LIOBN 0x80000000 +#define SPAPR_VIO_LIOBN(reg) (0x00000000 | (reg)) +#define SPAPR_PCI_LIOBN(phb_index, window_num) \ + (0x80000000 | ((phb_index) << 8) | (window_num)) +#define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000)) +#define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff) #define RTAS_ERROR_LOG_MAX 2048 +#define RTAS_EVENT_SCAN_RATE 1 + typedef struct sPAPRTCETable sPAPRTCETable; #define TYPE_SPAPR_TCE_TABLE "spapr-tce-table" @@ -507,6 +537,15 @@ struct sPAPRTCETable { QLIST_ENTRY(sPAPRTCETable) list; }; +sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn); + +struct sPAPREventLogEntry { + int log_type; + bool exception; + void *data; + QTAILQ_ENTRY(sPAPREventLogEntry) next; +}; + void spapr_events_init(sPAPREnvironment *spapr); void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq); int spapr_h_cas_compose_response(target_ulong addr, target_ulong size); @@ -521,6 +560,18 @@ int spapr_dma_dt(void *fdt, int node_off, const char *propname, int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, sPAPRTCETable *tcet); void spapr_pci_switch_vga(bool big_endian); +void spapr_hotplug_req_add_event(sPAPRDRConnector *drc); +void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc); + +/* rtas-configure-connector state */ +struct sPAPRConfigureConnectorState { + uint32_t drc_index; + int fdt_offset; + int fdt_depth; + QTAILQ_ENTRY(sPAPRConfigureConnectorState) next; +}; + +void spapr_ccs_reset_hook(void *opaque); #define TYPE_SPAPR_RTC "spapr-rtc" diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h new file mode 100644 index 0000000000..60cda35ed2 --- /dev/null +++ b/include/hw/ppc/spapr_drc.h @@ -0,0 +1,201 @@ +/* + * QEMU SPAPR Dynamic Reconfiguration Connector Implementation + * + * Copyright IBM Corp. 2014 + * + * Authors: + * Michael Roth <mdroth@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#if !defined(__HW_SPAPR_DRC_H__) +#define __HW_SPAPR_DRC_H__ + +#include "qom/object.h" +#include "hw/qdev.h" +#include "libfdt.h" + +#define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector" +#define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \ + OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DR_CONNECTOR) +#define SPAPR_DR_CONNECTOR_CLASS(klass) \ + OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, \ + TYPE_SPAPR_DR_CONNECTOR) +#define SPAPR_DR_CONNECTOR(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \ + TYPE_SPAPR_DR_CONNECTOR) + +/* + * Various hotplug types managed by sPAPRDRConnector + * + * these are somewhat arbitrary, but to make things easier + * when generating DRC indexes later we've aligned the bit + * positions with the values used to assign DRC indexes on + * pSeries. we use those values as bit shifts to allow for + * the OR'ing of these values in various QEMU routines, but + * for values exposed to the guest (via DRC indexes for + * instance) we will use the shift amounts. + */ +typedef enum { + SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU = 1, + SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB = 2, + SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO = 3, + SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI = 4, + SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB = 8, +} sPAPRDRConnectorTypeShift; + +typedef enum { + SPAPR_DR_CONNECTOR_TYPE_ANY = ~0, + SPAPR_DR_CONNECTOR_TYPE_CPU = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU, + SPAPR_DR_CONNECTOR_TYPE_PHB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB, + SPAPR_DR_CONNECTOR_TYPE_VIO = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO, + SPAPR_DR_CONNECTOR_TYPE_PCI = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI, + SPAPR_DR_CONNECTOR_TYPE_LMB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB, +} sPAPRDRConnectorType; + +/* + * set via set-indicator RTAS calls + * as documented by PAPR+ 2.7 13.5.3.4, Table 177 + * + * isolated: put device under firmware control + * unisolated: claim OS control of device (may or may not be in use) + */ +typedef enum { + SPAPR_DR_ISOLATION_STATE_ISOLATED = 0, + SPAPR_DR_ISOLATION_STATE_UNISOLATED = 1 +} sPAPRDRIsolationState; + +/* + * set via set-indicator RTAS calls + * as documented by PAPR+ 2.7 13.5.3.4, Table 177 + * + * unusable: mark device as unavailable to OS + * usable: mark device as available to OS + * exchange: (currently unused) + * recover: (currently unused) + */ +typedef enum { + SPAPR_DR_ALLOCATION_STATE_UNUSABLE = 0, + SPAPR_DR_ALLOCATION_STATE_USABLE = 1, + SPAPR_DR_ALLOCATION_STATE_EXCHANGE = 2, + SPAPR_DR_ALLOCATION_STATE_RECOVER = 3 +} sPAPRDRAllocationState; + +/* + * LED/visual indicator state + * + * set via set-indicator RTAS calls + * as documented by PAPR+ 2.7 13.5.3.4, Table 177, + * and PAPR+ 2.7 13.5.4.1, Table 180 + * + * inactive: hotpluggable entity inactive and safely removable + * active: hotpluggable entity in use and not safely removable + * identify: (currently unused) + * action: (currently unused) + */ +typedef enum { + SPAPR_DR_INDICATOR_STATE_INACTIVE = 0, + SPAPR_DR_INDICATOR_STATE_ACTIVE = 1, + SPAPR_DR_INDICATOR_STATE_IDENTIFY = 2, + SPAPR_DR_INDICATOR_STATE_ACTION = 3, +} sPAPRDRIndicatorState; + +/* + * returned via get-sensor-state RTAS calls + * as documented by PAPR+ 2.7 13.5.3.3, Table 175: + * + * empty: connector slot empty (e.g. empty hotpluggable PCI slot) + * present: connector slot populated and device available to OS + * unusable: device not currently available to OS + * exchange: (currently unused) + * recover: (currently unused) + */ +typedef enum { + SPAPR_DR_ENTITY_SENSE_EMPTY = 0, + SPAPR_DR_ENTITY_SENSE_PRESENT = 1, + SPAPR_DR_ENTITY_SENSE_UNUSABLE = 2, + SPAPR_DR_ENTITY_SENSE_EXCHANGE = 3, + SPAPR_DR_ENTITY_SENSE_RECOVER = 4, +} sPAPRDREntitySense; + +typedef enum { + SPAPR_DR_CC_RESPONSE_NEXT_SIB = 1, /* currently unused */ + SPAPR_DR_CC_RESPONSE_NEXT_CHILD = 2, + SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY = 3, + SPAPR_DR_CC_RESPONSE_PREV_PARENT = 4, + SPAPR_DR_CC_RESPONSE_SUCCESS = 0, + SPAPR_DR_CC_RESPONSE_ERROR = -1, + SPAPR_DR_CC_RESPONSE_CONTINUE = -2, +} sPAPRDRCCResponse; + +typedef void (spapr_drc_detach_cb)(DeviceState *d, void *opaque); + +typedef struct sPAPRDRConnector { + /*< private >*/ + DeviceState parent; + + sPAPRDRConnectorType type; + uint32_t id; + Object *owner; + const char *name; + + /* sensor/indicator states */ + uint32_t isolation_state; + uint32_t allocation_state; + uint32_t indicator_state; + + /* configure-connector state */ + void *fdt; + int fdt_start_offset; + bool configured; + + bool awaiting_release; + + /* device pointer, via link property */ + DeviceState *dev; + spapr_drc_detach_cb *detach_cb; + void *detach_cb_opaque; +} sPAPRDRConnector; + +typedef struct sPAPRDRConnectorClass { + /*< private >*/ + DeviceClass parent; + + /*< public >*/ + + /* accessors for guest-visible (generally via RTAS) DR state */ + int (*set_isolation_state)(sPAPRDRConnector *drc, + sPAPRDRIsolationState state); + int (*set_indicator_state)(sPAPRDRConnector *drc, + sPAPRDRIndicatorState state); + int (*set_allocation_state)(sPAPRDRConnector *drc, + sPAPRDRAllocationState state); + uint32_t (*get_index)(sPAPRDRConnector *drc); + uint32_t (*get_type)(sPAPRDRConnector *drc); + const char *(*get_name)(sPAPRDRConnector *drc); + + sPAPRDREntitySense (*entity_sense)(sPAPRDRConnector *drc); + + /* QEMU interfaces for managing FDT/configure-connector */ + const void *(*get_fdt)(sPAPRDRConnector *drc, int *fdt_start_offset); + void (*set_configured)(sPAPRDRConnector *drc); + + /* QEMU interfaces for managing hotplug operations */ + void (*attach)(sPAPRDRConnector *drc, DeviceState *d, void *fdt, + int fdt_start_offset, bool coldplug, Error **errp); + void (*detach)(sPAPRDRConnector *drc, DeviceState *d, + spapr_drc_detach_cb *detach_cb, + void *detach_cb_opaque, Error **errp); + bool (*release_pending)(sPAPRDRConnector *drc); +} sPAPRDRConnectorClass; + +sPAPRDRConnector *spapr_dr_connector_new(Object *owner, + sPAPRDRConnectorType type, + uint32_t id); +sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index); +sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type, + uint32_t id); +int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, + uint32_t drc_type_mask); + +#endif /* __HW_SPAPR_DRC_H__ */ diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 8f04888e72..816a2e8db2 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -28,6 +28,12 @@ typedef unsigned long vhost_log_chunk_t; #define VHOST_LOG_CHUNK (VHOST_LOG_PAGE * VHOST_LOG_BITS) #define VHOST_INVALID_FEATURE_BIT (0xff) +struct vhost_log { + unsigned long long size; + int refcnt; + vhost_log_chunk_t log[0]; +}; + struct vhost_memory; struct vhost_dev { MemoryListener memory_listener; @@ -43,7 +49,6 @@ struct vhost_dev { unsigned long long backend_features; bool started; bool log_enabled; - vhost_log_chunk_t *log; unsigned long long log_size; Error *migration_blocker; bool force; @@ -52,6 +57,7 @@ struct vhost_dev { hwaddr mem_changed_end_addr; const VhostOps *vhost_ops; void *opaque; + struct vhost_log *log; }; int vhost_dev_init(struct vhost_dev *hdev, void *opaque, diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 7222a904dc..2bb7c1ad1a 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -98,7 +98,7 @@ typedef struct VirtioDeviceClass { DeviceUnrealize unrealize; uint64_t (*get_features)(VirtIODevice *vdev, uint64_t requested_features); uint64_t (*bad_features)(VirtIODevice *vdev); - void (*set_features)(VirtIODevice *vdev, uint32_t val); + void (*set_features)(VirtIODevice *vdev, uint64_t val); void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); @@ -184,7 +184,7 @@ void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); void virtio_set_status(VirtIODevice *vdev, uint8_t val); void virtio_reset(void *opaque); void virtio_update_irq(VirtIODevice *vdev); -int virtio_set_features(VirtIODevice *vdev, uint32_t val); +int virtio_set_features(VirtIODevice *vdev, uint64_t val); /* Base devices. */ typedef struct VirtIOBlkConf VirtIOBlkConf; @@ -230,19 +230,19 @@ VirtQueue *virtio_vector_next_queue(VirtQueue *vq); static inline void virtio_add_feature(uint64_t *features, unsigned int fbit) { assert(fbit < 64); - *features |= (1 << fbit); + *features |= (1ULL << fbit); } static inline void virtio_clear_feature(uint64_t *features, unsigned int fbit) { assert(fbit < 64); - *features &= ~(1 << fbit); + *features &= ~(1ULL << fbit); } static inline bool __virtio_has_feature(uint64_t features, unsigned int fbit) { assert(fbit < 64); - return !!(features & (1 << fbit)); + return !!(features & (1ULL << fbit)); } static inline bool virtio_has_feature(VirtIODevice *vdev, unsigned int fbit) 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/qemu-common.h b/include/qemu-common.h index 6b373ff7e3..d52d09cfb8 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -186,6 +186,12 @@ int64_t strtosz(const char *nptr, char **end); int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix); int64_t strtosz_suffix_unit(const char *nptr, char **end, const char default_suffix, int64_t unit); +#define K_BYTE (1ULL << 10) +#define M_BYTE (1ULL << 20) +#define G_BYTE (1ULL << 30) +#define T_BYTE (1ULL << 40) +#define P_BYTE (1ULL << 50) +#define E_BYTE (1ULL << 60) /* used to print char* safely */ #define STR_OR_NULL(str) ((str) ? (str) : "null") diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 98e05ca875..bd2c075343 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -99,7 +99,13 @@ #ifndef smp_wmb #ifdef __ATOMIC_RELEASE -#define smp_wmb() __atomic_thread_fence(__ATOMIC_RELEASE) +/* __atomic_thread_fence does not include a compiler barrier; instead, + * the barrier is part of __atomic_load/__atomic_store's "volatile-like" + * semantics. If smp_wmb() is a no-op, absence of the barrier means that + * the compiler is free to reorder stores on each side of the barrier. + * Add one here, and similarly in smp_rmb() and smp_read_barrier_depends(). + */ +#define smp_wmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); barrier(); }) #else #define smp_wmb() __sync_synchronize() #endif @@ -107,7 +113,7 @@ #ifndef smp_rmb #ifdef __ATOMIC_ACQUIRE -#define smp_rmb() __atomic_thread_fence(__ATOMIC_ACQUIRE) +#define smp_rmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); barrier(); }) #else #define smp_rmb() __sync_synchronize() #endif @@ -115,7 +121,7 @@ #ifndef smp_read_barrier_depends #ifdef __ATOMIC_CONSUME -#define smp_read_barrier_depends() __atomic_thread_fence(__ATOMIC_CONSUME) +#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); barrier(); }) #else #define smp_read_barrier_depends() barrier() #endif diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index f0273c965f..86dd9cd5fc 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -39,7 +39,9 @@ * bitmap_empty(src, nbits) Are all bits zero in *src? * bitmap_full(src, nbits) Are all bits set in *src? * bitmap_set(dst, pos, nbits) Set specified bit area + * bitmap_set_atomic(dst, pos, nbits) Set specified bit area with atomic ops * bitmap_clear(dst, pos, nbits) Clear specified bit area + * bitmap_test_and_clear_atomic(dst, pos, nbits) Test and clear area * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area */ @@ -226,7 +228,9 @@ static inline int bitmap_intersects(const unsigned long *src1, } void bitmap_set(unsigned long *map, long i, long len); +void bitmap_set_atomic(unsigned long *map, long i, long len); void bitmap_clear(unsigned long *map, long start, long nr); +bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr); unsigned long bitmap_find_next_zero_area(unsigned long *map, unsigned long size, unsigned long start, diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 8abdcf9077..8164225152 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -16,6 +16,7 @@ #include <assert.h> #include "host-utils.h" +#include "atomic.h" #define BITS_PER_BYTE CHAR_BIT #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) @@ -39,6 +40,19 @@ static inline void set_bit(long nr, unsigned long *addr) } /** + * set_bit_atomic - Set a bit in memory atomically + * @nr: the bit to set + * @addr: the address to start counting from + */ +static inline void set_bit_atomic(long nr, unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = addr + BIT_WORD(nr); + + atomic_or(p, mask); +} + +/** * clear_bit - Clears a bit in memory * @nr: Bit to clear * @addr: Address to start counting from diff --git a/include/qemu/option.h b/include/qemu/option.h index f88b545dfc..ac0e43b7e5 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -100,9 +100,11 @@ void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, Error **errp); void qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, Error **errp); -typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque); +typedef int (*qemu_opt_loopfunc)(void *opaque, + const char *name, const char *value, + Error **errp); int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, - int abort_on_failure); + Error **errp); QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id); QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, @@ -125,10 +127,10 @@ QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp); -typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque); +typedef int (*qemu_opts_loopfunc)(void *opaque, QemuOpts *opts, Error **errp); +int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, + void *opaque, Error **errp); void qemu_opts_print(QemuOpts *opts, const char *sep); -int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, - int abort_on_failure); void qemu_opts_print_help(QemuOptsList *list); void qemu_opts_free(QemuOptsList *list); QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list); diff --git a/include/qom/object.h b/include/qom/object.h index d2d7748f62..0505f20e71 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1290,6 +1290,24 @@ void object_property_add_alias(Object *obj, const char *name, Error **errp); /** + * object_property_add_const_link: + * @obj: the object to add a property to + * @name: the name of the property + * @target: the object to be referred by the link + * @errp: if an error occurs, a pointer to an area to store the error + * + * Add an unmodifiable link for a property on an object. This function will + * add a property of type link<TYPE> where TYPE is the type of @target. + * + * The caller must ensure that @target stays alive as long as + * this property exists. In the case @target is a child of @obj, + * this will be the case. Otherwise, the caller is responsible for + * taking a reference. + */ +void object_property_add_const_link(Object *obj, const char *name, + Object *target, Error **errp); + +/** * object_property_set_description: * @obj: the object owning the property * @name: the name of the property diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h index cc647d61fc..6fe276fafb 100644 --- a/include/standard-headers/linux/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -155,7 +155,7 @@ static inline unsigned vring_size(unsigned int num, unsigned long align) } /* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */ -/* Assuming a given event_idx value from the other size, if +/* Assuming a given event_idx value from the other side, if * we have just incremented index from old to new_idx, * should we trigger an event? */ static inline int vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old) 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/kvm.h b/include/sysemu/kvm.h index 4878959404..f459fbdbd4 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -287,6 +287,8 @@ void kvm_arch_init_irq_routing(KVMState *s); int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data); +int kvm_arch_msi_data_to_gsi(uint32_t data); + int kvm_set_irq(KVMState *s, int irq, int level); int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 8a52934728..853d90a317 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -137,6 +137,7 @@ extern const char *mem_path; extern int mem_prealloc; #define MAX_NODES 128 +#define NUMA_NODE_UNASSIGNED MAX_NODES /* The following shall be true for all CPUs: * cpu->cpu_index < max_cpus <= MAX_CPUMASK_BITS @@ -161,9 +162,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/include/sysemu/tpm.h b/include/sysemu/tpm.h index c1438907de..c8afa179e5 100644 --- a/include/sysemu/tpm.h +++ b/include/sysemu/tpm.h @@ -32,11 +32,13 @@ TPMVersion tpm_tis_get_tpm_version(Object *obj); static inline TPMVersion tpm_get_version(void) { +#ifdef CONFIG_TPM Object *obj = object_resolve_path_type("", TYPE_TPM_TIS, NULL); if (obj) { return tpm_tis_get_tpm_version(obj); } +#endif return TPM_VERSION_UNSPEC; } diff --git a/include/ui/console.h b/include/ui/console.h index 6f7550ef9c..de92523bbb 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -241,10 +241,6 @@ void dpy_text_resize(QemuConsole *con, int w, int h); void dpy_mouse_set(QemuConsole *con, int x, int y, int on); void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor); bool dpy_cursor_define_supported(QemuConsole *con); -void dpy_gfx_update_dirty(QemuConsole *con, - MemoryRegion *address_space, - uint64_t base, - bool invalidate); bool dpy_gfx_check_format(QemuConsole *con, pixman_format_code_t format); @@ -374,7 +370,7 @@ char *vnc_display_local_addr(const char *id); int vnc_display_password(const char *id, const char *password); int vnc_display_pw_expire(const char *id, time_t expires); QemuOpts *vnc_parse_func(const char *str); -int vnc_init_func(QemuOpts *opts, void *opaque); +int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp); #else static inline int vnc_display_password(const char *id, const char *password) { @@ -83,7 +83,6 @@ struct KVMState struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; bool coalesced_flush_in_progress; int broken_set_mem_region; - int migration_log; int vcpu_events; int robust_singlestep; int debugregs; @@ -234,9 +233,6 @@ static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) mem.guest_phys_addr = slot->start_addr; mem.userspace_addr = (unsigned long)slot->ram; mem.flags = slot->flags; - if (s->migration_log) { - mem.flags |= KVM_MEM_LOG_DIRTY_PAGES; - } if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { /* Set the slot size to 0 before setting the slot to the desired @@ -317,10 +313,6 @@ static int kvm_slot_dirty_pages_log_change(KVMSlot *mem, bool log_dirty) mem->flags = flags; /* If nothing changed effectively, no need to issue ioctl */ - if (s->migration_log) { - flags |= KVM_MEM_LOG_DIRTY_PAGES; - } - if (flags == old_flags) { return 0; } @@ -335,19 +327,22 @@ static int kvm_dirty_pages_log_change(hwaddr phys_addr, KVMSlot *mem = kvm_lookup_matching_slot(s, phys_addr, phys_addr + size); if (mem == NULL) { - fprintf(stderr, "BUG: %s: invalid parameters " TARGET_FMT_plx "-" - TARGET_FMT_plx "\n", __func__, phys_addr, - (hwaddr)(phys_addr + size - 1)); - return -EINVAL; + return 0; + } else { + return kvm_slot_dirty_pages_log_change(mem, log_dirty); } - return kvm_slot_dirty_pages_log_change(mem, log_dirty); } static void kvm_log_start(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { int r; + if (old != 0) { + return; + } + r = kvm_dirty_pages_log_change(section->offset_within_address_space, int128_get64(section->size), true); if (r < 0) { @@ -356,10 +351,15 @@ static void kvm_log_start(MemoryListener *listener, } static void kvm_log_stop(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { int r; + if (new != 0) { + return; + } + r = kvm_dirty_pages_log_change(section->offset_within_address_space, int128_get64(section->size), false); if (r < 0) { @@ -367,31 +367,6 @@ static void kvm_log_stop(MemoryListener *listener, } } -static int kvm_set_migration_log(bool enable) -{ - KVMState *s = kvm_state; - KVMSlot *mem; - int i, err; - - s->migration_log = enable; - - for (i = 0; i < s->nr_slots; i++) { - mem = &s->slots[i]; - - if (!mem->memory_size) { - continue; - } - if (!!(mem->flags & KVM_MEM_LOG_DIRTY_PAGES) == enable) { - continue; - } - err = kvm_set_user_memory_region(s, mem); - if (err) { - return err; - } - } - return 0; -} - /* get kvm's dirty pages bitmap and update qemu's */ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, unsigned long *bitmap) @@ -663,7 +638,7 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add) KVMSlot *mem, old; int err; MemoryRegion *mr = section->mr; - bool log_dirty = memory_region_is_logging(mr); + bool log_dirty = memory_region_get_dirty_log_mask(mr) != 0; bool writeable = !mr->readonly && !mr->rom_device; bool readonly_flag = mr->readonly || memory_region_is_romd(mr); hwaddr start_addr = section->offset_within_address_space; @@ -715,7 +690,7 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add) old = *mem; - if ((mem->flags & KVM_MEM_LOG_DIRTY_PAGES) || s->migration_log) { + if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { kvm_physical_sync_dirty_bitmap(section); } @@ -844,22 +819,6 @@ static void kvm_log_sync(MemoryListener *listener, } } -static void kvm_log_global_start(struct MemoryListener *listener) -{ - int r; - - r = kvm_set_migration_log(1); - assert(r >= 0); -} - -static void kvm_log_global_stop(struct MemoryListener *listener) -{ - int r; - - r = kvm_set_migration_log(0); - assert(r >= 0); -} - static void kvm_mem_ioeventfd_add(MemoryListener *listener, MemoryRegionSection *section, bool match_data, uint64_t data, @@ -935,8 +894,6 @@ static MemoryListener kvm_memory_listener = { .log_start = kvm_log_start, .log_stop = kvm_log_stop, .log_sync = kvm_log_sync, - .log_global_start = kvm_log_global_start, - .log_global_stop = kvm_log_global_stop, .eventfd_add = kvm_mem_ioeventfd_add, .eventfd_del = kvm_mem_ioeventfd_del, .coalesced_mmio_add = kvm_coalesce_mmio_region, @@ -1228,7 +1185,7 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) int virq; if (kvm_gsi_direct_mapping()) { - return msg.data & 0xffff; + return kvm_arch_msi_data_to_gsi(msg.data); } if (!kvm_gsi_routing_enabled()) { @@ -1828,6 +1785,14 @@ int kvm_cpu_exec(CPUState *cpu) } fprintf(stderr, "error: kvm run failed %s\n", strerror(-run_ret)); +#ifdef TARGET_PPC + if (run_ret == -EBUSY) { + fprintf(stderr, + "This is probably because your SMT is enabled.\n" + "VCPU can only run on primary threads with all " + "secondary threads offline.\n"); + } +#endif ret = -1; break; } diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index d7dcef58ae..a4ae82eb82 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -106,6 +106,8 @@ struct kvm_ioapic_state { #define KVM_IRQCHIP_IOAPIC 2 #define KVM_NR_IRQCHIPS 3 +#define KVM_RUN_X86_SMM (1 << 0) + /* for KVM_GET_REGS and KVM_SET_REGS */ struct kvm_regs { /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */ @@ -281,6 +283,7 @@ struct kvm_reinject_control { #define KVM_VCPUEVENT_VALID_NMI_PENDING 0x00000001 #define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002 #define KVM_VCPUEVENT_VALID_SHADOW 0x00000004 +#define KVM_VCPUEVENT_VALID_SMM 0x00000008 /* Interrupt shadow states */ #define KVM_X86_SHADOW_INT_MOV_SS 0x01 @@ -309,7 +312,13 @@ struct kvm_vcpu_events { } nmi; __u32 sipi_vector; __u32 flags; - __u32 reserved[10]; + struct { + __u8 smm; + __u8 pending; + __u8 smm_inside_nmi; + __u8 latched_init; + } smi; + __u32 reserved[9]; }; /* for KVM_GET/SET_DEBUGREGS */ @@ -345,4 +354,7 @@ struct kvm_xcrs { struct kvm_sync_regs { }; +#define KVM_QUIRK_LINT0_REENABLED (1 << 0) +#define KVM_QUIRK_CD_NW_CLEARED (1 << 1) + #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index b96d9787dd..fad9e5c561 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -202,7 +202,7 @@ struct kvm_run { __u32 exit_reason; __u8 ready_for_interrupt_injection; __u8 if_flag; - __u8 padding2[2]; + __u16 flags; /* in (pre_kvm_run), out (post_kvm_run) */ __u64 cr8; @@ -814,6 +814,9 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_S390_INJECT_IRQ 113 #define KVM_CAP_S390_IRQ_STATE 114 #define KVM_CAP_PPC_HWRNG 115 +#define KVM_CAP_DISABLE_QUIRKS 116 +#define KVM_CAP_X86_SMM 117 +#define KVM_CAP_MULTI_ADDRESS_SPACE 118 #ifdef KVM_CAP_IRQ_ROUTING @@ -1199,6 +1202,8 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_S390_IRQ_STATE */ #define KVM_S390_SET_IRQ_STATE _IOW(KVMIO, 0xb5, struct kvm_s390_irq_state) #define KVM_S390_GET_IRQ_STATE _IOW(KVMIO, 0xb6, struct kvm_s390_irq_state) +/* Available with KVM_CAP_X86_SMM */ +#define KVM_SMI _IO(KVMIO, 0xb7) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) diff --git a/linux-user/main.c b/linux-user/main.c index 3f32db0afd..6989b82455 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -215,10 +215,6 @@ void cpu_list_unlock(void) /***********************************************************/ /* CPUX86 core interface */ -void cpu_smm_update(CPUX86State *env) -{ -} - uint64_t cpu_get_tsc(CPUX86State *env) { return cpu_get_real_ticks(); diff --git a/linux-user/mmap.c b/linux-user/mmap.c index a249f0ceb6..78e1b2df43 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -30,6 +30,7 @@ #include "qemu.h" #include "qemu-common.h" +#include "translate-all.h" //#define DEBUG_MMAP @@ -574,7 +575,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, page_dump(stdout); printf("\n"); #endif - tb_invalidate_phys_range(start, start + len, 0); + tb_invalidate_phys_range(start, start + len); mmap_unlock(); return start; fail: @@ -679,7 +680,7 @@ int target_munmap(abi_ulong start, abi_ulong len) if (ret == 0) { page_set_flags(start, start + len, 0); - tb_invalidate_phys_range(start, start + len, 0); + tb_invalidate_phys_range(start, start + len); } mmap_unlock(); return ret; @@ -758,7 +759,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, page_set_flags(old_addr, old_addr + old_size, 0); page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID); } - tb_invalidate_phys_range(new_addr, new_addr + new_size, 0); + tb_invalidate_phys_range(new_addr, new_addr + new_size); mmap_unlock(); return new_addr; } @@ -28,6 +28,8 @@ //#define DEBUG_UNASSIGNED +#define RAM_ADDR_INVALID (~(ram_addr_t)0) + static unsigned memory_region_transaction_depth; static bool memory_region_update_pending; static bool ioeventfd_update_pending; @@ -152,7 +154,7 @@ static bool memory_listener_match(MemoryListener *listener, } while (0) /* No need to ref/unref .mr, the FlatRange keeps it alive. */ -#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback) \ +#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \ MEMORY_LISTENER_CALL(callback, dir, (&(MemoryRegionSection) { \ .mr = (fr)->mr, \ .address_space = (as), \ @@ -160,7 +162,7 @@ static bool memory_listener_match(MemoryListener *listener, .size = (fr)->addr.size, \ .offset_within_address_space = int128_get64((fr)->addr.start), \ .readonly = (fr)->readonly, \ - })) + }), ##_args) struct CoalescedMemoryRange { AddrRange addr; @@ -588,7 +590,7 @@ static void render_memory_region(FlatView *view, remain = clip.size; fr.mr = mr; - fr.dirty_log_mask = mr->dirty_log_mask; + fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr); fr.romd_mode = mr->romd_mode; fr.readonly = readonly; @@ -774,10 +776,15 @@ static void address_space_update_topology_pass(AddressSpace *as, if (adding) { MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_nop); - if (frold->dirty_log_mask && !frnew->dirty_log_mask) { - MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop); - } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) { - MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start); + if (frnew->dirty_log_mask & ~frold->dirty_log_mask) { + MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start, + frold->dirty_log_mask, + frnew->dirty_log_mask); + } + if (frold->dirty_log_mask & ~frnew->dirty_log_mask) { + MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop, + frold->dirty_log_mask, + frnew->dirty_log_mask); } } @@ -1002,6 +1009,7 @@ static void memory_region_initfn(Object *obj) ObjectProperty *op; mr->ops = &unassigned_mem_ops; + mr->ram_addr = RAM_ADDR_INVALID; mr->enabled = true; mr->romd_mode = true; mr->destructor = memory_region_destructor_none; @@ -1193,7 +1201,6 @@ void memory_region_init_io(MemoryRegion *mr, mr->ops = ops; mr->opaque = opaque; mr->terminates = true; - mr->ram_addr = ~(ram_addr_t)0; } void memory_region_init_ram(MemoryRegion *mr, @@ -1207,6 +1214,7 @@ void memory_region_init_ram(MemoryRegion *mr, mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_addr = qemu_ram_alloc(size, mr, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } void memory_region_init_resizeable_ram(MemoryRegion *mr, @@ -1224,6 +1232,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_addr = qemu_ram_alloc_resizeable(size, max_size, resized, mr, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } #ifdef __linux__ @@ -1240,6 +1249,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_addr = qemu_ram_alloc_from_file(size, mr, share, path, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } #endif @@ -1253,6 +1263,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram_from_ptr; + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */ assert(ptr != NULL); @@ -1389,9 +1400,18 @@ bool memory_region_is_skip_dump(MemoryRegion *mr) return mr->skip_dump; } -bool memory_region_is_logging(MemoryRegion *mr) +uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) +{ + uint8_t mask = mr->dirty_log_mask; + if (global_dirty_log) { + mask |= (1 << DIRTY_MEMORY_MIGRATION); + } + return mask; +} + +bool memory_region_is_logging(MemoryRegion *mr, uint8_t client) { - return mr->dirty_log_mask; + return memory_region_get_dirty_log_mask(mr) & (1 << client); } bool memory_region_is_rom(MemoryRegion *mr) @@ -1425,6 +1445,7 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { uint8_t mask = 1 << client; + assert(client == DIRTY_MEMORY_VGA); memory_region_transaction_begin(); mr->dirty_log_mask = (mr->dirty_log_mask & ~mask) | (log * mask); memory_region_update_pending |= mr->enabled; @@ -1434,27 +1455,24 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { - assert(mr->terminates); + assert(mr->ram_addr != RAM_ADDR_INVALID); return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client); } void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size) { - assert(mr->terminates); - cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size); + assert(mr->ram_addr != RAM_ADDR_INVALID); + cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, + memory_region_get_dirty_log_mask(mr)); } bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { - bool ret; - assert(mr->terminates); - ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client); - if (ret) { - cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client); - } - return ret; + assert(mr->ram_addr != RAM_ADDR_INVALID); + return cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr, + size, client); } @@ -1497,8 +1515,9 @@ void memory_region_rom_device_set_romd(MemoryRegion *mr, bool romd_mode) void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { - assert(mr->terminates); - cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client); + assert(mr->ram_addr != RAM_ADDR_INVALID); + cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr, size, + client); } int memory_region_get_fd(MemoryRegion *mr) @@ -1507,7 +1526,7 @@ int memory_region_get_fd(MemoryRegion *mr) return memory_region_get_fd(mr->alias); } - assert(mr->terminates); + assert(mr->ram_addr != RAM_ADDR_INVALID); return qemu_get_ram_fd(mr->ram_addr & TARGET_PAGE_MASK); } @@ -1518,14 +1537,14 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr) return memory_region_get_ram_ptr(mr->alias) + mr->alias_offset; } - assert(mr->terminates); + assert(mr->ram_addr != RAM_ADDR_INVALID); return qemu_get_ram_ptr(mr->ram_addr & TARGET_PAGE_MASK); } void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp) { - assert(mr->terminates); + assert(mr->ram_addr != RAM_ADDR_INVALID); qemu_ram_resize(mr->ram_addr, newsize, errp); } @@ -1947,12 +1966,24 @@ void address_space_sync_dirty_bitmap(AddressSpace *as) void memory_global_dirty_log_start(void) { global_dirty_log = true; + MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward); + + /* Refresh DIRTY_LOG_MIGRATION bit. */ + memory_region_transaction_begin(); + memory_region_update_pending = true; + memory_region_transaction_commit(); } void memory_global_dirty_log_stop(void) { global_dirty_log = false; + + /* Refresh DIRTY_LOG_MIGRATION bit. */ + memory_region_transaction_begin(); + memory_region_update_pending = true; + memory_region_transaction_commit(); + MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse); } @@ -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,70 @@ 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 */ + /* Any string is acceptable as "id", so nothing to check */ } 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 +5019,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 +5070,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 +5082,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 +5110,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 +5257,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); @@ -1329,7 +1329,7 @@ void net_check_clients(void) } } -static int net_init_client(QemuOpts *opts, void *dummy) +static int net_init_client(void *dummy, QemuOpts *opts, Error **errp) { Error *local_err = NULL; @@ -1342,7 +1342,7 @@ static int net_init_client(QemuOpts *opts, void *dummy) return 0; } -static int net_init_netdev(QemuOpts *opts, void *dummy) +static int net_init_netdev(void *dummy, QemuOpts *opts, Error **errp) { Error *local_err = NULL; int ret; @@ -1373,10 +1373,12 @@ int net_init_clients(void) QTAILQ_INIT(&net_clients); - if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1) + if (qemu_opts_foreach(qemu_find_opts("netdev"), + net_init_netdev, NULL, NULL)) { return -1; + } - if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) { + if (qemu_opts_foreach(net, net_init_client, NULL, NULL)) { return -1; } diff --git a/net/slirp.c b/net/slirp.c index 0e15cf6750..35338376f7 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -481,7 +481,6 @@ static void slirp_smb_cleanup(SlirpState *s) static int slirp_smb(SlirpState* s, const char *exported_dir, struct in_addr vserver_addr) { - static int instance; char smb_conf[128]; char smb_cmdline[128]; struct passwd *passwd; @@ -505,10 +504,10 @@ static int slirp_smb(SlirpState* s, const char *exported_dir, return -1; } - snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d", - (long)getpid(), instance++); - if (mkdir(s->smb_dir, 0700) < 0) { + snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.XXXXXX"); + if (!mkdtemp(s->smb_dir)) { error_report("could not create samba server dir '%s'", s->smb_dir); + s->smb_dir[0] = 0; return -1; } snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf"); diff --git a/net/vhost-user.c b/net/vhost-user.c index 8d2672846f..3930741fb6 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -157,8 +157,9 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, return 0; } -static int net_vhost_chardev_opts(const char *name, const char *value, - void *opaque) +static int net_vhost_chardev_opts(void *opaque, + const char *name, const char *value, + Error **errp) { VhostUserChardevProps *props = opaque; @@ -169,33 +170,34 @@ static int net_vhost_chardev_opts(const char *name, const char *value, } else if (strcmp(name, "server") == 0) { props->is_server = true; } else { - error_report("vhost-user does not support a chardev" - " with the following option:\n %s = %s", - name, value); + error_setg(errp, + "vhost-user does not support a chardev with option %s=%s", + name, value); return -1; } return 0; } -static CharDriverState *net_vhost_parse_chardev(const NetdevVhostUserOptions *opts) +static CharDriverState *net_vhost_parse_chardev( + const NetdevVhostUserOptions *opts, Error **errp) { CharDriverState *chr = qemu_chr_find(opts->chardev); VhostUserChardevProps props; if (chr == NULL) { - error_report("chardev \"%s\" not found", opts->chardev); + error_setg(errp, "chardev \"%s\" not found", opts->chardev); return NULL; } /* inspect chardev opts */ memset(&props, 0, sizeof(props)); - if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, true) != 0) { + if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, errp)) { return NULL; } if (!props.is_socket || !props.is_unix) { - error_report("chardev \"%s\" is not a unix socket", - opts->chardev); + error_setg(errp, "chardev \"%s\" is not a unix socket", + opts->chardev); return NULL; } @@ -204,7 +206,7 @@ static CharDriverState *net_vhost_parse_chardev(const NetdevVhostUserOptions *op return chr; } -static int net_vhost_check_net(QemuOpts *opts, void *opaque) +static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) { const char *name = opaque; const char *driver, *netdev; @@ -219,7 +221,7 @@ static int net_vhost_check_net(QemuOpts *opts, void *opaque) if (strcmp(netdev, name) == 0 && strncmp(driver, virtio_name, strlen(virtio_name)) != 0) { - error_report("vhost-user requires frontend driver virtio-net-*"); + error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); return -1; } @@ -229,7 +231,6 @@ static int net_vhost_check_net(QemuOpts *opts, void *opaque) int net_init_vhost_user(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { - /* FIXME error_setg(errp, ...) on failure */ uint32_t queues; const NetdevVhostUserOptions *vhost_user_opts; CharDriverState *chr; @@ -237,15 +238,14 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER); vhost_user_opts = opts->vhost_user; - chr = net_vhost_parse_chardev(vhost_user_opts); + chr = net_vhost_parse_chardev(vhost_user_opts, errp); if (!chr) { - error_report("No suitable chardev found"); return -1; } /* verify net frontend */ if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, - (char *)name, true) == -1) { + (char *)name, errp)) { return -1; } @@ -125,7 +125,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1); } -static int parse_numa(QemuOpts *opts, void *opaque) +static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) { NumaOptions *object = NULL; Error *err = NULL; @@ -216,8 +216,7 @@ void parse_numa_opts(MachineClass *mc) { int i; - if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, - NULL, 1) != 0) { + if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, NULL, NULL)) { exit(1); } 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/qdev-monitor.c b/qdev-monitor.c index 1d87f573e8..7dd62dd094 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -143,7 +143,8 @@ static void qdev_print_devinfos(bool show_no_user) g_slist_free(list); } -static int set_property(const char *name, const char *value, void *opaque) +static int set_property(void *opaque, const char *name, const char *value, + Error **errp) { Object *obj = opaque; Error *err = NULL; @@ -564,7 +565,7 @@ DeviceState *qdev_device_add(QemuOpts *opts) } /* set properties */ - if (qemu_opt_foreach(opts, set_property, dev, 1) != 0) { + if (qemu_opt_foreach(opts, set_property, dev, NULL)) { object_unparent(OBJECT(dev)); object_unref(OBJECT(dev)); return NULL; @@ -822,15 +823,19 @@ int qemu_global_option(const char *str) QemuOpts *opts; int rc, offset; - rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset); - if (rc < 2 || str[offset] != '=') { - error_report("can't parse: \"%s\"", str); + rc = sscanf(str, "%63[^.=].%63[^=]%n", driver, property, &offset); + if (rc == 2 && str[offset] == '=') { + opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort); + qemu_opt_set(opts, "driver", driver, &error_abort); + qemu_opt_set(opts, "property", property, &error_abort); + qemu_opt_set(opts, "value", str + offset + 1, &error_abort); + return 0; + } + + opts = qemu_opts_parse(&qemu_global_opts, str, false); + if (!opts) { return -1; } - opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort); - qemu_opt_set(opts, "driver", driver, &error_abort); - qemu_opt_set(opts, "property", property, &error_abort); - qemu_opt_set(opts, "value", str + offset + 1, &error_abort); return 0; } diff --git a/qemu-nbd.c b/qemu-nbd.c index 7e690fff7e..5af6d11e33 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -53,6 +53,7 @@ static int persistent = 0; static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state; static int shared = 1; static int nb_fds; +static int server_fd; static void usage(const char *name) { @@ -340,7 +341,7 @@ out: return (void *) EXIT_FAILURE; } -static int nbd_can_accept(void *opaque) +static int nbd_can_accept(void) { return nb_fds < shared; } @@ -351,19 +352,21 @@ static void nbd_export_closed(NBDExport *exp) state = TERMINATED; } +static void nbd_update_server_fd_handler(int fd); + static void nbd_client_closed(NBDClient *client) { nb_fds--; if (nb_fds == 0 && !persistent && state == RUNNING) { state = TERMINATE; } + nbd_update_server_fd_handler(server_fd); qemu_notify_event(); nbd_client_put(client); } static void nbd_accept(void *opaque) { - int server_fd = (uintptr_t) opaque; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); @@ -380,12 +383,22 @@ static void nbd_accept(void *opaque) if (nbd_client_new(exp, fd, nbd_client_closed)) { nb_fds++; + nbd_update_server_fd_handler(server_fd); } else { shutdown(fd, 2); close(fd); } } +static void nbd_update_server_fd_handler(int fd) +{ + if (nbd_can_accept()) { + qemu_set_fd_handler(fd, nbd_accept, NULL, (void *)(uintptr_t)fd); + } else { + qemu_set_fd_handler(fd, NULL, NULL, NULL); + } +} + int main(int argc, char **argv) { BlockBackend *blk; @@ -761,8 +774,8 @@ int main(int argc, char **argv) memset(&client_thread, 0, sizeof(client_thread)); } - qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL, - (void *)(uintptr_t)fd); + server_fd = fd; + nbd_update_server_fd_handler(fd); /* now when the initialization is (almost) complete, chdir("/") * to free any busy filesystems */ diff --git a/qemu-options.hx b/qemu-options.hx index 7edd1f18ce..4be98f71df 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -171,11 +171,13 @@ Set parameter @var{arg} for item @var{id} of type @var{group}\n" ETEXI DEF("global", HAS_ARG, QEMU_OPTION_global, - "-global driver.prop=value\n" + "-global driver.property=value\n" + "-global driver=driver,property=property,value=value\n" " set a global default for a driver property\n", QEMU_ARCH_ALL) STEXI @item -global @var{driver}.@var{prop}=@var{value} +@itemx -global driver=@var{driver},property=@var{property},value=@var{value} @findex -global Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.: @@ -186,6 +188,9 @@ qemu-system-i386 -global ide-drive.physical_block_size=4096 -drive file=file,if= In particular, you can use this to set driver properties for devices which are created automatically by the machine model. To create a device which is not created automatically and set properties on it, use -@option{device}. + +The two syntaxes are equivalent. The longer one works for drivers whose name +contains a dot. ETEXI DEF("boot", HAS_ARG, QEMU_OPTION_boot, @@ -241,8 +246,7 @@ ETEXI DEF("m", HAS_ARG, QEMU_OPTION_m, "-m[emory] [size=]megs[,slots=n,maxmem=size]\n" " configure guest RAM\n" - " size: initial amount of guest memory (default: " - stringify(DEFAULT_RAM_SIZE) "MiB)\n" + " size: initial amount of guest memory\n" " slots: number of hotplug slots (default: none)\n" " maxmem: maximum amount of guest memory (default: none)\n" "NOTE: Some architectures might enforce a specific granularity\n", @@ -3100,9 +3104,10 @@ re-inject them. ETEXI DEF("icount", HAS_ARG, QEMU_OPTION_icount, \ - "-icount [shift=N|auto][,align=on|off]\n" \ + "-icount [shift=N|auto][,align=on|off][,sleep=no]\n" \ " enable virtual instruction counter with 2^N clock ticks per\n" \ - " instruction and enable aligning the host and virtual clocks\n", QEMU_ARCH_ALL) + " instruction, enable aligning the host and virtual clocks\n" \ + " or disable real time cpu sleeping\n", QEMU_ARCH_ALL) STEXI @item -icount [shift=@var{N}|auto] @findex -icount @@ -3111,6 +3116,13 @@ instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified then the virtual cpu speed will be automatically adjusted to keep virtual time within a few seconds of real time. +When the virtual cpu is sleeping, the virtual time will advance at default +speed unless @option{sleep=no} is specified. +With @option{sleep=no}, the virtual time will jump to the next timer deadline +instantly whenever the virtual cpu goes to sleep mode and will not advance +if no timer is enabled. This behavior give deterministic execution times from +the guest point of view. + Note that while this option can give deterministic behavior, it does not provide cycle accurate emulation. Modern CPUs contain superscalar out of order cores with complex cache hierarchies. The number of instructions @@ -3239,7 +3251,9 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ "-incoming fd:fd\n" \ "-incoming exec:cmdline\n" \ " accept incoming migration on given file descriptor\n" \ - " or from given external command\n", + " or from given external command\n" \ + "-incoming defer\n" \ + " wait for the URI to be specified via migrate_incoming\n", QEMU_ARCH_ALL) STEXI @item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6] @@ -3255,6 +3269,11 @@ Accept incoming migration from a given filedescriptor. @item -incoming exec:@var{cmdline} Accept incoming migration as an output from specified external command. + +@item -incoming defer +Wait for the URI to be specified via migrate_incoming. The monitor can +be used to change settings (such as migration parameters) prior to issuing +the migrate_incoming to allow the migration to begin. ETEXI DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \ 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/qom/object.c b/qom/object.c index b8dff43297..96abd347b1 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1266,6 +1266,22 @@ out: g_free(full_type); } +void object_property_add_const_link(Object *obj, const char *name, + Object *target, Error **errp) +{ + char *link_type; + ObjectProperty *op; + + link_type = g_strdup_printf("link<%s>", object_get_typename(target)); + op = object_property_add(obj, name, link_type, + object_get_child_property, NULL, + NULL, target, errp); + if (op != NULL) { + op->resolve = object_resolve_child_property; + } + g_free(link_type); +} + gchar *object_get_canonical_path_component(Object *obj) { ObjectProperty *prop = NULL; 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; } diff --git a/sysconfigs/target/target-x86_64.conf b/sysconfigs/target/target-x86_64.conf deleted file mode 100644 index e69de29bb2..0000000000 --- a/sysconfigs/target/target-x86_64.conf +++ /dev/null diff --git a/target-arm/helper.c b/target-arm/helper.c index 1cc4993ca1..3da0c0579c 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -294,23 +294,15 @@ static gint cpreg_key_compare(gconstpointer a, gconstpointer b) return 0; } -static void cpreg_make_keylist(gpointer key, gpointer value, gpointer udata) -{ - GList **plist = udata; - - *plist = g_list_prepend(*plist, key); -} - void init_cpreg_list(ARMCPU *cpu) { /* Initialise the cpreg_tuples[] array based on the cp_regs hash. * Note that we require cpreg_tuples[] to be sorted by key ID. */ - GList *keys = NULL; + GList *keys; int arraylen; - g_hash_table_foreach(cpu->cp_regs, cpreg_make_keylist, &keys); - + keys = g_hash_table_get_keys(cpu->cp_regs); keys = g_list_sort(keys, cpreg_key_compare); cpu->cpreg_array_len = 0; @@ -492,10 +484,16 @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = { .writefn = dacr_write, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), offsetoflow32(CPUARMState, cp15.dacr_ns) } }, - /* ??? This covers not just the impdef TLB lockdown registers but also - * some v7VMSA registers relating to TEX remap, so it is overly broad. + /* ARMv7 allocates a range of implementation defined TLB LOCKDOWN regs. + * For v6 and v5, these mappings are overly broad. */ - { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = CP_ANY, + { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 0, + .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, + { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 1, + .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, + { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 4, + .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, + { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 8, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, /* Cache maintenance ops; some of this space may be overridden later. */ { .name = "CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, @@ -555,6 +553,10 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, .type = ARM_CP_NO_RAW }, + { .name = "PRRR", .cp = 15, .crn = 10, .crm = 2, + .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP }, + { .name = "NMRR", .cp = 15, .crn = 10, .crm = 2, + .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_NOP }, REGINFO_SENTINEL }; @@ -1021,19 +1023,17 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .resetvalue = 0 }, /* For non-long-descriptor page tables these are PRRR and NMRR; * regardless they still act as reads-as-written for QEMU. - * The override is necessary because of the overly-broad TLB_LOCKDOWN - * definition. */ /* MAIR0/1 are defined separately from their 64-bit counterpart which * allows them to assign the correct fieldoffset based on the endianness * handled in the field definitions. */ - { .name = "MAIR0", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, + { .name = "MAIR0", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair0_s), offsetof(CPUARMState, cp15.mair0_ns) }, .resetfn = arm_cp_reset_ignore }, - { .name = "MAIR1", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, + { .name = "MAIR1", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair1_s), offsetof(CPUARMState, cp15.mair1_ns) }, @@ -2088,16 +2088,14 @@ static const ARMCPRegInfo mpidr_cp_reginfo[] = { }; static const ARMCPRegInfo lpae_cp_reginfo[] = { - /* NOP AMAIR0/1: the override is because these clash with the rather - * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo. - */ + /* NOP AMAIR0/1 */ { .name = "AMAIR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, /* AMAIR1 is mapped to AMAIR_EL1[63:32] */ { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0, .access = PL1_RW, .type = ARM_CP_64BIT, .resetvalue = 0, @@ -2362,6 +2360,14 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, .access = PL1_W, .type = ARM_CP_NOP }, /* TLBI operations */ + { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbiall_write }, + { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbiall_write }, { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW, @@ -2498,7 +2504,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { }; /* Used to describe the behaviour of EL2 regs when EL2 does not exist. */ -static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = { +static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, @@ -2511,6 +2517,28 @@ static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = { { .name = "CPTR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "MAIR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, + .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, + .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_CONST, + .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -2539,7 +2567,7 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) raw_write(env, ri, value); } -static const ARMCPRegInfo v8_el2_cp_reginfo[] = { +static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "HCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), @@ -2582,6 +2610,47 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2, .access = PL2_RW, .accessfn = cptr_access, .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.cptr_el[2]) }, + { .name = "MAIR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 0, + .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[2]), + .resetvalue = 0 }, + { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, + .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el[2]) }, + { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, + .access = PL2_RW, .writefn = vmsa_tcr_el1_write, + .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, + .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) }, + { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, + .access = PL2_RW, .raw_writefn = raw_write, .writefn = sctlr_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr_el[2]) }, + { .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2, + .access = PL2_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) }, + { .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, + { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, + .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, + .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, + { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_write }, + { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbi_aa64_vaa_write }, + { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbi_aa64_vaa_write }, REGINFO_SENTINEL }; @@ -3243,7 +3312,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, v8_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_EL2)) { - define_arm_cp_regs(cpu, v8_el2_cp_reginfo); + define_arm_cp_regs(cpu, el2_cp_reginfo); /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3)) { ARMCPRegInfo rvbar = { @@ -3258,7 +3327,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) * register the no_el2 reginfos. */ if (arm_feature(env, ARM_FEATURE_EL3)) { - define_arm_cp_regs(cpu, v8_el3_no_el2_cp_reginfo); + define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo); } } if (arm_feature(env, ARM_FEATURE_EL3)) { diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 16abbf198c..548bfd768d 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -600,3 +600,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, { return 0; } + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + return (data - 32) & 0xffff; +} diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs index 027b94e1d3..7a1df2c983 100644 --- a/target-i386/Makefile.objs +++ b/target-i386/Makefile.objs @@ -5,5 +5,3 @@ obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o obj-$(CONFIG_KVM) += kvm.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o -obj-$(CONFIG_LINUX_USER) += ioport-user.o -obj-$(CONFIG_BSD_USER) += ioport-user.o diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index 31a0c1e776..7a4fddd85f 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -23,6 +23,7 @@ #include "qom/cpu.h" #include "cpu.h" #include "qapi/error.h" +#include "qemu/notify.h" #ifdef TARGET_X86_64 #define TYPE_X86_CPU "x86_64-cpu" @@ -111,6 +112,8 @@ typedef struct X86CPU { /* in order to simplify APIC support, we leave this pointer to the user */ struct DeviceState *apic_state; + struct MemoryRegion *cpu_as_root, *cpu_as_mem, *smram; + Notifier machine_done; } X86CPU; static inline X86CPU *x86_env_get_cpu(CPUX86State *env) diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 3305e09413..4e7cdaaaa5 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -44,6 +44,7 @@ #include "hw/qdev-properties.h" #include "hw/cpu/icc_bus.h" #ifndef CONFIG_USER_ONLY +#include "exec/address-spaces.h" #include "hw/xen/xen.h" #include "hw/i386/apic_internal.h" #endif @@ -2750,6 +2751,21 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) object_property_set_bool(OBJECT(cpu->apic_state), true, "realized", errp); } + +static void x86_cpu_machine_done(Notifier *n, void *unused) +{ + X86CPU *cpu = container_of(n, X86CPU, machine_done); + MemoryRegion *smram = + (MemoryRegion *) object_resolve_path("/machine/smram", NULL); + + if (smram) { + cpu->smram = g_new(MemoryRegion, 1); + memory_region_init_alias(cpu->smram, OBJECT(cpu), "smram", + smram, 0, 1ull << 32); + memory_region_set_enabled(cpu->smram, false); + memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->smram, 1); + } +} #else static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) { @@ -2811,6 +2827,32 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #endif mce_init(cpu); + +#ifndef CONFIG_USER_ONLY + if (tcg_enabled()) { + cpu->cpu_as_mem = g_new(MemoryRegion, 1); + cpu->cpu_as_root = g_new(MemoryRegion, 1); + cs->as = g_new(AddressSpace, 1); + + /* Outer container... */ + memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull); + memory_region_set_enabled(cpu->cpu_as_root, true); + + /* ... with two regions inside: normal system memory with low + * priority, and... + */ + memory_region_init_alias(cpu->cpu_as_mem, OBJECT(cpu), "memory", + get_system_memory(), 0, ~0ull); + memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0); + memory_region_set_enabled(cpu->cpu_as_mem, true); + address_space_init(cs->as, cpu->cpu_as_root, "CPU"); + + /* ... SMRAM with higher priority, linked from /machine/smram. */ + cpu->machine_done.notify = x86_cpu_machine_done; + qemu_add_machine_init_done_notifier(&cpu->machine_done); + } +#endif + qemu_init_vcpu(cs); /* Only Intel CPUs support hyperthreading. Even though QEMU fixes this @@ -2834,6 +2876,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) cpu_reset(cs); xcc->parent_realize(dev, &local_err); + out: if (local_err != NULL) { error_propagate(errp, local_err); @@ -2841,12 +2884,126 @@ out: } } +typedef struct BitProperty { + uint32_t *ptr; + uint32_t mask; +} BitProperty; + +static void x86_cpu_get_bit_prop(Object *obj, + struct Visitor *v, + void *opaque, + const char *name, + Error **errp) +{ + BitProperty *fp = opaque; + bool value = (*fp->ptr & fp->mask) == fp->mask; + visit_type_bool(v, &value, name, errp); +} + +static void x86_cpu_set_bit_prop(Object *obj, + struct Visitor *v, + void *opaque, + const char *name, + Error **errp) +{ + DeviceState *dev = DEVICE(obj); + BitProperty *fp = opaque; + Error *local_err = NULL; + bool value; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_bool(v, &value, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (value) { + *fp->ptr |= fp->mask; + } else { + *fp->ptr &= ~fp->mask; + } +} + +static void x86_cpu_release_bit_prop(Object *obj, const char *name, + void *opaque) +{ + BitProperty *prop = opaque; + g_free(prop); +} + +/* Register a boolean property to get/set a single bit in a uint32_t field. + * + * The same property name can be registered multiple times to make it affect + * multiple bits in the same FeatureWord. In that case, the getter will return + * true only if all bits are set. + */ +static void x86_cpu_register_bit_prop(X86CPU *cpu, + const char *prop_name, + uint32_t *field, + int bitnr) +{ + BitProperty *fp; + ObjectProperty *op; + uint32_t mask = (1UL << bitnr); + + op = object_property_find(OBJECT(cpu), prop_name, NULL); + if (op) { + fp = op->opaque; + assert(fp->ptr == field); + fp->mask |= mask; + } else { + fp = g_new0(BitProperty, 1); + fp->ptr = field; + fp->mask = mask; + object_property_add(OBJECT(cpu), prop_name, "bool", + x86_cpu_get_bit_prop, + x86_cpu_set_bit_prop, + x86_cpu_release_bit_prop, fp, &error_abort); + } +} + +static void x86_cpu_register_feature_bit_props(X86CPU *cpu, + FeatureWord w, + int bitnr) +{ + Object *obj = OBJECT(cpu); + int i; + char **names; + FeatureWordInfo *fi = &feature_word_info[w]; + + if (!fi->feat_names) { + return; + } + if (!fi->feat_names[bitnr]) { + return; + } + + names = g_strsplit(fi->feat_names[bitnr], "|", 0); + + feat2prop(names[0]); + x86_cpu_register_bit_prop(cpu, names[0], &cpu->env.features[w], bitnr); + + for (i = 1; names[i]; i++) { + feat2prop(names[i]); + object_property_add_alias(obj, names[i], obj, g_strdup(names[0]), + &error_abort); + } + + g_strfreev(names); +} + static void x86_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); X86CPU *cpu = X86_CPU(obj); X86CPUClass *xcc = X86_CPU_GET_CLASS(obj); CPUX86State *env = &cpu->env; + FeatureWord w; static int inited; cs->env_ptr = env; @@ -2887,6 +3044,14 @@ static void x86_cpu_initfn(Object *obj) cpu->apic_id = -1; #endif + for (w = 0; w < FEATURE_WORDS; w++) { + int bitnr; + + for (bitnr = 0; bitnr < 32; bitnr++) { + x86_cpu_register_feature_bit_props(cpu, w, bitnr); + } + } + x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort); /* init various static tables used in TCG mode */ @@ -2941,7 +3106,9 @@ static bool x86_cpu_has_work(CPUState *cs) (cs->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_INIT | CPU_INTERRUPT_SIPI | - CPU_INTERRUPT_MCE)); + CPU_INTERRUPT_MCE)) || + ((cs->interrupt_request & CPU_INTERRUPT_SMI) && + !(env->hflags & HF_SMM_MASK)); } static Property x86_cpu_properties[] = { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 4ee12ca2e9..603aaf0924 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -180,15 +180,17 @@ /* hflags2 */ -#define HF2_GIF_SHIFT 0 /* if set CPU takes interrupts */ -#define HF2_HIF_SHIFT 1 /* value of IF_MASK when entering SVM */ -#define HF2_NMI_SHIFT 2 /* CPU serving NMI */ -#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */ - -#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT) -#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT) -#define HF2_NMI_MASK (1 << HF2_NMI_SHIFT) -#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT) +#define HF2_GIF_SHIFT 0 /* if set CPU takes interrupts */ +#define HF2_HIF_SHIFT 1 /* value of IF_MASK when entering SVM */ +#define HF2_NMI_SHIFT 2 /* CPU serving NMI */ +#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */ +#define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */ + +#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT) +#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT) +#define HF2_NMI_MASK (1 << HF2_NMI_SHIFT) +#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT) +#define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT) #define CR0_PE_SHIFT 0 #define CR0_MP_SHIFT 1 @@ -305,7 +307,7 @@ #define MSR_IA32_APICBASE 0x1b #define MSR_IA32_APICBASE_BSP (1<<8) #define MSR_IA32_APICBASE_ENABLE (1<<11) -#define MSR_IA32_APICBASE_BASE (0xfffff<<12) +#define MSR_IA32_APICBASE_BASE (0xfffffU<<12) #define MSR_IA32_FEATURE_CONTROL 0x0000003a #define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_TSCDEADLINE 0x6e0 @@ -1105,6 +1107,18 @@ int x86_cpu_handle_mmu_fault(CPUState *cpu, vaddr addr, int is_write, int mmu_idx); void x86_cpu_set_a20(X86CPU *cpu, int a20_state); +#ifndef CONFIG_USER_ONLY +uint8_t x86_ldub_phys(CPUState *cs, hwaddr addr); +uint32_t x86_lduw_phys(CPUState *cs, hwaddr addr); +uint32_t x86_ldl_phys(CPUState *cs, hwaddr addr); +uint64_t x86_ldq_phys(CPUState *cs, hwaddr addr); +void x86_stb_phys(CPUState *cs, hwaddr addr, uint8_t val); +void x86_stl_phys_notdirty(CPUState *cs, hwaddr addr, uint32_t val); +void x86_stw_phys(CPUState *cs, hwaddr addr, uint32_t val); +void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val); +void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val); +#endif + static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) { return (dr7 >> (index * 2)) & 1; @@ -1143,7 +1157,6 @@ void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3); void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4); /* hw/pc.c */ -void cpu_smm_update(CPUX86State *env); uint64_t cpu_get_tsc(CPUX86State *env); #define TARGET_PAGE_BITS 12 @@ -1292,6 +1305,11 @@ static inline void cpu_load_efer(CPUX86State *env, uint64_t val) } } +static inline MemTxAttrs cpu_get_mem_attrs(CPUX86State *env) +{ + return ((MemTxAttrs) { .secure = (env->hflags & HF_SMM_MASK) != 0 }); +} + /* fpu_helper.c */ void cpu_set_mxcsr(CPUX86State *env, uint32_t val); void cpu_set_fpuc(CPUX86State *env, uint16_t val); @@ -1304,7 +1322,9 @@ void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1); /* seg_helper.c */ void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw); +/* smm_helper.c */ void do_smm_enter(X86CPU *cpu); +void cpu_smm_update(X86CPU *cpu); void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); diff --git a/target-i386/helper.c b/target-i386/helper.c index 4f1ddf701e..5480a96a0f 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -565,7 +565,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & env->a20_mask; - pml4e = ldq_phys(cs->as, pml4e_addr); + pml4e = x86_ldq_phys(cs, pml4e_addr); if (!(pml4e & PG_PRESENT_MASK)) { goto do_fault; } @@ -574,12 +574,12 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, } if (!(pml4e & PG_ACCESSED_MASK)) { pml4e |= PG_ACCESSED_MASK; - stl_phys_notdirty(cs->as, pml4e_addr, pml4e); + x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); } ptep = pml4e ^ PG_NX_MASK; pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & env->a20_mask; - pdpe = ldq_phys(cs->as, pdpe_addr); + pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) { goto do_fault; } @@ -589,7 +589,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, ptep &= pdpe ^ PG_NX_MASK; if (!(pdpe & PG_ACCESSED_MASK)) { pdpe |= PG_ACCESSED_MASK; - stl_phys_notdirty(cs->as, pdpe_addr, pdpe); + x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); } if (pdpe & PG_PSE_MASK) { /* 1 GB page */ @@ -604,7 +604,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, /* XXX: load them when cr3 is loaded ? */ pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & env->a20_mask; - pdpe = ldq_phys(cs->as, pdpe_addr); + pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) { goto do_fault; } @@ -617,7 +617,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & env->a20_mask; - pde = ldq_phys(cs->as, pde_addr); + pde = x86_ldq_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) { goto do_fault; } @@ -635,11 +635,11 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, /* 4 KB page */ if (!(pde & PG_ACCESSED_MASK)) { pde |= PG_ACCESSED_MASK; - stl_phys_notdirty(cs->as, pde_addr, pde); + x86_stl_phys_notdirty(cs, pde_addr, pde); } pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & env->a20_mask; - pte = ldq_phys(cs->as, pte_addr); + pte = x86_ldq_phys(cs, pte_addr); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -655,7 +655,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, /* page directory entry */ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask; - pde = ldl_phys(cs->as, pde_addr); + pde = x86_ldl_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) { goto do_fault; } @@ -676,13 +676,13 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, if (!(pde & PG_ACCESSED_MASK)) { pde |= PG_ACCESSED_MASK; - stl_phys_notdirty(cs->as, pde_addr, pde); + x86_stl_phys_notdirty(cs, pde_addr, pde); } /* page directory entry */ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask; - pte = ldl_phys(cs->as, pte_addr); + pte = x86_ldl_phys(cs, pte_addr); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -737,7 +737,7 @@ do_check_protect_pse36: if (is_dirty) { pte |= PG_DIRTY_MASK; } - stl_phys_notdirty(cs->as, pte_addr, pte); + x86_stl_phys_notdirty(cs, pte_addr, pte); } /* the page can be put in the TLB */ @@ -771,7 +771,8 @@ do_check_protect_pse36: page_offset = vaddr & (page_size - 1); paddr = pte + page_offset; - tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size); + tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), + prot, mmu_idx, page_size); return 0; do_fault_rsvd: error_code |= PG_ERROR_RSVD_MASK; @@ -788,7 +789,7 @@ do_check_protect_pse36: error_code |= PG_ERROR_I_D_MASK; if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) { /* cr2 is not modified in case of exceptions */ - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), addr); } else { @@ -827,13 +828,13 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & env->a20_mask; - pml4e = ldq_phys(cs->as, pml4e_addr); + pml4e = x86_ldq_phys(cs, pml4e_addr); if (!(pml4e & PG_PRESENT_MASK)) { return -1; } pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & env->a20_mask; - pdpe = ldq_phys(cs->as, pdpe_addr); + pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) { return -1; } @@ -848,14 +849,14 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & env->a20_mask; - pdpe = ldq_phys(cs->as, pdpe_addr); + pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) return -1; } pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & env->a20_mask; - pde = ldq_phys(cs->as, pde_addr); + pde = x86_ldq_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) { return -1; } @@ -868,7 +869,7 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & env->a20_mask; page_size = 4096; - pte = ldq_phys(cs->as, pte_addr); + pte = x86_ldq_phys(cs, pte_addr); } if (!(pte & PG_PRESENT_MASK)) { return -1; @@ -878,7 +879,7 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) /* page directory entry */ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask; - pde = ldl_phys(cs->as, pde_addr); + pde = x86_ldl_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) return -1; if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { @@ -887,7 +888,7 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } else { /* page directory entry */ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask; - pte = ldl_phys(cs->as, pte_addr); + pte = x86_ldl_phys(cs, pte_addr); if (!(pte & PG_PRESENT_MASK)) { return -1; } @@ -1276,3 +1277,95 @@ void x86_cpu_exec_exit(CPUState *cs) env->eflags = cpu_compute_eflags(env); } + +#ifndef CONFIG_USER_ONLY +uint8_t x86_ldub_phys(CPUState *cs, hwaddr addr) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + return address_space_ldub(cs->as, addr, + cpu_get_mem_attrs(env), + NULL); +} + +uint32_t x86_lduw_phys(CPUState *cs, hwaddr addr) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + return address_space_lduw(cs->as, addr, + cpu_get_mem_attrs(env), + NULL); +} + +uint32_t x86_ldl_phys(CPUState *cs, hwaddr addr) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + return address_space_ldl(cs->as, addr, + cpu_get_mem_attrs(env), + NULL); +} + +uint64_t x86_ldq_phys(CPUState *cs, hwaddr addr) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + return address_space_ldq(cs->as, addr, + cpu_get_mem_attrs(env), + NULL); +} + +void x86_stb_phys(CPUState *cs, hwaddr addr, uint8_t val) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + address_space_stb(cs->as, addr, val, + cpu_get_mem_attrs(env), + NULL); +} + +void x86_stl_phys_notdirty(CPUState *cs, hwaddr addr, uint32_t val) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + address_space_stl_notdirty(cs->as, addr, val, + cpu_get_mem_attrs(env), + NULL); +} + +void x86_stw_phys(CPUState *cs, hwaddr addr, uint32_t val) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + address_space_stw(cs->as, addr, val, + cpu_get_mem_attrs(env), + NULL); +} + +void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + address_space_stl(cs->as, addr, val, + cpu_get_mem_attrs(env), + NULL); +} + +void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + address_space_stq(cs->as, addr, val, + cpu_get_mem_attrs(env), + NULL); +} +#endif diff --git a/target-i386/helper.h b/target-i386/helper.h index 8eb0145039..74308f442e 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -86,12 +86,12 @@ DEF_HELPER_1(wrmsr, void, env) DEF_HELPER_2(check_iob, void, env, i32) DEF_HELPER_2(check_iow, void, env, i32) DEF_HELPER_2(check_iol, void, env, i32) -DEF_HELPER_2(outb, void, i32, i32) -DEF_HELPER_1(inb, tl, i32) -DEF_HELPER_2(outw, void, i32, i32) -DEF_HELPER_1(inw, tl, i32) -DEF_HELPER_2(outl, void, i32, i32) -DEF_HELPER_1(inl, tl, i32) +DEF_HELPER_3(outb, void, env, i32, i32) +DEF_HELPER_2(inb, tl, env, i32) +DEF_HELPER_3(outw, void, env, i32, i32) +DEF_HELPER_2(inw, tl, env, i32) +DEF_HELPER_3(outl, void, env, i32, i32) +DEF_HELPER_2(inl, tl, env, i32) DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64) DEF_HELPER_3(vmexit, void, env, i32, i64) diff --git a/target-i386/ioport-user.c b/target-i386/ioport-user.c deleted file mode 100644 index f7636e0a87..0000000000 --- a/target-i386/ioport-user.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * qemu user ioport functions - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> - -#include "qemu.h" -#include "qemu-common.h" -#include "exec/ioport.h" - -void cpu_outb(pio_addr_t addr, uint8_t val) -{ - fprintf(stderr, "outb: port=0x%04"FMT_pioaddr", data=%02"PRIx8"\n", - addr, val); -} - -void cpu_outw(pio_addr_t addr, uint16_t val) -{ - fprintf(stderr, "outw: port=0x%04"FMT_pioaddr", data=%04"PRIx16"\n", - addr, val); -} - -void cpu_outl(pio_addr_t addr, uint32_t val) -{ - fprintf(stderr, "outl: port=0x%04"FMT_pioaddr", data=%08"PRIx32"\n", - addr, val); -} - -uint8_t cpu_inb(pio_addr_t addr) -{ - fprintf(stderr, "inb: port=0x%04"FMT_pioaddr"\n", addr); - return 0; -} - -uint16_t cpu_inw(pio_addr_t addr) -{ - fprintf(stderr, "inw: port=0x%04"FMT_pioaddr"\n", addr); - return 0; -} - -uint32_t cpu_inl(pio_addr_t addr) -{ - fprintf(stderr, "inl: port=0x%04"FMT_pioaddr"\n", addr); - return 0; -} diff --git a/target-i386/kvm.c b/target-i386/kvm.c index a26d25a81f..5a236e3103 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -2259,7 +2259,7 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) } cpu_set_apic_tpr(x86_cpu->apic_state, run->cr8); cpu_set_apic_base(x86_cpu->apic_state, run->apic_base); - return MEMTXATTRS_UNSPECIFIED; + return cpu_get_mem_attrs(env); } int kvm_arch_process_async_events(CPUState *cs) @@ -2766,3 +2766,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, { return 0; } + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + abort(); +} diff --git a/target-i386/machine.c b/target-i386/machine.c index cd1ddd29e9..69d86cb476 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -372,6 +372,9 @@ static int cpu_post_load(void *opaque, int version_id) } tlb_flush(cs, 1); + if (tcg_enabled()) { + cpu_smm_update(cpu); + } return 0; } diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c index 4aaf1e4d95..52c5d65e91 100644 --- a/target-i386/misc_helper.c +++ b/target-i386/misc_helper.c @@ -18,38 +18,71 @@ */ #include "cpu.h" -#include "exec/ioport.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" +#include "exec/address-spaces.h" -void helper_outb(uint32_t port, uint32_t data) +void helper_outb(CPUX86State *env, uint32_t port, uint32_t data) { - cpu_outb(port, data & 0xff); +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "outb: port=0x%04x, data=%02x\n", port, data); +#else + address_space_stb(&address_space_io, port, data, + cpu_get_mem_attrs(env), NULL); +#endif } -target_ulong helper_inb(uint32_t port) +target_ulong helper_inb(CPUX86State *env, uint32_t port) { - return cpu_inb(port); +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "inb: port=0x%04x\n", port); + return 0; +#else + return address_space_ldub(&address_space_io, port, + cpu_get_mem_attrs(env), NULL); +#endif } -void helper_outw(uint32_t port, uint32_t data) +void helper_outw(CPUX86State *env, uint32_t port, uint32_t data) { - cpu_outw(port, data & 0xffff); +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "outw: port=0x%04x, data=%04x\n", port, data); +#else + address_space_stw(&address_space_io, port, data, + cpu_get_mem_attrs(env), NULL); +#endif } -target_ulong helper_inw(uint32_t port) +target_ulong helper_inw(CPUX86State *env, uint32_t port) { - return cpu_inw(port); +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "inw: port=0x%04x\n", port); + return 0; +#else + return address_space_lduw(&address_space_io, port, + cpu_get_mem_attrs(env), NULL); +#endif } -void helper_outl(uint32_t port, uint32_t data) +void helper_outl(CPUX86State *env, uint32_t port, uint32_t data) { - cpu_outl(port, data); +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "outw: port=0x%04x, data=%08x\n", port, data); +#else + address_space_stl(&address_space_io, port, data, + cpu_get_mem_attrs(env), NULL); +#endif } -target_ulong helper_inl(uint32_t port) +target_ulong helper_inl(CPUX86State *env, uint32_t port) { - return cpu_inl(port); +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "inl: port=0x%04x\n", port); + return 0; +#else + return address_space_ldl(&address_space_io, port, + cpu_get_mem_attrs(env), NULL); +#endif } void helper_into(CPUX86State *env, int next_eip_addend) diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index 2bc757af31..8a4271ebe2 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -1144,7 +1144,7 @@ static void handle_even_inj(CPUX86State *env, int intno, int is_int, int error_code, int is_hw, int rm) { CPUState *cs = CPU(x86_env_get_cpu(env)); - uint32_t event_inj = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj)); if (!(event_inj & SVM_EVTINJ_VALID)) { @@ -1158,11 +1158,11 @@ static void handle_even_inj(CPUX86State *env, int intno, int is_int, event_inj = intno | type | SVM_EVTINJ_VALID; if (!rm && exception_has_error_code(intno)) { event_inj |= SVM_EVTINJ_VALID_ERR; - stl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err), error_code); } - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj); } @@ -1240,11 +1240,11 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, #if !defined(CONFIG_USER_ONLY) if (env->hflags & HF_SVMI_MASK) { CPUState *cs = CPU(cpu); - uint32_t event_inj = ldl_phys(cs->as, env->vm_vmcb + + uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj)); - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj & ~SVM_EVTINJ_VALID); } @@ -1339,7 +1339,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) int intno; /* FIXME: this should respect TPR */ cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0); - intno = ldl_phys(cs->as, env->vm_vmcb + intno = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno); diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c index c62f46847c..02e24b9236 100644 --- a/target-i386/smm_helper.c +++ b/target-i386/smm_helper.c @@ -40,6 +40,16 @@ void helper_rsm(CPUX86State *env) #define SMM_REVISION_ID 0x00020000 #endif +void cpu_smm_update(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + bool smm_enabled = (env->hflags & HF_SMM_MASK); + + if (cpu->smram) { + memory_region_set_enabled(cpu->smram, smm_enabled); + } +} + void do_smm_enter(X86CPU *cpu) { CPUX86State *env = &cpu->env; @@ -52,7 +62,12 @@ void do_smm_enter(X86CPU *cpu) log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); env->hflags |= HF_SMM_MASK; - cpu_smm_update(env); + if (env->hflags2 & HF2_NMI_MASK) { + env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK; + } else { + env->hflags2 |= HF2_NMI_MASK; + } + cpu_smm_update(cpu); sm_state = env->smbase + 0x8000; @@ -60,83 +75,83 @@ void do_smm_enter(X86CPU *cpu) for (i = 0; i < 6; i++) { dt = &env->segs[i]; offset = 0x7e00 + i * 16; - stw_phys(cs->as, sm_state + offset, dt->selector); - stw_phys(cs->as, sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff); - stl_phys(cs->as, sm_state + offset + 4, dt->limit); - stq_phys(cs->as, sm_state + offset + 8, dt->base); + x86_stw_phys(cs, sm_state + offset, dt->selector); + x86_stw_phys(cs, sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff); + x86_stl_phys(cs, sm_state + offset + 4, dt->limit); + x86_stq_phys(cs, sm_state + offset + 8, dt->base); } - stq_phys(cs->as, sm_state + 0x7e68, env->gdt.base); - stl_phys(cs->as, sm_state + 0x7e64, env->gdt.limit); + x86_stq_phys(cs, sm_state + 0x7e68, env->gdt.base); + x86_stl_phys(cs, sm_state + 0x7e64, env->gdt.limit); - stw_phys(cs->as, sm_state + 0x7e70, env->ldt.selector); - stq_phys(cs->as, sm_state + 0x7e78, env->ldt.base); - stl_phys(cs->as, sm_state + 0x7e74, env->ldt.limit); - stw_phys(cs->as, sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff); + x86_stw_phys(cs, sm_state + 0x7e70, env->ldt.selector); + x86_stq_phys(cs, sm_state + 0x7e78, env->ldt.base); + x86_stl_phys(cs, sm_state + 0x7e74, env->ldt.limit); + x86_stw_phys(cs, sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff); - stq_phys(cs->as, sm_state + 0x7e88, env->idt.base); - stl_phys(cs->as, sm_state + 0x7e84, env->idt.limit); + x86_stq_phys(cs, sm_state + 0x7e88, env->idt.base); + x86_stl_phys(cs, sm_state + 0x7e84, env->idt.limit); - stw_phys(cs->as, sm_state + 0x7e90, env->tr.selector); - stq_phys(cs->as, sm_state + 0x7e98, env->tr.base); - stl_phys(cs->as, sm_state + 0x7e94, env->tr.limit); - stw_phys(cs->as, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff); + x86_stw_phys(cs, sm_state + 0x7e90, env->tr.selector); + x86_stq_phys(cs, sm_state + 0x7e98, env->tr.base); + x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit); + x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff); - stq_phys(cs->as, sm_state + 0x7ed0, env->efer); + x86_stq_phys(cs, sm_state + 0x7ed0, env->efer); - stq_phys(cs->as, sm_state + 0x7ff8, env->regs[R_EAX]); - stq_phys(cs->as, sm_state + 0x7ff0, env->regs[R_ECX]); - stq_phys(cs->as, sm_state + 0x7fe8, env->regs[R_EDX]); - stq_phys(cs->as, sm_state + 0x7fe0, env->regs[R_EBX]); - stq_phys(cs->as, sm_state + 0x7fd8, env->regs[R_ESP]); - stq_phys(cs->as, sm_state + 0x7fd0, env->regs[R_EBP]); - stq_phys(cs->as, sm_state + 0x7fc8, env->regs[R_ESI]); - stq_phys(cs->as, sm_state + 0x7fc0, env->regs[R_EDI]); + x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]); + x86_stq_phys(cs, sm_state + 0x7ff0, env->regs[R_ECX]); + x86_stq_phys(cs, sm_state + 0x7fe8, env->regs[R_EDX]); + x86_stq_phys(cs, sm_state + 0x7fe0, env->regs[R_EBX]); + x86_stq_phys(cs, sm_state + 0x7fd8, env->regs[R_ESP]); + x86_stq_phys(cs, sm_state + 0x7fd0, env->regs[R_EBP]); + x86_stq_phys(cs, sm_state + 0x7fc8, env->regs[R_ESI]); + x86_stq_phys(cs, sm_state + 0x7fc0, env->regs[R_EDI]); for (i = 8; i < 16; i++) { - stq_phys(cs->as, sm_state + 0x7ff8 - i * 8, env->regs[i]); + x86_stq_phys(cs, sm_state + 0x7ff8 - i * 8, env->regs[i]); } - stq_phys(cs->as, sm_state + 0x7f78, env->eip); - stl_phys(cs->as, sm_state + 0x7f70, cpu_compute_eflags(env)); - stl_phys(cs->as, sm_state + 0x7f68, env->dr[6]); - stl_phys(cs->as, sm_state + 0x7f60, env->dr[7]); + x86_stq_phys(cs, sm_state + 0x7f78, env->eip); + x86_stl_phys(cs, sm_state + 0x7f70, cpu_compute_eflags(env)); + x86_stl_phys(cs, sm_state + 0x7f68, env->dr[6]); + x86_stl_phys(cs, sm_state + 0x7f60, env->dr[7]); - stl_phys(cs->as, sm_state + 0x7f48, env->cr[4]); - stq_phys(cs->as, sm_state + 0x7f50, env->cr[3]); - stl_phys(cs->as, sm_state + 0x7f58, env->cr[0]); + x86_stl_phys(cs, sm_state + 0x7f48, env->cr[4]); + x86_stq_phys(cs, sm_state + 0x7f50, env->cr[3]); + x86_stl_phys(cs, sm_state + 0x7f58, env->cr[0]); - stl_phys(cs->as, sm_state + 0x7efc, SMM_REVISION_ID); - stl_phys(cs->as, sm_state + 0x7f00, env->smbase); + x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); + x86_stl_phys(cs, sm_state + 0x7f00, env->smbase); #else - stl_phys(cs->as, sm_state + 0x7ffc, env->cr[0]); - stl_phys(cs->as, sm_state + 0x7ff8, env->cr[3]); - stl_phys(cs->as, sm_state + 0x7ff4, cpu_compute_eflags(env)); - stl_phys(cs->as, sm_state + 0x7ff0, env->eip); - stl_phys(cs->as, sm_state + 0x7fec, env->regs[R_EDI]); - stl_phys(cs->as, sm_state + 0x7fe8, env->regs[R_ESI]); - stl_phys(cs->as, sm_state + 0x7fe4, env->regs[R_EBP]); - stl_phys(cs->as, sm_state + 0x7fe0, env->regs[R_ESP]); - stl_phys(cs->as, sm_state + 0x7fdc, env->regs[R_EBX]); - stl_phys(cs->as, sm_state + 0x7fd8, env->regs[R_EDX]); - stl_phys(cs->as, sm_state + 0x7fd4, env->regs[R_ECX]); - stl_phys(cs->as, sm_state + 0x7fd0, env->regs[R_EAX]); - stl_phys(cs->as, sm_state + 0x7fcc, env->dr[6]); - stl_phys(cs->as, sm_state + 0x7fc8, env->dr[7]); - - stl_phys(cs->as, sm_state + 0x7fc4, env->tr.selector); - stl_phys(cs->as, sm_state + 0x7f64, env->tr.base); - stl_phys(cs->as, sm_state + 0x7f60, env->tr.limit); - stl_phys(cs->as, sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff); - - stl_phys(cs->as, sm_state + 0x7fc0, env->ldt.selector); - stl_phys(cs->as, sm_state + 0x7f80, env->ldt.base); - stl_phys(cs->as, sm_state + 0x7f7c, env->ldt.limit); - stl_phys(cs->as, sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff); - - stl_phys(cs->as, sm_state + 0x7f74, env->gdt.base); - stl_phys(cs->as, sm_state + 0x7f70, env->gdt.limit); - - stl_phys(cs->as, sm_state + 0x7f58, env->idt.base); - stl_phys(cs->as, sm_state + 0x7f54, env->idt.limit); + x86_stl_phys(cs, sm_state + 0x7ffc, env->cr[0]); + x86_stl_phys(cs, sm_state + 0x7ff8, env->cr[3]); + x86_stl_phys(cs, sm_state + 0x7ff4, cpu_compute_eflags(env)); + x86_stl_phys(cs, sm_state + 0x7ff0, env->eip); + x86_stl_phys(cs, sm_state + 0x7fec, env->regs[R_EDI]); + x86_stl_phys(cs, sm_state + 0x7fe8, env->regs[R_ESI]); + x86_stl_phys(cs, sm_state + 0x7fe4, env->regs[R_EBP]); + x86_stl_phys(cs, sm_state + 0x7fe0, env->regs[R_ESP]); + x86_stl_phys(cs, sm_state + 0x7fdc, env->regs[R_EBX]); + x86_stl_phys(cs, sm_state + 0x7fd8, env->regs[R_EDX]); + x86_stl_phys(cs, sm_state + 0x7fd4, env->regs[R_ECX]); + x86_stl_phys(cs, sm_state + 0x7fd0, env->regs[R_EAX]); + x86_stl_phys(cs, sm_state + 0x7fcc, env->dr[6]); + x86_stl_phys(cs, sm_state + 0x7fc8, env->dr[7]); + + x86_stl_phys(cs, sm_state + 0x7fc4, env->tr.selector); + x86_stl_phys(cs, sm_state + 0x7f64, env->tr.base); + x86_stl_phys(cs, sm_state + 0x7f60, env->tr.limit); + x86_stl_phys(cs, sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff); + + x86_stl_phys(cs, sm_state + 0x7fc0, env->ldt.selector); + x86_stl_phys(cs, sm_state + 0x7f80, env->ldt.base); + x86_stl_phys(cs, sm_state + 0x7f7c, env->ldt.limit); + x86_stl_phys(cs, sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff); + + x86_stl_phys(cs, sm_state + 0x7f74, env->gdt.base); + x86_stl_phys(cs, sm_state + 0x7f70, env->gdt.limit); + + x86_stl_phys(cs, sm_state + 0x7f58, env->idt.base); + x86_stl_phys(cs, sm_state + 0x7f54, env->idt.limit); for (i = 0; i < 6; i++) { dt = &env->segs[i]; @@ -145,15 +160,15 @@ void do_smm_enter(X86CPU *cpu) } else { offset = 0x7f2c + (i - 3) * 12; } - stl_phys(cs->as, sm_state + 0x7fa8 + i * 4, dt->selector); - stl_phys(cs->as, sm_state + offset + 8, dt->base); - stl_phys(cs->as, sm_state + offset + 4, dt->limit); - stl_phys(cs->as, sm_state + offset, (dt->flags >> 8) & 0xf0ff); + x86_stl_phys(cs, sm_state + 0x7fa8 + i * 4, dt->selector); + x86_stl_phys(cs, sm_state + offset + 8, dt->base); + x86_stl_phys(cs, sm_state + offset + 4, dt->limit); + x86_stl_phys(cs, sm_state + offset, (dt->flags >> 8) & 0xf0ff); } - stl_phys(cs->as, sm_state + 0x7f14, env->cr[4]); + x86_stl_phys(cs, sm_state + 0x7f14, env->cr[4]); - stl_phys(cs->as, sm_state + 0x7efc, SMM_REVISION_ID); - stl_phys(cs->as, sm_state + 0x7ef8, env->smbase); + x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); + x86_stl_phys(cs, sm_state + 0x7ef8, env->smbase); #endif /* init SMM cpu state */ @@ -172,22 +187,22 @@ void do_smm_enter(X86CPU *cpu) cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase, 0xffffffff, DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK); + DESC_G_MASK | DESC_A_MASK); cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK); + DESC_G_MASK | DESC_A_MASK); cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK); + DESC_G_MASK | DESC_A_MASK); cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK); + DESC_G_MASK | DESC_A_MASK); cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK); + DESC_G_MASK | DESC_A_MASK); cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK); + DESC_G_MASK | DESC_A_MASK); } void helper_rsm(CPUX86State *env) @@ -200,91 +215,91 @@ void helper_rsm(CPUX86State *env) sm_state = env->smbase + 0x8000; #ifdef TARGET_X86_64 - cpu_load_efer(env, ldq_phys(cs->as, sm_state + 0x7ed0)); - - env->gdt.base = ldq_phys(cs->as, sm_state + 0x7e68); - env->gdt.limit = ldl_phys(cs->as, sm_state + 0x7e64); - - env->ldt.selector = lduw_phys(cs->as, sm_state + 0x7e70); - env->ldt.base = ldq_phys(cs->as, sm_state + 0x7e78); - env->ldt.limit = ldl_phys(cs->as, sm_state + 0x7e74); - env->ldt.flags = (lduw_phys(cs->as, sm_state + 0x7e72) & 0xf0ff) << 8; - - env->idt.base = ldq_phys(cs->as, sm_state + 0x7e88); - env->idt.limit = ldl_phys(cs->as, sm_state + 0x7e84); - - env->tr.selector = lduw_phys(cs->as, sm_state + 0x7e90); - env->tr.base = ldq_phys(cs->as, sm_state + 0x7e98); - env->tr.limit = ldl_phys(cs->as, sm_state + 0x7e94); - env->tr.flags = (lduw_phys(cs->as, sm_state + 0x7e92) & 0xf0ff) << 8; - - env->regs[R_EAX] = ldq_phys(cs->as, sm_state + 0x7ff8); - env->regs[R_ECX] = ldq_phys(cs->as, sm_state + 0x7ff0); - env->regs[R_EDX] = ldq_phys(cs->as, sm_state + 0x7fe8); - env->regs[R_EBX] = ldq_phys(cs->as, sm_state + 0x7fe0); - env->regs[R_ESP] = ldq_phys(cs->as, sm_state + 0x7fd8); - env->regs[R_EBP] = ldq_phys(cs->as, sm_state + 0x7fd0); - env->regs[R_ESI] = ldq_phys(cs->as, sm_state + 0x7fc8); - env->regs[R_EDI] = ldq_phys(cs->as, sm_state + 0x7fc0); + cpu_load_efer(env, x86_ldq_phys(cs, sm_state + 0x7ed0)); + + env->gdt.base = x86_ldq_phys(cs, sm_state + 0x7e68); + env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7e64); + + env->ldt.selector = x86_lduw_phys(cs, sm_state + 0x7e70); + env->ldt.base = x86_ldq_phys(cs, sm_state + 0x7e78); + env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7e74); + env->ldt.flags = (x86_lduw_phys(cs, sm_state + 0x7e72) & 0xf0ff) << 8; + + env->idt.base = x86_ldq_phys(cs, sm_state + 0x7e88); + env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7e84); + + env->tr.selector = x86_lduw_phys(cs, sm_state + 0x7e90); + env->tr.base = x86_ldq_phys(cs, sm_state + 0x7e98); + env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7e94); + env->tr.flags = (x86_lduw_phys(cs, sm_state + 0x7e92) & 0xf0ff) << 8; + + env->regs[R_EAX] = x86_ldq_phys(cs, sm_state + 0x7ff8); + env->regs[R_ECX] = x86_ldq_phys(cs, sm_state + 0x7ff0); + env->regs[R_EDX] = x86_ldq_phys(cs, sm_state + 0x7fe8); + env->regs[R_EBX] = x86_ldq_phys(cs, sm_state + 0x7fe0); + env->regs[R_ESP] = x86_ldq_phys(cs, sm_state + 0x7fd8); + env->regs[R_EBP] = x86_ldq_phys(cs, sm_state + 0x7fd0); + env->regs[R_ESI] = x86_ldq_phys(cs, sm_state + 0x7fc8); + env->regs[R_EDI] = x86_ldq_phys(cs, sm_state + 0x7fc0); for (i = 8; i < 16; i++) { - env->regs[i] = ldq_phys(cs->as, sm_state + 0x7ff8 - i * 8); + env->regs[i] = x86_ldq_phys(cs, sm_state + 0x7ff8 - i * 8); } - env->eip = ldq_phys(cs->as, sm_state + 0x7f78); - cpu_load_eflags(env, ldl_phys(cs->as, sm_state + 0x7f70), + env->eip = x86_ldq_phys(cs, sm_state + 0x7f78); + cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7f70), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); - env->dr[6] = ldl_phys(cs->as, sm_state + 0x7f68); - env->dr[7] = ldl_phys(cs->as, sm_state + 0x7f60); + env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7f68); + env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7f60); - cpu_x86_update_cr4(env, ldl_phys(cs->as, sm_state + 0x7f48)); - cpu_x86_update_cr3(env, ldq_phys(cs->as, sm_state + 0x7f50)); - cpu_x86_update_cr0(env, ldl_phys(cs->as, sm_state + 0x7f58)); + cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f48)); + cpu_x86_update_cr3(env, x86_ldq_phys(cs, sm_state + 0x7f50)); + cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7f58)); for (i = 0; i < 6; i++) { offset = 0x7e00 + i * 16; cpu_x86_load_seg_cache(env, i, - lduw_phys(cs->as, sm_state + offset), - ldq_phys(cs->as, sm_state + offset + 8), - ldl_phys(cs->as, sm_state + offset + 4), - (lduw_phys(cs->as, sm_state + offset + 2) & + x86_lduw_phys(cs, sm_state + offset), + x86_ldq_phys(cs, sm_state + offset + 8), + x86_ldl_phys(cs, sm_state + offset + 4), + (x86_lduw_phys(cs, sm_state + offset + 2) & 0xf0ff) << 8); } - val = ldl_phys(cs->as, sm_state + 0x7efc); /* revision ID */ + val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ if (val & 0x20000) { - env->smbase = ldl_phys(cs->as, sm_state + 0x7f00) & ~0x7fff; + env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00) & ~0x7fff; } #else - cpu_x86_update_cr0(env, ldl_phys(cs->as, sm_state + 0x7ffc)); - cpu_x86_update_cr3(env, ldl_phys(cs->as, sm_state + 0x7ff8)); - cpu_load_eflags(env, ldl_phys(cs->as, sm_state + 0x7ff4), + cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7ffc)); + cpu_x86_update_cr3(env, x86_ldl_phys(cs, sm_state + 0x7ff8)); + cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7ff4), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); - env->eip = ldl_phys(cs->as, sm_state + 0x7ff0); - env->regs[R_EDI] = ldl_phys(cs->as, sm_state + 0x7fec); - env->regs[R_ESI] = ldl_phys(cs->as, sm_state + 0x7fe8); - env->regs[R_EBP] = ldl_phys(cs->as, sm_state + 0x7fe4); - env->regs[R_ESP] = ldl_phys(cs->as, sm_state + 0x7fe0); - env->regs[R_EBX] = ldl_phys(cs->as, sm_state + 0x7fdc); - env->regs[R_EDX] = ldl_phys(cs->as, sm_state + 0x7fd8); - env->regs[R_ECX] = ldl_phys(cs->as, sm_state + 0x7fd4); - env->regs[R_EAX] = ldl_phys(cs->as, sm_state + 0x7fd0); - env->dr[6] = ldl_phys(cs->as, sm_state + 0x7fcc); - env->dr[7] = ldl_phys(cs->as, sm_state + 0x7fc8); - - env->tr.selector = ldl_phys(cs->as, sm_state + 0x7fc4) & 0xffff; - env->tr.base = ldl_phys(cs->as, sm_state + 0x7f64); - env->tr.limit = ldl_phys(cs->as, sm_state + 0x7f60); - env->tr.flags = (ldl_phys(cs->as, sm_state + 0x7f5c) & 0xf0ff) << 8; - - env->ldt.selector = ldl_phys(cs->as, sm_state + 0x7fc0) & 0xffff; - env->ldt.base = ldl_phys(cs->as, sm_state + 0x7f80); - env->ldt.limit = ldl_phys(cs->as, sm_state + 0x7f7c); - env->ldt.flags = (ldl_phys(cs->as, sm_state + 0x7f78) & 0xf0ff) << 8; - - env->gdt.base = ldl_phys(cs->as, sm_state + 0x7f74); - env->gdt.limit = ldl_phys(cs->as, sm_state + 0x7f70); - - env->idt.base = ldl_phys(cs->as, sm_state + 0x7f58); - env->idt.limit = ldl_phys(cs->as, sm_state + 0x7f54); + env->eip = x86_ldl_phys(cs, sm_state + 0x7ff0); + env->regs[R_EDI] = x86_ldl_phys(cs, sm_state + 0x7fec); + env->regs[R_ESI] = x86_ldl_phys(cs, sm_state + 0x7fe8); + env->regs[R_EBP] = x86_ldl_phys(cs, sm_state + 0x7fe4); + env->regs[R_ESP] = x86_ldl_phys(cs, sm_state + 0x7fe0); + env->regs[R_EBX] = x86_ldl_phys(cs, sm_state + 0x7fdc); + env->regs[R_EDX] = x86_ldl_phys(cs, sm_state + 0x7fd8); + env->regs[R_ECX] = x86_ldl_phys(cs, sm_state + 0x7fd4); + env->regs[R_EAX] = x86_ldl_phys(cs, sm_state + 0x7fd0); + env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7fcc); + env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7fc8); + + env->tr.selector = x86_ldl_phys(cs, sm_state + 0x7fc4) & 0xffff; + env->tr.base = x86_ldl_phys(cs, sm_state + 0x7f64); + env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7f60); + env->tr.flags = (x86_ldl_phys(cs, sm_state + 0x7f5c) & 0xf0ff) << 8; + + env->ldt.selector = x86_ldl_phys(cs, sm_state + 0x7fc0) & 0xffff; + env->ldt.base = x86_ldl_phys(cs, sm_state + 0x7f80); + env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7f7c); + env->ldt.flags = (x86_ldl_phys(cs, sm_state + 0x7f78) & 0xf0ff) << 8; + + env->gdt.base = x86_ldl_phys(cs, sm_state + 0x7f74); + env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7f70); + + env->idt.base = x86_ldl_phys(cs, sm_state + 0x7f58); + env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7f54); for (i = 0; i < 6; i++) { if (i < 3) { @@ -293,22 +308,26 @@ void helper_rsm(CPUX86State *env) offset = 0x7f2c + (i - 3) * 12; } cpu_x86_load_seg_cache(env, i, - ldl_phys(cs->as, + x86_ldl_phys(cs, sm_state + 0x7fa8 + i * 4) & 0xffff, - ldl_phys(cs->as, sm_state + offset + 8), - ldl_phys(cs->as, sm_state + offset + 4), - (ldl_phys(cs->as, + x86_ldl_phys(cs, sm_state + offset + 8), + x86_ldl_phys(cs, sm_state + offset + 4), + (x86_ldl_phys(cs, sm_state + offset) & 0xf0ff) << 8); } - cpu_x86_update_cr4(env, ldl_phys(cs->as, sm_state + 0x7f14)); + cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f14)); - val = ldl_phys(cs->as, sm_state + 0x7efc); /* revision ID */ + val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ if (val & 0x20000) { - env->smbase = ldl_phys(cs->as, sm_state + 0x7ef8) & ~0x7fff; + env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8) & ~0x7fff; } #endif + if ((env->hflags2 & HF2_SMM_INSIDE_NMI_MASK) == 0) { + env->hflags2 &= ~HF2_NMI_MASK; + } + env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK; env->hflags &= ~HF_SMM_MASK; - cpu_smm_update(env); + cpu_smm_update(cpu); qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n"); log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); diff --git a/target-i386/svm_helper.c b/target-i386/svm_helper.c index 429d029a3d..f1fabf54e7 100644 --- a/target-i386/svm_helper.c +++ b/target-i386/svm_helper.c @@ -87,13 +87,13 @@ static inline void svm_save_seg(CPUX86State *env, hwaddr addr, { CPUState *cs = CPU(x86_env_get_cpu(env)); - stw_phys(cs->as, addr + offsetof(struct vmcb_seg, selector), + x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, selector), sc->selector); - stq_phys(cs->as, addr + offsetof(struct vmcb_seg, base), + x86_stq_phys(cs, addr + offsetof(struct vmcb_seg, base), sc->base); - stl_phys(cs->as, addr + offsetof(struct vmcb_seg, limit), + x86_stl_phys(cs, addr + offsetof(struct vmcb_seg, limit), sc->limit); - stw_phys(cs->as, addr + offsetof(struct vmcb_seg, attrib), + x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, attrib), ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00)); } @@ -103,11 +103,11 @@ static inline void svm_load_seg(CPUX86State *env, hwaddr addr, CPUState *cs = CPU(x86_env_get_cpu(env)); unsigned int flags; - sc->selector = lduw_phys(cs->as, + sc->selector = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, selector)); - sc->base = ldq_phys(cs->as, addr + offsetof(struct vmcb_seg, base)); - sc->limit = ldl_phys(cs->as, addr + offsetof(struct vmcb_seg, limit)); - flags = lduw_phys(cs->as, addr + offsetof(struct vmcb_seg, attrib)); + sc->base = x86_ldq_phys(cs, addr + offsetof(struct vmcb_seg, base)); + sc->limit = x86_ldl_phys(cs, addr + offsetof(struct vmcb_seg, limit)); + flags = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, attrib)); sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12); } @@ -141,32 +141,32 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->vm_vmcb = addr; /* save the current CPU state in the hsave page */ - stq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), env->gdt.base); - stl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), + x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit); - stq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.idtr.base), + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.base), env->idt.base); - stl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), + x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), env->idt.limit); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rflags), cpu_compute_eflags(env)); @@ -179,30 +179,30 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ds), &env->segs[R_DS]); - stq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.rip), + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip), env->eip + next_eip_addend); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); /* load the interception bitmaps so we do not need to access the vmcb in svm mode */ - env->intercept = ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + env->intercept = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.intercept)); - env->intercept_cr_read = lduw_phys(cs->as, env->vm_vmcb + + env->intercept_cr_read = x86_lduw_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_read)); - env->intercept_cr_write = lduw_phys(cs->as, env->vm_vmcb + + env->intercept_cr_write = x86_lduw_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_write)); - env->intercept_dr_read = lduw_phys(cs->as, env->vm_vmcb + + env->intercept_dr_read = x86_lduw_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_read)); - env->intercept_dr_write = lduw_phys(cs->as, env->vm_vmcb + + env->intercept_dr_write = x86_lduw_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_write)); - env->intercept_exceptions = ldl_phys(cs->as, env->vm_vmcb + + env->intercept_exceptions = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.intercept_exceptions )); @@ -210,35 +210,35 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) /* enable intercepts */ env->hflags |= HF_SVMI_MASK; - env->tsc_offset = ldq_phys(cs->as, env->vm_vmcb + + env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.tsc_offset)); - env->gdt.base = ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + env->gdt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base)); - env->gdt.limit = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + env->gdt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit)); - env->idt.base = ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + env->idt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base)); - env->idt.limit = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + env->idt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit)); /* clear exit_info_2 so we behave like the real hardware */ - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0); - cpu_x86_update_cr0(env, ldq_phys(cs->as, + cpu_x86_update_cr0(env, x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr0))); - cpu_x86_update_cr4(env, ldq_phys(cs->as, + cpu_x86_update_cr4(env, x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr4))); - cpu_x86_update_cr3(env, ldq_phys(cs->as, + cpu_x86_update_cr3(env, x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr3))); - env->cr[2] = ldq_phys(cs->as, + env->cr[2] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr2)); - int_ctl = ldl_phys(cs->as, + int_ctl = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); if (int_ctl & V_INTR_MASKING_MASK) { @@ -250,10 +250,10 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) } cpu_load_efer(env, - ldq_phys(cs->as, + x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.efer))); env->eflags = 0; - cpu_load_eflags(env, ldq_phys(cs->as, + cpu_load_eflags(env, x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rflags)), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); @@ -267,21 +267,21 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), R_DS); - env->eip = ldq_phys(cs->as, + env->eip = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip)); - env->regs[R_ESP] = ldq_phys(cs->as, + env->regs[R_ESP] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rsp)); - env->regs[R_EAX] = ldq_phys(cs->as, + env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rax)); - env->dr[7] = ldq_phys(cs->as, + env->dr[7] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr7)); - env->dr[6] = ldq_phys(cs->as, + env->dr[6] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr6)); /* FIXME: guest state consistency checks */ - switch (ldub_phys(cs->as, + switch (x86_ldub_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) { case TLB_CONTROL_DO_NOTHING: break; @@ -300,12 +300,12 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) } /* maybe we need to inject an event */ - event_inj = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj)); if (event_inj & SVM_EVTINJ_VALID) { uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK; uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR; - uint32_t event_inj_err = ldl_phys(cs->as, env->vm_vmcb + + uint32_t event_inj_err = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err)); @@ -372,7 +372,7 @@ void helper_vmload(CPUX86State *env, int aflag) qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", - addr, ldq_phys(cs->as, addr + offsetof(struct vmcb, + addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.fs.base)), env->segs[R_FS].base); @@ -382,18 +382,18 @@ void helper_vmload(CPUX86State *env, int aflag) svm_load_seg(env, addr + offsetof(struct vmcb, save.ldtr), &env->ldt); #ifdef TARGET_X86_64 - env->kernelgsbase = ldq_phys(cs->as, addr + offsetof(struct vmcb, + env->kernelgsbase = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base)); - env->lstar = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.lstar)); - env->cstar = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.cstar)); - env->fmask = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.sfmask)); + env->lstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.lstar)); + env->cstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.cstar)); + env->fmask = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sfmask)); #endif - env->star = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.star)); - env->sysenter_cs = ldq_phys(cs->as, + env->star = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.star)); + env->sysenter_cs = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_cs)); - env->sysenter_esp = ldq_phys(cs->as, addr + offsetof(struct vmcb, + env->sysenter_esp = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp)); - env->sysenter_eip = ldq_phys(cs->as, addr + offsetof(struct vmcb, + env->sysenter_eip = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip)); } @@ -412,7 +412,7 @@ void helper_vmsave(CPUX86State *env, int aflag) qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", - addr, ldq_phys(cs->as, + addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.fs.base)), env->segs[R_FS].base); @@ -426,18 +426,18 @@ void helper_vmsave(CPUX86State *env, int aflag) &env->ldt); #ifdef TARGET_X86_64 - stq_phys(cs->as, addr + offsetof(struct vmcb, save.kernel_gs_base), + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base), env->kernelgsbase); - stq_phys(cs->as, addr + offsetof(struct vmcb, save.lstar), env->lstar); - stq_phys(cs->as, addr + offsetof(struct vmcb, save.cstar), env->cstar); - stq_phys(cs->as, addr + offsetof(struct vmcb, save.sfmask), env->fmask); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.lstar), env->lstar); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.cstar), env->cstar); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sfmask), env->fmask); #endif - stq_phys(cs->as, addr + offsetof(struct vmcb, save.star), env->star); - stq_phys(cs->as, + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.star), env->star); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs); - stq_phys(cs->as, addr + offsetof(struct vmcb, save.sysenter_esp), + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp), env->sysenter_esp); - stq_phys(cs->as, addr + offsetof(struct vmcb, save.sysenter_eip), + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip), env->sysenter_eip); } @@ -515,7 +515,7 @@ void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, case SVM_EXIT_MSR: if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) { /* FIXME: this should be read in at vmrun (faster this way?) */ - uint64_t addr = ldq_phys(cs->as, env->vm_vmcb + + uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.msrpm_base_pa)); uint32_t t0, t1; @@ -541,7 +541,7 @@ void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, t1 = 0; break; } - if (ldub_phys(cs->as, addr + t1) & ((1 << param) << t0)) { + if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { helper_vmexit(env, type, param); } } @@ -567,13 +567,13 @@ void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) { /* FIXME: this should be read in at vmrun (faster this way?) */ - uint64_t addr = ldq_phys(cs->as, env->vm_vmcb + + uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.iopm_base_pa)); uint16_t mask = (1 << ((param >> 4) & 7)) - 1; - if (lduw_phys(cs->as, addr + port / 8) & (mask << (port & 7))) { + if (x86_lduw_phys(cs, addr + port / 8) & (mask << (port & 7))) { /* next env->eip */ - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), env->eip + next_eip_addend); helper_vmexit(env, SVM_EXIT_IOIO, param | (port << 16)); @@ -590,17 +590,17 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" PRIx64 ", " TARGET_FMT_lx ")!\n", exit_code, exit_info_1, - ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2)), env->eip); if (env->hflags & HF_INHIBIT_IRQ_MASK) { - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_state), SVM_INTERRUPT_SHADOW_MASK); env->hflags &= ~HF_INHIBIT_IRQ_MASK; } else { - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); } @@ -614,50 +614,50 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), &env->segs[R_DS]); - stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), env->gdt.base); - stl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit); - stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), env->idt.base); - stl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), env->idt.limit); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]); - int_ctl = ldl_phys(cs->as, + int_ctl = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK); int_ctl |= env->v_tpr & V_TPR_MASK; if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { int_ctl |= V_IRQ_MASK; } - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl); - stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.rflags), + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rflags), cpu_compute_eflags(env)); - stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.rip), + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip), env->eip); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]); - stq_phys(cs->as, + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]); - stb_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.cpl), + x86_stb_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cpl), env->hflags & HF_CPL_MASK); /* Reload the host state from vm_hsave */ @@ -668,32 +668,32 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; env->tsc_offset = 0; - env->gdt.base = ldq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, + env->gdt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base)); - env->gdt.limit = ldl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, + env->gdt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit)); - env->idt.base = ldq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, + env->idt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.base)); - env->idt.limit = ldl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, + env->idt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit)); - cpu_x86_update_cr0(env, ldq_phys(cs->as, + cpu_x86_update_cr0(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr0)) | CR0_PE_MASK); - cpu_x86_update_cr4(env, ldq_phys(cs->as, + cpu_x86_update_cr4(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr4))); - cpu_x86_update_cr3(env, ldq_phys(cs->as, + cpu_x86_update_cr3(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr3))); /* we need to set the efer after the crs so the hidden flags get set properly */ - cpu_load_efer(env, ldq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, + cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.efer))); env->eflags = 0; - cpu_load_eflags(env, ldq_phys(cs->as, + cpu_load_eflags(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rflags)), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | @@ -708,33 +708,33 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ds), R_DS); - env->eip = ldq_phys(cs->as, + env->eip = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip)); - env->regs[R_ESP] = ldq_phys(cs->as, env->vm_hsave + + env->regs[R_ESP] = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rsp)); - env->regs[R_EAX] = ldq_phys(cs->as, env->vm_hsave + + env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rax)); - env->dr[6] = ldq_phys(cs->as, + env->dr[6] = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.dr6)); - env->dr[7] = ldq_phys(cs->as, + env->dr[7] = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.dr7)); /* other setups */ - stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, control.exit_code), + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_code), exit_code); - stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), exit_info_1); - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info), - ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj))); - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err), - ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, + x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err))); - stl_phys(cs->as, + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0); env->hflags2 &= ~HF2_GIF_MASK; diff --git a/target-i386/translate.c b/target-i386/translate.c index 305ce5077c..58b1959154 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -631,13 +631,13 @@ static void gen_helper_in_func(TCGMemOp ot, TCGv v, TCGv_i32 n) { switch (ot) { case MO_8: - gen_helper_inb(v, n); + gen_helper_inb(v, cpu_env, n); break; case MO_16: - gen_helper_inw(v, n); + gen_helper_inw(v, cpu_env, n); break; case MO_32: - gen_helper_inl(v, n); + gen_helper_inl(v, cpu_env, n); break; default: tcg_abort(); @@ -648,13 +648,13 @@ static void gen_helper_out_func(TCGMemOp ot, TCGv_i32 v, TCGv_i32 n) { switch (ot) { case MO_8: - gen_helper_outb(v, n); + gen_helper_outb(cpu_env, v, n); break; case MO_16: - gen_helper_outw(v, n); + gen_helper_outw(cpu_env, v, n); break; case MO_32: - gen_helper_outl(v, n); + gen_helper_outl(cpu_env, v, n); break; default: tcg_abort(); diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index 4ea04acc4d..d73e1c7286 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -285,7 +285,6 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 -#define MMAP_SHIFT TARGET_PAGE_BITS #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 diff --git a/target-mips/kvm.c b/target-mips/kvm.c index 59eb11105a..948619fbab 100644 --- a/target-mips/kvm.c +++ b/target-mips/kvm.c @@ -696,3 +696,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, { return 0; } + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + abort(); +} diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 1da9ea81e5..afb4696b8a 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1884,6 +1884,23 @@ int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) return 0; } +static inline int kvmppc_enable_hcall(KVMState *s, target_ulong hcall) +{ + return kvm_vm_enable_cap(s, KVM_CAP_PPC_ENABLE_HCALL, 0, hcall, 1); +} + +void kvmppc_enable_logical_ci_hcalls(void) +{ + /* + * FIXME: it would be nice if we could detect the cases where + * we're using a device which requires the in kernel + * implementation of these hcalls, but the kernel lacks them and + * produce a warning. + */ + kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_LOAD); + kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_STORE); +} + void kvmppc_set_papr(PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -2410,3 +2427,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, { return 0; } + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + return data & 0xffff; +} diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 2e0224c6af..4d30e27951 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -24,6 +24,7 @@ bool kvmppc_get_host_serial(char **buf); int kvmppc_get_hasidle(CPUPPCState *env); int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len); int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level); +void kvmppc_enable_logical_ci_hcalls(void); void kvmppc_set_papr(PowerPCCPU *cpu); int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version); void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); @@ -107,6 +108,10 @@ static inline int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) return -1; } +static inline void kvmppc_enable_logical_ci_hcalls(void) +{ +} + static inline void kvmppc_set_papr(PowerPCCPU *cpu) { } diff --git a/target-s390x/cc_helper.c b/target-s390x/cc_helper.c index 00bc883a8a..bfce3f1e60 100644 --- a/target-s390x/cc_helper.c +++ b/target-s390x/cc_helper.c @@ -195,7 +195,7 @@ static uint32_t cc_calc_abs_64(int64_t dst) if ((uint64_t)dst == 0x8000000000000000ULL) { return 3; } else if (dst) { - return 1; + return 2; } else { return 0; } @@ -296,7 +296,7 @@ static uint32_t cc_calc_abs_32(int32_t dst) if ((uint32_t)dst == 0x80000000UL) { return 3; } else if (dst) { - return 1; + return 2; } else { return 0; } diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index d2f9836e86..7f17823e40 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -118,6 +118,10 @@ static void s390_cpu_initial_reset(CPUState *s) env->pfault_token = -1UL; + /* tininess for underflow is detected before rounding */ + set_float_detect_tininess(float_tininess_before_rounding, + &env->fpu_status); + /* Reset state inside the kernel that we cannot access yet from QEMU. */ if (kvm_enabled()) { kvm_s390_reset_vcpu(cpu); @@ -143,6 +147,10 @@ static void s390_cpu_full_reset(CPUState *s) env->pfault_token = -1UL; + /* tininess for underflow is detected before rounding */ + set_float_detect_tininess(float_tininess_before_rounding, + &env->fpu_status); + /* Reset state inside the kernel that we cannot access yet from QEMU. */ if (kvm_enabled()) { kvm_s390_reset_vcpu(cpu); diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index a71abaeef7..584e74b89a 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -48,7 +48,7 @@ #define MMU_MODE1_SUFFIX _secondary #define MMU_MODE2_SUFFIX _home -#define MMU_USER_IDX 1 +#define MMU_USER_IDX 0 #define MAX_EXT_QUEUE 16 #define MAX_IO_QUEUE 16 @@ -302,13 +302,39 @@ static inline CPU_DoubleU *get_freg(CPUS390XState *cs, int nr) #define CR0_LOWPROT 0x0000000010000000ULL #define CR0_EDAT 0x0000000000800000ULL +/* MMU */ +#define MMU_PRIMARY_IDX 0 +#define MMU_SECONDARY_IDX 1 +#define MMU_HOME_IDX 2 + static inline int cpu_mmu_index (CPUS390XState *env) { - if (env->psw.mask & PSW_MASK_PSTATE) { - return 1; + switch (env->psw.mask & PSW_MASK_ASC) { + case PSW_ASC_PRIMARY: + return MMU_PRIMARY_IDX; + case PSW_ASC_SECONDARY: + return MMU_SECONDARY_IDX; + case PSW_ASC_HOME: + return MMU_HOME_IDX; + case PSW_ASC_ACCREG: + /* Fallthrough: access register mode is not yet supported */ + default: + abort(); } +} - return 0; +static inline uint64_t cpu_mmu_idx_to_asc(int mmu_idx) +{ + switch (mmu_idx) { + case MMU_PRIMARY_IDX: + return PSW_ASC_PRIMARY; + case MMU_SECONDARY_IDX: + return PSW_ASC_SECONDARY; + case MMU_HOME_IDX: + return PSW_ASC_HOME; + default: + abort(); + } } static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc, @@ -995,6 +1021,11 @@ static inline uint64_t time2tod(uint64_t ns) { return (ns << 9) / 125; } +/* Converts s390's clock format to ns */ +static inline uint64_t tod2time(uint64_t t) { + return (t * 125) >> 9; +} + static inline void cpu_inject_ext(S390CPU *cpu, uint32_t code, uint32_t param, uint64_t param64) { diff --git a/target-s390x/fpu_helper.c b/target-s390x/fpu_helper.c index b946ec1d51..45b7ddfbe3 100644 --- a/target-s390x/fpu_helper.c +++ b/target-s390x/fpu_helper.c @@ -265,7 +265,7 @@ uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2) { float64 ret = float32_to_float64(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return ret; + return float64_maybe_silence_nan(ret); } /* convert 128-bit float to 64-bit float */ @@ -273,7 +273,7 @@ uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al) { float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status); handle_exceptions(env, GETPC()); - return ret; + return float64_maybe_silence_nan(ret); } /* convert 64-bit float to 128-bit float */ @@ -281,7 +281,7 @@ uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) { float128 ret = float64_to_float128(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return RET128(ret); + return RET128(float128_maybe_silence_nan(ret)); } /* convert 32-bit float to 128-bit float */ @@ -289,7 +289,7 @@ uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2) { float128 ret = float32_to_float128(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return RET128(ret); + return RET128(float128_maybe_silence_nan(ret)); } /* convert 64-bit float to 32-bit float */ @@ -297,7 +297,7 @@ uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2) { float32 ret = float64_to_float32(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return ret; + return float32_maybe_silence_nan(ret); } /* convert 128-bit float to 32-bit float */ @@ -305,7 +305,7 @@ uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al) { float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status); handle_exceptions(env, GETPC()); - return ret; + return float32_maybe_silence_nan(ret); } /* 32-bit FP compare */ @@ -552,6 +552,37 @@ uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) return ret; } +/* round to integer 32-bit */ +uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float32 ret = float32_round_to_int(f2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* round to integer 64-bit */ +uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float64 ret = float64_round_to_int(f2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* round to integer 128-bit */ +uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float128 ret = float128_round_to_int(make_float128(ah, al), + &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); +} + /* 32-bit FP multiply and add */ uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) diff --git a/target-s390x/helper.c b/target-s390x/helper.c index 6b47766494..90d273c098 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -112,7 +112,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, { S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; - uint64_t asc = env->psw.mask & PSW_MASK_ASC; + uint64_t asc = cpu_mmu_idx_to_asc(mmu_idx); target_ulong vaddr, raddr; int prot; diff --git a/target-s390x/helper.h b/target-s390x/helper.h index 8d2c8596bb..7e048ecb00 100644 --- a/target-s390x/helper.h +++ b/target-s390x/helper.h @@ -15,10 +15,6 @@ DEF_HELPER_4(clst, i64, env, i64, i64, i64) DEF_HELPER_4(mvpg, void, env, i64, i64, i64) DEF_HELPER_4(mvst, i64, env, i64, i64, i64) DEF_HELPER_5(ex, i32, env, i32, i64, i64, i64) -DEF_HELPER_FLAGS_1(abs_i32, TCG_CALL_NO_RWG_SE, i32, s32) -DEF_HELPER_FLAGS_1(nabs_i32, TCG_CALL_NO_RWG_SE, s32, s32) -DEF_HELPER_FLAGS_1(abs_i64, TCG_CALL_NO_RWG_SE, i64, s64) -DEF_HELPER_FLAGS_1(nabs_i64, TCG_CALL_NO_RWG_SE, s64, s64) DEF_HELPER_FLAGS_4(stam, TCG_CALL_NO_WG, void, env, i32, i64, i32) DEF_HELPER_FLAGS_4(lam, TCG_CALL_NO_WG, void, env, i32, i64, i32) DEF_HELPER_4(mvcle, i32, env, i32, i64, i32) @@ -64,6 +60,9 @@ DEF_HELPER_FLAGS_4(clgxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) DEF_HELPER_FLAGS_3(clfeb, TCG_CALL_NO_WG, i64, env, i64, i32) DEF_HELPER_FLAGS_3(clfdb, TCG_CALL_NO_WG, i64, env, i64, i32) DEF_HELPER_FLAGS_4(clfxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_FLAGS_3(fieb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_3(fidb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_4(fixb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) DEF_HELPER_FLAGS_4(maeb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(madb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(mseb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) @@ -78,6 +77,8 @@ DEF_HELPER_FLAGS_3(sqxb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32) DEF_HELPER_FLAGS_4(unpk, TCG_CALL_NO_WG, void, env, i32, i64, i64) DEF_HELPER_FLAGS_4(tr, TCG_CALL_NO_WG, void, env, i32, i64, i64) +DEF_HELPER_4(tre, i64, env, i64, i64, i64) +DEF_HELPER_4(trt, i32, env, i32, i64, i64) DEF_HELPER_4(cksm, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64) DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_RWG, void, env, i64) diff --git a/target-s390x/insn-data.def b/target-s390x/insn-data.def index 72c3a2edda..1223670721 100644 --- a/target-s390x/insn-data.def +++ b/target-s390x/insn-data.def @@ -34,6 +34,9 @@ C(0xb34a, AXBR, RRE, Z, 0, x2_o, x1, 0, axb, f128) C(0xed0a, AEB, RXE, Z, e1, m2_32u, new, e1, aeb, f32) C(0xed1a, ADB, RXE, Z, f1_o, m2_64, f1, 0, adb, f64) +/* ADD HIGH */ + C(0xb9c8, AHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, add, adds32) + C(0xb9d8, AHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, add, adds32) /* ADD IMMEDIATE */ C(0xc209, AFI, RIL_a, EI, r1, i2, new, r1_32, add, adds32) C(0xeb6a, ASI, SIY, GIE, m1_32s, i2, new, m1_32, add, adds32) @@ -41,6 +44,8 @@ C(0xc208, AGFI, RIL_a, EI, r1, i2, r1, 0, add, adds64) C(0xeb7a, AGSI, SIY, GIE, m1_64, i2, new, m1_64, add, adds64) C(0xecd9, AGHIK, RIE_d, DO, r3, i2, r1, 0, add, adds64) +/* ADD IMMEDIATE HIGH */ + C(0xcc08, AIH, RIL_a, HW, r1_sr32, i2, new, r1_32h, add, adds32) /* ADD HALFWORD */ C(0x4a00, AH, RX_a, Z, r1, m2_16s, new, r1_32, add, adds32) C(0xe37a, AHY, RXY_a, LD, r1, m2_16s, new, r1_32, add, adds32) @@ -58,6 +63,9 @@ C(0xb9ea, ALGRK, RRF_a, DO, r2, r3, r1, 0, add, addu64) C(0xe30a, ALG, RXY_a, Z, r1, m2_64, r1, 0, add, addu64) C(0xe31a, ALGF, RXY_a, Z, r1, m2_32u, r1, 0, add, addu64) +/* ADD LOGICAL HIGH */ + C(0xb9ca, ALHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, add, addu32) + C(0xb9da, ALHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, add, addu32) /* ADD LOGICAL IMMEDIATE */ C(0xc20b, ALFI, RIL_a, EI, r1, i2_32u, new, r1_32, add, addu32) C(0xc20a, ALGFI, RIL_a, EI, r1, i2_32u, r1, 0, add, addu64) @@ -66,6 +74,9 @@ C(0xecda, ALHSIK, RIE_d, DO, r3, i2, new, r1_32, add, addu32) C(0xeb7e, ALGSI, SIY, GIE, m1_64, i2, new, m1_64, add, addu64) C(0xecdb, ALGHSIK, RIE_d, DO, r3, i2, r1, 0, add, addu64) +/* ADD LOGICAL WITH SIGNED IMMEDIATE HIGH */ + C(0xcc0a, ALSIH, RIL_a, HW, r1_sr32, i2, new, r1_32h, add, addu32) + C(0xcc0b, ALSIHN, RIL_a, HW, r1_sr32, i2, new, r1_32h, add, 0) /* ADD LOGICAL WITH CARRY */ C(0xb998, ALCR, RRE, Z, r1, r2, new, r1_32, addc, addc32) C(0xb988, ALCGR, RRE, Z, r1, r2, r1, 0, addc, addc64) @@ -111,6 +122,8 @@ /* BRANCH RELATIVE ON COUNT */ C(0xa706, BRCT, RI_b, Z, 0, 0, 0, 0, bct32, 0) C(0xa707, BRCTG, RI_b, Z, 0, 0, 0, 0, bct64, 0) +/* BRANCH RELATIVE ON COUNT HIGH */ + C(0xcc06, BRCTH, RIL_b, HW, 0, 0, 0, 0, bcth, 0) /* BRANCH ON INDEX */ D(0x8600, BXH, RS_a, Z, 0, a2, 0, 0, bx32, 0, 0) D(0x8700, BXLE, RS_a, Z, 0, a2, 0, 0, bx32, 0, 1) @@ -159,8 +172,14 @@ C(0xe55c, CHSI, SIL, GIE, m1_32s, i2, 0, 0, 0, cmps64) C(0xe558, CGHSI, SIL, GIE, m1_64, i2, 0, 0, 0, cmps64) /* COMPARE HALFWORD RELATIVE LONG */ - C(0xc605, CHRL, RIL_a, GIE, r1_o, mri2_32s, 0, 0, 0, cmps32) - C(0xc604, CGHRL, RIL_a, GIE, r1_o, mri2_64, 0, 0, 0, cmps64) + C(0xc605, CHRL, RIL_b, GIE, r1_o, mri2_32s, 0, 0, 0, cmps32) + C(0xc604, CGHRL, RIL_b, GIE, r1_o, mri2_64, 0, 0, 0, cmps64) +/* COMPARE HIGH */ + C(0xb9cd, CHHR, RRE, HW, r1_sr32, r2_sr32, 0, 0, 0, cmps32) + C(0xb9dd, CHLR, RRE, HW, r1_sr32, r2_o, 0, 0, 0, cmps32) + C(0xe3cd, CHF, RXY_a, HW, r1_sr32, m2_32s, 0, 0, 0, cmps32) +/* COMPARE IMMEDIATE HIGH */ + C(0xcc0d, CIH, RIL_a, HW, r1_sr32, i2, 0, 0, 0, cmps32) /* COMPARE LOGICAL */ C(0x1500, CLR, RR_a, Z, r1, r2, 0, 0, 0, cmpu32) @@ -171,6 +190,10 @@ C(0xe321, CLG, RXY_a, Z, r1, m2_64, 0, 0, 0, cmpu64) C(0xe331, CLGF, RXY_a, Z, r1, m2_32u, 0, 0, 0, cmpu64) C(0xd500, CLC, SS_a, Z, la1, a2, 0, 0, clc, 0) +/* COMPARE LOGICAL HIGH */ + C(0xb9cf, CLHHR, RRE, HW, r1_sr32, r2_sr32, 0, 0, 0, cmpu32) + C(0xb9df, CLHLR, RRE, HW, r1_sr32, r2_o, 0, 0, 0, cmpu32) + C(0xe3cf, CLHF, RXY_a, HW, r1_sr32, m2_32s, 0, 0, 0, cmpu32) /* COMPARE LOGICAL IMMEDIATE */ C(0xc20f, CLFI, RIL_a, EI, r1, i2, 0, 0, 0, cmpu32) C(0xc20e, CLGFI, RIL_a, EI, r1, i2_32u, 0, 0, 0, cmpu64) @@ -179,6 +202,8 @@ C(0xe555, CLHHSI, SIL, GIE, m1_16u, i2_16u, 0, 0, 0, cmpu64) C(0xe55d, CLFHSI, SIL, GIE, m1_32u, i2_16u, 0, 0, 0, cmpu64) C(0xe559, CLGHSI, SIL, GIE, m1_64, i2_16u, 0, 0, 0, cmpu64) +/* COMPARE LOGICAL IMMEDIATE HIGH */ + C(0xcc0f, CLIH, RIL_a, HW, r1_sr32, i2, 0, 0, 0, cmpu32) /* COMPARE LOGICAL RELATIVE LONG */ C(0xc60f, CLRL, RIL_b, GIE, r1_o, mri2_32u, 0, 0, 0, cmpu32) C(0xc60a, CLGRL, RIL_b, GIE, r1_o, mri2_64, 0, 0, 0, cmpu64) @@ -230,8 +255,10 @@ /* COMPARE LOGICAL AND TRAP */ D(0xb973, CLRT, RRF_c, GIE, r1_32u, r2_32u, 0, 0, ct, 0, 1) D(0xb961, CLGRT, RRF_c, GIE, r1_o, r2_o, 0, 0, ct, 0, 1) + D(0xeb23, CLT, RSY_b, MIE, r1_32u, m2_32u, 0, 0, ct, 0, 1) + D(0xeb2b, CLGT, RSY_b, MIE, r1_o, m2_64, 0, 0, ct, 0, 1) D(0xec73, CLFIT, RIE_a, GIE, r1_32u, i2_32u, 0, 0, ct, 0, 1) - D(0xec71, CLGIT, RIE_a, GIE, r1_o, i2_32u, 0, 0, ct, 0, 0) + D(0xec71, CLGIT, RIE_a, GIE, r1_o, i2_32u, 0, 0, ct, 0, 1) /* CONVERT TO DECIMAL */ C(0x4e00, CVD, RX_a, Z, r1_o, a2, 0, 0, cvd, 0) @@ -336,7 +363,7 @@ /* LOAD */ C(0x1800, LR, RR_a, Z, 0, r2_o, 0, cond_r1r2_32, mov2, 0) C(0x5800, L, RX_a, Z, 0, a2, new, r1_32, ld32s, 0) - C(0xe358, LY, RXY_a, Z, 0, a2, new, r1_32, ld32s, 0) + C(0xe358, LY, RXY_a, LD, 0, a2, new, r1_32, ld32s, 0) C(0xb904, LGR, RRE, Z, 0, r2_o, 0, r1, mov2, 0) C(0xb914, LGFR, RRE, Z, 0, r2_32s, 0, r1, mov2, 0) C(0xe304, LG, RXY_a, Z, 0, a2, r1, 0, ld64, 0) @@ -357,6 +384,9 @@ /* LOAD ADDRESS */ C(0x4100, LA, RX_a, Z, 0, a2, 0, r1, mov2, 0) C(0xe371, LAY, RXY_a, LD, 0, a2, 0, r1, mov2, 0) +/* LOAD ADDRESS EXTENDED */ + C(0x5100, LAE, RX_a, Z, 0, a2, 0, r1, mov2e, 0) + C(0xe375, LAEY, RXY_a, GIE, 0, a2, 0, r1, mov2e, 0) /* LOAD ADDRESS RELATIVE LONG */ C(0xc000, LARL, RIL_b, Z, 0, ri2, 0, r1, mov2, 0) /* LOAD AND ADD */ @@ -384,11 +414,16 @@ C(0xb302, LTEBR, RRE, Z, 0, e2, 0, cond_e1e2, mov2, f32) C(0xb312, LTDBR, RRE, Z, 0, f2_o, 0, f1, mov2, f64) C(0xb342, LTXBR, RRE, Z, 0, x2_o, 0, x1, movx, f128) +/* LOAD AND TRAP */ + C(0xe39f, LAT, RXY_a, LAT, 0, m2_32u, r1, 0, lat, 0) + C(0xe385, LGAT, RXY_a, LAT, 0, a2, r1, 0, lgat, 0) /* LOAD BYTE */ C(0xb926, LBR, RRE, EI, 0, r2_8s, 0, r1_32, mov2, 0) C(0xb906, LGBR, RRE, EI, 0, r2_8s, 0, r1, mov2, 0) C(0xe376, LB, RXY_a, LD, 0, a2, new, r1_32, ld8s, 0) C(0xe377, LGB, RXY_a, LD, 0, a2, r1, 0, ld8s, 0) +/* LOAD BYTE HIGH */ + C(0xe3c0, LBH, RXY_a, HW, 0, a2, new, r1_32h, ld8s, 0) /* LOAD COMPLEMENT */ C(0x1300, LCR, RR_a, Z, 0, r2, new, r1_32, neg, neg32) C(0xb903, LCGR, RRE, Z, 0, r2, r1, 0, neg, neg64) @@ -403,15 +438,23 @@ C(0x4800, LH, RX_a, Z, 0, a2, new, r1_32, ld16s, 0) C(0xe378, LHY, RXY_a, LD, 0, a2, new, r1_32, ld16s, 0) C(0xe315, LGH, RXY_a, Z, 0, a2, r1, 0, ld16s, 0) +/* LOAD HALFWORD HIGH */ + C(0xe3c4, LHH, RXY_a, HW, 0, a2, new, r1_32h, ld16s, 0) /* LOAD HALFWORD IMMEDIATE */ C(0xa708, LHI, RI_a, Z, 0, i2, 0, r1_32, mov2, 0) C(0xa709, LGHI, RI_a, Z, 0, i2, 0, r1, mov2, 0) /* LOAD HALFWORD RELATIVE LONG */ C(0xc405, LHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16s, 0) C(0xc404, LGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16s, 0) +/* LOAD HIGH */ + C(0xe3ca, LFH, RXY_a, HW, 0, a2, new, r1_32h, ld32u, 0) +/* LOAG HIGH AND TRAP */ + C(0xe3c8, LFHAT, RXY_a, LAT, 0, m2_32u, r1, 0, lfhat, 0) /* LOAD LOGICAL */ C(0xb916, LLGFR, RRE, Z, 0, r2_32u, 0, r1, mov2, 0) C(0xe316, LLGF, RXY_a, Z, 0, a2, r1, 0, ld32u, 0) +/* LOAD LOGICAL AND TRAP */ + C(0xe39d, LLGFAT, RXY_a, LAT, 0, a2, r1, 0, llgfat, 0) /* LOAD LOGICAL RELATIVE LONG */ C(0xc40e, LLGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32u, 0) /* LOAD LOGICAL CHARACTER */ @@ -419,11 +462,15 @@ C(0xb984, LLGCR, RRE, EI, 0, r2_8u, 0, r1, mov2, 0) C(0xe394, LLC, RXY_a, EI, 0, a2, new, r1_32, ld8u, 0) C(0xe390, LLGC, RXY_a, Z, 0, a2, r1, 0, ld8u, 0) +/* LOAD LOGICAL CHARACTER HIGH */ + C(0xe3c2, LLCH, RXY_a, HW, 0, a2, new, r1_32h, ld8u, 0) /* LOAD LOGICAL HALFWORD */ C(0xb995, LLHR, RRE, EI, 0, r2_16u, 0, r1_32, mov2, 0) C(0xb985, LLGHR, RRE, EI, 0, r2_16u, 0, r1, mov2, 0) C(0xe395, LLH, RXY_a, EI, 0, a2, new, r1_32, ld16u, 0) C(0xe391, LLGH, RXY_a, Z, 0, a2, r1, 0, ld16u, 0) +/* LOAD LOGICAL HALFWORD HIGH */ + C(0xe3c6, LLHH, RXY_a, HW, 0, a2, new, r1_32h, ld16u, 0) /* LOAD LOGICAL HALFWORD RELATIVE LONG */ C(0xc402, LLHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16u, 0) C(0xc406, LLGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16u, 0) @@ -437,6 +484,9 @@ /* LOAD LOGICAL THIRTY ONE BITS */ C(0xb917, LLGTR, RRE, Z, 0, r2_o, r1, 0, llgt, 0) C(0xe317, LLGT, RXY_a, Z, 0, m2_32u, r1, 0, llgt, 0) +/* LOAD LOGICAL THIRTY ONE BITS AND TRAP */ + C(0xe39c, LLGTAT, RXY_a, LAT, 0, m2_32u, r1, 0, llgtat, 0) + /* LOAD FPR FROM GR */ C(0xb3c1, LDGR, RRE, FPRGR, 0, r2_o, 0, f1, mov2, 0) /* LOAD GR FROM FPR */ @@ -448,6 +498,7 @@ C(0xb301, LNEBR, RRE, Z, 0, e2, new, e1, nabsf32, f32) C(0xb311, LNDBR, RRE, Z, 0, f2_o, f1, 0, nabsf64, f64) C(0xb341, LNXBR, RRE, Z, 0, x2_o, x1, 0, nabsf128, f128) + C(0xb371, LNDFR, RRE, FPSSH, 0, f2_o, f1, 0, nabsf64, 0) /* LOAD ON CONDITION */ C(0xb9f2, LOCR, RRF_c, LOC, r1, r2, new, r1_32, loc, 0) C(0xb9e2, LOCGR, RRF_c, LOC, r1, r2, r1, 0, loc, 0) @@ -461,6 +512,7 @@ C(0xb300, LPEBR, RRE, Z, 0, e2, new, e1, absf32, f32) C(0xb310, LPDBR, RRE, Z, 0, f2_o, f1, 0, absf64, f64) C(0xb340, LPXBR, RRE, Z, 0, x2_o, x1, 0, absf128, f128) + C(0xb370, LPDFR, RRE, FPSSH, 0, f2_o, f1, 0, absf64, 0) /* LOAD REVERSED */ C(0xb91f, LRVR, RRE, Z, 0, r2_32u, new, r1_32, rev32, 0) C(0xb90f, LRVGR, RRE, Z, 0, r2_o, r1, 0, rev64, 0) @@ -476,6 +528,10 @@ C(0xb29d, LFPC, S, Z, 0, m2_32u, 0, 0, sfpc, 0) /* LOAD FPC AND SIGNAL */ C(0xb2bd, LFAS, S, IEEEE_SIM, 0, m2_32u, 0, 0, sfas, 0) +/* LOAD FP INTEGER */ + C(0xb357, FIEBR, RRF_e, Z, 0, e2, new, e1, fieb, 0) + C(0xb35f, FIDBR, RRF_e, Z, 0, f2_o, f1, 0, fidb, 0) + C(0xb347, FIXBR, RRF_e, Z, 0, x2_o, x1, 0, fixb, 0) /* LOAD LENGTHENED */ C(0xb304, LDEBR, RRE, Z, 0, e2, f1, 0, ldeb, 0) @@ -595,8 +651,9 @@ /* ROTATE THEN INSERT SELECTED BITS */ C(0xec55, RISBG, RIE_f, GIE, 0, r2, r1, 0, risbg, s64) - C(0xec5d, RISBHG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0) - C(0xec51, RISBLG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0) + C(0xec59, RISBGN, RIE_f, MIE, 0, r2, r1, 0, risbg, 0) + C(0xec5d, RISBHG, RIE_f, HW, 0, r2, r1, 0, risbg, 0) + C(0xec51, RISBLG, RIE_f, HW, 0, r2, r1, 0, risbg, 0) /* ROTATE_THEN <OP> SELECTED BITS */ C(0xec54, RNSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0) C(0xec56, ROSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0) @@ -619,7 +676,7 @@ C(0xb299, SRNM, S, Z, 0, 0, 0, 0, srnm, 0) C(0xb2b8, SRNMB, S, FPE, 0, 0, 0, 0, srnm, 0) /* SET DFP ROUNDING MODE */ - C(0xb2b9, SRNMT, S, DFP, 0, 0, 0, 0, srnm, 0) + C(0xb2b9, SRNMT, S, DFPR, 0, 0, 0, 0, srnm, 0) /* SHIFT LEFT SINGLE */ D(0x8b00, SLA, RS_a, Z, r1, sh32, new, r1_32, sla, 0, 31) @@ -667,15 +724,21 @@ /* STORE CHARACTER */ C(0x4200, STC, RX_a, Z, r1_o, a2, 0, 0, st8, 0) C(0xe372, STCY, RXY_a, LD, r1_o, a2, 0, 0, st8, 0) +/* STORE CHARACTER HIGH */ + C(0xe3c3, STCH, RXY_a, HW, r1_sr32, a2, 0, 0, st8, 0) /* STORE CHARACTERS UNDER MASK */ D(0xbe00, STCM, RS_b, Z, r1_o, a2, 0, 0, stcm, 0, 0) D(0xeb2d, STCMY, RSY_b, LD, r1_o, a2, 0, 0, stcm, 0, 0) - D(0xeb2c, STCMH, RSY_b, LD, r1_o, a2, 0, 0, stcm, 0, 32) + D(0xeb2c, STCMH, RSY_b, Z, r1_o, a2, 0, 0, stcm, 0, 32) /* STORE HALFWORD */ C(0x4000, STH, RX_a, Z, r1_o, a2, 0, 0, st16, 0) C(0xe370, STHY, RXY_a, LD, r1_o, a2, 0, 0, st16, 0) +/* STORE HALFWORD HIGH */ + C(0xe3c7, STHH, RXY_a, HW, r1_sr32, a2, 0, 0, st16, 0) /* STORE HALFWORD RELATIVE LONG */ C(0xc407, STHRL, RIL_b, GIE, r1_o, ri2, 0, 0, st16, 0) +/* STORE HIGH */ + C(0xe3cb, STFH, RXY_a, HW, r1_sr32, a2, 0, 0, st32, 0) /* STORE ON CONDITION */ D(0xebf3, STOC, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 0) D(0xebe3, STOCG, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 1) @@ -715,6 +778,9 @@ /* SUBTRACT HALFWORD */ C(0x4b00, SH, RX_a, Z, r1, m2_16s, new, r1_32, sub, subs32) C(0xe37b, SHY, RXY_a, LD, r1, m2_16s, new, r1_32, sub, subs32) +/* SUBTRACT HIGH */ + C(0xb9c9, SHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, sub, subs32) + C(0xb9d9, SHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, sub, subs32) /* SUBTRACT LOGICAL */ C(0x1f00, SLR, RR_a, Z, r1, r2, new, r1_32, sub, subu32) C(0xb9fb, SLRK, RRF_a, DO, r2, r3, new, r1_32, sub, subu32) @@ -725,6 +791,9 @@ C(0xb9eb, SLGRK, RRF_a, DO, r2, r3, r1, 0, sub, subu64) C(0xe30b, SLG, RXY_a, Z, r1, m2_64, r1, 0, sub, subu64) C(0xe31b, SLGF, RXY_a, Z, r1, m2_32u, r1, 0, sub, subu64) +/* SUBTRACT LOCICAL HIGH */ + C(0xb9cb, SLHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, sub, subu32) + C(0xb9db, SLHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, sub, subu32) /* SUBTRACT LOGICAL IMMEDIATE */ C(0xc205, SLFI, RIL_a, EI, r1, i2_32u, new, r1_32, sub, subu32) C(0xc204, SLGFI, RIL_a, EI, r1, i2_32u, r1, 0, sub, subu64) @@ -752,6 +821,10 @@ /* TRANSLATE */ C(0xdc00, TR, SS_a, Z, la1, a2, 0, 0, tr, 0) +/* TRANSLATE AND TEST */ + C(0xdd00, TRT, SS_a, Z, la1, a2, 0, 0, trt, 0) +/* TRANSLATE EXTENDED */ + C(0xb2a5, TRE, RRE, Z, 0, r2, r1_P, 0, tre, 0) /* UNPACK */ /* Really format SS_b, but we pack both lengths into one argument @@ -812,7 +885,7 @@ C(0xae00, SIGP, RS_a, Z, r3_o, a2, 0, 0, sigp, 0) /* STORE CLOCK */ C(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0) - C(0xb27c, STCKF, S, Z, la2, 0, new, m1_64, stck, 0) + C(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0) /* STORE CLOCK EXTENDED */ C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0) /* STORE CLOCK COMPARATOR */ diff --git a/target-s390x/int_helper.c b/target-s390x/int_helper.c index cb8dd98542..2c2b3f622c 100644 --- a/target-s390x/int_helper.c +++ b/target-s390x/int_helper.c @@ -115,48 +115,6 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, return ret; } -/* absolute value 32-bit */ -uint32_t HELPER(abs_i32)(int32_t val) -{ - if (val < 0) { - return -val; - } else { - return val; - } -} - -/* negative absolute value 32-bit */ -int32_t HELPER(nabs_i32)(int32_t val) -{ - if (val < 0) { - return val; - } else { - return -val; - } -} - -/* absolute value 64-bit */ -uint64_t HELPER(abs_i64)(int64_t val) -{ - HELPER_LOG("%s: val 0x%" PRIx64 "\n", __func__, val); - - if (val < 0) { - return -val; - } else { - return val; - } -} - -/* negative absolute value 64-bit */ -int64_t HELPER(nabs_i64)(int64_t val) -{ - if (val < 0) { - return val; - } else { - return -val; - } -} - /* count leading zeros, for find leftmost one */ uint64_t HELPER(clz)(uint64_t v) { diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 070f995c68..f6f61b9619 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -2217,3 +2217,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, route->u.adapter.adapter_id = pbdev->routes.adapter.adapter_id; return 0; } + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + abort(); +} diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c index 0e8cd0f489..b4e5d44011 100644 --- a/target-s390x/mem_helper.c +++ b/target-s390x/mem_helper.c @@ -213,21 +213,22 @@ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) if (dest == (src + 1)) { memset(g2h(dest), cpu_ldub_data(env, src), l + 1); return; - } else { + /* mvc and memmove do not behave the same when areas overlap! */ + } else if ((dest < src) || (src + l < dest)) { memmove(g2h(dest), g2h(src), l + 1); return; } #endif /* handle the parts that fit into 8-byte loads/stores */ - if (dest != (src + 1)) { + if ((dest + 8 <= src) || (src + 8 <= dest)) { for (i = 0; i < l_64; i++) { cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x)); x += 8; } } - /* slow version crossing pages with byte accesses */ + /* slow version with byte accesses which always work */ for (i = x; i <= l; i++) { cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); } @@ -509,6 +510,9 @@ uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1, case 0xc00: helper_tr(env, l, get_address(env, 0, b1, d1), get_address(env, 0, b2, d2)); + case 0xd00: + cc = helper_trt(env, l, get_address(env, 0, b1, d1), + get_address(env, 0, b2, d2)); break; default: goto abort; @@ -801,6 +805,66 @@ void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array, } } +uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array, + uint64_t len, uint64_t trans) +{ + uint8_t end = env->regs[0] & 0xff; + uint64_t l = len; + uint64_t i; + + if (!(env->psw.mask & PSW_MASK_64)) { + array &= 0x7fffffff; + l = (uint32_t)l; + } + + /* Lest we fail to service interrupts in a timely manner, limit the + amount of work we're willing to do. For now, let's cap at 8k. */ + if (l > 0x2000) { + l = 0x2000; + env->cc_op = 3; + } else { + env->cc_op = 0; + } + + for (i = 0; i < l; i++) { + uint8_t byte, new_byte; + + byte = cpu_ldub_data(env, array + i); + + if (byte == end) { + env->cc_op = 1; + break; + } + + new_byte = cpu_ldub_data(env, trans + byte); + cpu_stb_data(env, array + i, new_byte); + } + + env->retxl = len - i; + return array + i; +} + +uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array, + uint64_t trans) +{ + uint32_t cc = 0; + int i; + + for (i = 0; i <= len; i++) { + uint8_t byte = cpu_ldub_data(env, array + i); + uint8_t sbyte = cpu_ldub_data(env, trans + byte); + + if (sbyte != 0) { + env->regs[1] = array + i; + env->regs[2] = (env->regs[2] & ~0xff) | sbyte; + cc = (i == len) ? 2 : 1; + break; + } + } + + return cc; +} + #if !defined(CONFIG_USER_ONLY) void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) { @@ -952,59 +1016,46 @@ uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2) return cc; } -static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1, - uint64_t mode1, uint64_t a2, uint64_t mode2) +uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) { - CPUState *cs = CPU(s390_env_get_cpu(env)); - target_ulong src, dest; - int flags, cc = 0, i; + int cc = 0, i; - if (!l) { - return 0; - } else if (l > 256) { + HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", + __func__, l, a1, a2); + + if (l > 256) { /* max 256 */ l = 256; cc = 3; } - if (mmu_translate(env, a1, 1, mode1, &dest, &flags, true)) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } - dest |= a1 & ~TARGET_PAGE_MASK; - - if (mmu_translate(env, a2, 0, mode2, &src, &flags, true)) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } - src |= a2 & ~TARGET_PAGE_MASK; - /* XXX replace w/ memcpy */ for (i = 0; i < l; i++) { - /* XXX be more clever */ - if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) || - (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) { - mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2); - break; - } - stb_phys(cs->as, dest + i, ldub_phys(cs->as, src + i)); + cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i)); } return cc; } -uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) +uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) { + int cc = 0, i; + HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", __func__, l, a1, a2); - return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY); -} + if (l > 256) { + /* max 256 */ + l = 256; + cc = 3; + } -uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) -{ - HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", - __func__, l, a1, a2); + /* XXX replace w/ memcpy */ + for (i = 0; i < l; i++) { + cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i)); + } - return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY); + return cc; } /* invalidate pte */ diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index e1007fa35b..b375ab724b 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -61,7 +61,7 @@ void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, /* Advance past the insn. */ t = cpu_ldub_code(env, env->psw.addr); env->int_pgm_ilen = t = get_ilen(t); - env->psw.addr += 2 * t; + env->psw.addr += t; cpu_loop_exit(cs); } @@ -268,7 +268,8 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) tlb_flush_page(cs, TARGET_PAGE_SIZE); } -static inline uint64_t clock_value(CPUS390XState *env) +/* Store Clock */ +uint64_t HELPER(stck)(CPUS390XState *env) { uint64_t time; @@ -278,12 +279,6 @@ static inline uint64_t clock_value(CPUS390XState *env) return time; } -/* Store Clock */ -uint64_t HELPER(stck)(CPUS390XState *env) -{ - return clock_value(env); -} - /* Set Clock Comparator */ void HELPER(sckc)(CPUS390XState *env, uint64_t time) { @@ -291,19 +286,21 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) return; } - /* difference between now and then */ - time -= clock_value(env); + env->ckc = time; + + /* difference between origins */ + time -= env->tod_offset; + /* nanoseconds */ - time = (time * 125) >> 9; + time = tod2time(time); - timer_mod(env->tod_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time); + timer_mod(env->tod_timer, env->tod_basetime + time); } /* Store Clock Comparator */ uint64_t HELPER(stckc)(CPUS390XState *env) { - /* XXX implement */ - return 0; + return env->ckc; } /* Set CPU Timer */ @@ -314,16 +311,17 @@ void HELPER(spt)(CPUS390XState *env, uint64_t time) } /* nanoseconds */ - time = (time * 125) >> 9; + time = tod2time(time); + + env->cputm = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time; - timer_mod(env->cpu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time); + timer_mod(env->cpu_timer, env->cputm); } /* Store CPU Timer */ uint64_t HELPER(stpt)(CPUS390XState *env) { - /* XXX implement */ - return 0; + return time2tod(env->cputm - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); } /* Store System Information */ diff --git a/target-s390x/mmu_helper.c b/target-s390x/mmu_helper.c index e8dcd0c18f..815ff42dde 100644 --- a/target-s390x/mmu_helper.c +++ b/target-s390x/mmu_helper.c @@ -358,7 +358,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, /* Convert real address -> absolute address */ *raddr = mmu_real2abs(env, *raddr); - if (*raddr <= ram_size) { + if (*raddr < ram_size) { sk = &env->storage_keys[*raddr / TARGET_PAGE_SIZE]; if (*flags & PAGE_READ) { *sk |= SK_R; diff --git a/target-s390x/translate.c b/target-s390x/translate.c index fbffd3066d..9b877148c6 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -320,7 +320,21 @@ static void gen_program_exception(DisasContext *s, int code) static inline void gen_illegal_opcode(DisasContext *s) { - gen_program_exception(s, PGM_SPECIFICATION); + gen_program_exception(s, PGM_OPERATION); +} + +static inline void gen_trap(DisasContext *s) +{ + TCGv_i32 t; + + /* Set DXC to 0xff. */ + t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_gen_ori_i32(t, t, 0xff00); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_temp_free_i32(t); + + gen_program_exception(s, PGM_DATA); } #ifndef CONFIG_USER_ONLY @@ -1119,6 +1133,8 @@ typedef enum DisasFacility { FAC_HFP_MA, /* HFP multiply-and-add/subtract */ FAC_HW, /* high-word */ FAC_IEEEE_SIM, /* IEEE exception sumilation */ + FAC_MIE, /* miscellaneous-instruction-extensions */ + FAC_LAT, /* load-and-trap */ FAC_LOC, /* load/store on condition */ FAC_LD, /* long displacement */ FAC_PC, /* population count */ @@ -1310,7 +1326,13 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, static ExitStatus op_abs(DisasContext *s, DisasOps *o) { - gen_helper_abs_i64(o->out, o->in2); + TCGv_i64 z, n; + z = tcg_const_i64(0); + n = tcg_temp_new_i64(); + tcg_gen_neg_i64(n, o->in2); + tcg_gen_movcond_i64(TCG_COND_LT, o->out, o->in2, z, n, o->in2); + tcg_temp_free_i64(n); + tcg_temp_free_i64(z); return NO_EXIT; } @@ -1460,6 +1482,30 @@ static ExitStatus op_bct32(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } +static ExitStatus op_bcth(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int imm = get_field(s->fields, i2); + DisasCompare c; + TCGv_i64 t; + + c.cond = TCG_COND_NE; + c.is_64 = false; + c.g1 = false; + c.g2 = false; + + t = tcg_temp_new_i64(); + tcg_gen_shri_i64(t, regs[r1], 32); + tcg_gen_subi_i64(t, t, 1); + store_reg32h_i64(r1, t); + c.u.s32.a = tcg_temp_new_i32(); + c.u.s32.b = tcg_const_i32(0); + tcg_gen_trunc_i64_i32(c.u.s32.a, t); + tcg_temp_free_i64(t); + + return help_branch(s, &c, 1, imm, o->in2); +} + static ExitStatus op_bct64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); @@ -1961,7 +2007,6 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); TCGLabel *lab = gen_new_label(); - TCGv_i32 t; TCGCond c; c = tcg_invert_cond(ltgt_cond[m3]); @@ -1970,15 +2015,8 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o) } tcg_gen_brcond_i64(c, o->in1, o->in2, lab); - /* Set DXC to 0xff. */ - t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_gen_ori_i32(t, t, 0xff00); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_temp_free_i32(t); - /* Trap. */ - gen_program_exception(s, PGM_DATA); + gen_trap(s); gen_set_label(lab); return NO_EXIT; @@ -2101,13 +2139,37 @@ static ExitStatus op_ex(DisasContext *s, DisasOps *o) TCGv_i64 tmp; update_psw_addr(s); - update_cc_op(s); + gen_op_calc_cc(s); tmp = tcg_const_i64(s->next_pc); gen_helper_ex(cc_op, cpu_env, cc_op, o->in1, o->in2, tmp); tcg_temp_free_i64(tmp); - set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_fieb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_fieb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return NO_EXIT; +} + +static ExitStatus op_fidb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_fidb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return NO_EXIT; +} + +static ExitStatus op_fixb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m3); + return_low128(o->out2); + tcg_temp_free_i32(m3); return NO_EXIT; } @@ -2320,6 +2382,61 @@ static ExitStatus op_ld64(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_lat(DisasContext *s, DisasOps *o) +{ + TCGLabel *lab = gen_new_label(); + store_reg32_i64(get_field(s->fields, r1), o->in2); + /* The value is stored even in case of trap. */ + tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); + gen_trap(s); + gen_set_label(lab); + return NO_EXIT; +} + +static ExitStatus op_lgat(DisasContext *s, DisasOps *o) +{ + TCGLabel *lab = gen_new_label(); + tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); + /* The value is stored even in case of trap. */ + tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); + gen_trap(s); + gen_set_label(lab); + return NO_EXIT; +} + +static ExitStatus op_lfhat(DisasContext *s, DisasOps *o) +{ + TCGLabel *lab = gen_new_label(); + store_reg32h_i64(get_field(s->fields, r1), o->in2); + /* The value is stored even in case of trap. */ + tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); + gen_trap(s); + gen_set_label(lab); + return NO_EXIT; +} + +static ExitStatus op_llgfat(DisasContext *s, DisasOps *o) +{ + TCGLabel *lab = gen_new_label(); + tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); + /* The value is stored even in case of trap. */ + tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); + gen_trap(s); + gen_set_label(lab); + return NO_EXIT; +} + +static ExitStatus op_llgtat(DisasContext *s, DisasOps *o) +{ + TCGLabel *lab = gen_new_label(); + tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff); + /* The value is stored even in case of trap. */ + tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); + gen_trap(s); + gen_set_label(lab); + return NO_EXIT; +} + static ExitStatus op_loc(DisasContext *s, DisasOps *o) { DisasCompare c; @@ -2435,21 +2552,45 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); - TCGv_i64 t = tcg_temp_new_i64(); - TCGv_i64 t4 = tcg_const_i64(4); + TCGv_i64 t1, t2; - while (1) { - tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s)); - store_reg32_i64(r1, t); - if (r1 == r3) { - break; - } - tcg_gen_add_i64(o->in2, o->in2, t4); + /* Only one register to read. */ + t1 = tcg_temp_new_i64(); + if (unlikely(r1 == r3)) { + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32_i64(r1, t1); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* First load the values of the first and last registers to trigger + possible page faults. */ + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15)); + tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s)); + store_reg32_i64(r1, t1); + store_reg32_i64(r3, t2); + + /* Only two registers to read. */ + if (((r1 + 1) & 15) == r3) { + tcg_temp_free(t2); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* Then load the remaining registers. Page fault can't occur. */ + r3 = (r3 - 1) & 15; + tcg_gen_movi_i64(t2, 4); + while (r1 != r3) { r1 = (r1 + 1) & 15; + tcg_gen_add_i64(o->in2, o->in2, t2); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32_i64(r1, t1); } + tcg_temp_free(t2); + tcg_temp_free(t1); - tcg_temp_free_i64(t); - tcg_temp_free_i64(t4); return NO_EXIT; } @@ -2457,21 +2598,45 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); - TCGv_i64 t = tcg_temp_new_i64(); - TCGv_i64 t4 = tcg_const_i64(4); + TCGv_i64 t1, t2; - while (1) { - tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s)); - store_reg32h_i64(r1, t); - if (r1 == r3) { - break; - } - tcg_gen_add_i64(o->in2, o->in2, t4); + /* Only one register to read. */ + t1 = tcg_temp_new_i64(); + if (unlikely(r1 == r3)) { + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32h_i64(r1, t1); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* First load the values of the first and last registers to trigger + possible page faults. */ + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15)); + tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s)); + store_reg32h_i64(r1, t1); + store_reg32h_i64(r3, t2); + + /* Only two registers to read. */ + if (((r1 + 1) & 15) == r3) { + tcg_temp_free(t2); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* Then load the remaining registers. Page fault can't occur. */ + r3 = (r3 - 1) & 15; + tcg_gen_movi_i64(t2, 4); + while (r1 != r3) { r1 = (r1 + 1) & 15; + tcg_gen_add_i64(o->in2, o->in2, t2); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32h_i64(r1, t1); } + tcg_temp_free(t2); + tcg_temp_free(t1); - tcg_temp_free_i64(t); - tcg_temp_free_i64(t4); return NO_EXIT; } @@ -2479,18 +2644,40 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); - TCGv_i64 t8 = tcg_const_i64(8); + TCGv_i64 t1, t2; - while (1) { + /* Only one register to read. */ + if (unlikely(r1 == r3)) { tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); - if (r1 == r3) { - break; - } - tcg_gen_add_i64(o->in2, o->in2, t8); + return NO_EXIT; + } + + /* First load the values of the first and last registers to trigger + possible page faults. */ + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(t2, o->in2, 8 * ((r3 - r1) & 15)); + tcg_gen_qemu_ld64(regs[r3], t2, get_mem_index(s)); + tcg_gen_mov_i64(regs[r1], t1); + tcg_temp_free(t2); + + /* Only two registers to read. */ + if (((r1 + 1) & 15) == r3) { + tcg_temp_free(t1); + return NO_EXIT; + } + + /* Then load the remaining registers. Page fault can't occur. */ + r3 = (r3 - 1) & 15; + tcg_gen_movi_i64(t1, 8); + while (r1 != r3) { r1 = (r1 + 1) & 15; + tcg_gen_add_i64(o->in2, o->in2, t1); + tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); } + tcg_temp_free(t1); - tcg_temp_free_i64(t8); return NO_EXIT; } @@ -2521,6 +2708,41 @@ static ExitStatus op_mov2(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_mov2e(DisasContext *s, DisasOps *o) +{ + int b2 = get_field(s->fields, b2); + TCGv ar1 = tcg_temp_new_i64(); + + o->out = o->in2; + o->g_out = o->g_in2; + TCGV_UNUSED_I64(o->in2); + o->g_in2 = false; + + switch (s->tb->flags & FLAG_MASK_ASC) { + case PSW_ASC_PRIMARY >> 32: + tcg_gen_movi_i64(ar1, 0); + break; + case PSW_ASC_ACCREG >> 32: + tcg_gen_movi_i64(ar1, 1); + break; + case PSW_ASC_SECONDARY >> 32: + if (b2) { + tcg_gen_ld32u_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[b2])); + } else { + tcg_gen_movi_i64(ar1, 0); + } + break; + case PSW_ASC_HOME >> 32: + tcg_gen_movi_i64(ar1, 2); + break; + } + + tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1])); + tcg_temp_free_i64(ar1); + + return NO_EXIT; +} + static ExitStatus op_movx(DisasContext *s, DisasOps *o) { o->out = o->in1; @@ -2681,7 +2903,13 @@ static ExitStatus op_msdb(DisasContext *s, DisasOps *o) static ExitStatus op_nabs(DisasContext *s, DisasOps *o) { - gen_helper_nabs_i64(o->out, o->in2); + TCGv_i64 z, n; + z = tcg_const_i64(0); + n = tcg_temp_new_i64(); + tcg_gen_neg_i64(n, o->in2); + tcg_gen_movcond_i64(TCG_COND_GE, o->out, o->in2, z, n, o->in2); + tcg_temp_free_i64(n); + tcg_temp_free_i64(z); return NO_EXIT; } @@ -3646,6 +3874,25 @@ static ExitStatus op_tr(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_tre(DisasContext *s, DisasOps *o) +{ + potential_page_fault(s); + gen_helper_tre(o->out, cpu_env, o->out, o->out2, o->in2); + return_low128(o->out2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_trt(DisasContext *s, DisasOps *o) +{ + TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + potential_page_fault(s); + gen_helper_trt(cc_op, cpu_env, l, o->addr1, o->in2); + tcg_temp_free_i32(l); + set_cc_static(s); + return NO_EXIT; +} + static ExitStatus op_unpk(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); @@ -3989,6 +4236,12 @@ static void wout_r1_32(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_wout_r1_32 0 +static void wout_r1_32h(DisasContext *s, DisasFields *f, DisasOps *o) +{ + store_reg32h_i64(get_field(f, r1), o->out); +} +#define SPEC_wout_r1_32h 0 + static void wout_r1_P32(DisasContext *s, DisasFields *f, DisasOps *o) { int r1 = get_field(f, r1); @@ -4159,6 +4412,13 @@ static void in1_r2(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in1_r2 0 +static void in1_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_shri_i64(o->in1, regs[get_field(f, r2)], 32); +} +#define SPEC_in1_r2_sr32 0 + static void in1_r3(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = load_reg(get_field(f, r3)); @@ -4372,6 +4632,13 @@ static void in2_r3(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_r3 0 +static void in2_r3_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(o->in2, regs[get_field(f, r3)], 32); +} +#define SPEC_in2_r3_sr32 0 + static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); @@ -4386,6 +4653,13 @@ static void in2_r2_32u(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_r2_32u 0 +static void in2_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(o->in2, regs[get_field(f, r2)], 32); +} +#define SPEC_in2_r2_sr32 0 + static void in2_e2(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = load_freg32_i64(get_field(f, r2)); diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 60c7493ac1..8aec04d2bf 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -14,6 +14,7 @@ #define TCG_TARGET_AARCH64 1 #define TCG_TARGET_INSN_UNIT_SIZE 4 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 24 #undef TCG_TARGET_STACK_GROWSUP typedef enum { diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 1c719e2862..6559f80b71 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -27,6 +27,7 @@ #undef TCG_TARGET_STACK_GROWSUP #define TCG_TARGET_INSN_UNIT_SIZE 4 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16 typedef enum { TCG_REG_R0 = 0, diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 7a9980e70e..25b513354c 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -25,6 +25,7 @@ #define TCG_TARGET_I386 1 #define TCG_TARGET_INSN_UNIT_SIZE 1 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 31 #ifdef __x86_64__ # define TCG_TARGET_REG_BITS 64 diff --git a/tcg/ia64/tcg-target.h b/tcg/ia64/tcg-target.h index d67558988a..a04ed81262 100644 --- a/tcg/ia64/tcg-target.h +++ b/tcg/ia64/tcg-target.h @@ -26,6 +26,8 @@ #define TCG_TARGET_IA64 1 #define TCG_TARGET_INSN_UNIT_SIZE 16 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 21 + typedef struct { uint64_t lo __attribute__((aligned(16))); uint64_t hi; diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index c88a1c9272..f5ba52cacf 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -27,6 +27,7 @@ #define TCG_TARGET_MIPS 1 #define TCG_TARGET_INSN_UNIT_SIZE 4 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16 #define TCG_TARGET_NB_REGS 32 typedef enum { diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 32ac4424db..7ce7048824 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -32,6 +32,7 @@ #define TCG_TARGET_NB_REGS 32 #define TCG_TARGET_INSN_UNIT_SIZE 4 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16 typedef enum { TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3, diff --git a/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h index 5acc28ca6b..91576d5949 100644 --- a/tcg/s390/tcg-target.h +++ b/tcg/s390/tcg-target.h @@ -25,6 +25,7 @@ #define TCG_TARGET_S390 1 #define TCG_TARGET_INSN_UNIT_SIZE 2 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 19 typedef enum TCGReg { TCG_REG_R0 = 0, diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h index 0c4c8af0b2..f584de4766 100644 --- a/tcg/sparc/tcg-target.h +++ b/tcg/sparc/tcg-target.h @@ -27,6 +27,7 @@ #define TCG_TARGET_REG_BITS 64 #define TCG_TARGET_INSN_UNIT_SIZE 4 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32 #define TCG_TARGET_NB_REGS 32 typedef enum { @@ -927,7 +927,9 @@ static inline unsigned get_mmuidx(TCGMemOpIdx oi) #define TB_EXIT_ICOUNT_EXPIRED 2 #define TB_EXIT_REQUESTED 3 -#if !defined(tcg_qemu_tb_exec) +#ifdef HAVE_TCG_QEMU_TB_EXEC +uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr); +#else # define tcg_qemu_tb_exec(env, tb_ptr) \ ((uintptr_t (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, tb_ptr) #endif diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index bd1e97468c..cbf3f9b5a6 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -44,6 +44,7 @@ #define TCG_TARGET_INTERPRETER 1 #define TCG_TARGET_INSN_UNIT_SIZE 1 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32 #if UINTPTR_MAX == UINT32_MAX # define TCG_TARGET_REG_BITS 32 @@ -175,8 +176,7 @@ typedef enum { void tci_disas(uint8_t opc); -uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr); -#define tcg_qemu_tb_exec tcg_qemu_tb_exec +#define HAVE_TCG_QEMU_TB_EXEC static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/tests/Makefile b/tests/Makefile index 729b9694cf..c5e474455c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -174,6 +174,8 @@ gcov-files-i386-y += hw/usb/dev-storage.c check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF) gcov-files-i386-y += hw/usb/hcd-xhci.c check-qtest-i386-y += tests/pc-cpu-test$(EXESUF) +check-qtest-i386-y += tests/q35-test$(EXESUF) +gcov-files-i386-y += hw/pci-host/q35.c check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c @@ -355,6 +357,7 @@ tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) +tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) tests/e1000-test$(EXESUF): tests/e1000-test.o tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) diff --git a/tests/fdc-test.c b/tests/fdc-test.c index 3c6c83cac4..416394fc77 100644 --- a/tests/fdc-test.c +++ b/tests/fdc-test.c @@ -218,6 +218,10 @@ static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0) inb(FLOPPY_BASE + reg_fifo); } + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, BUSY | RQM | DIO); + g_assert(get_irq(FLOPPY_IRQ)); + st0 = floppy_recv(); if (st0 != expected_st0) { ret = 1; @@ -228,8 +232,15 @@ static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0) floppy_recv(); floppy_recv(); floppy_recv(); + g_assert(get_irq(FLOPPY_IRQ)); floppy_recv(); + /* Check that we're back in command phase */ + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_clear(msr, BUSY | DIO); + assert_bit_set(msr, RQM); + g_assert(!get_irq(FLOPPY_IRQ)); + return ret; } @@ -403,6 +414,7 @@ static void test_read_id(void) uint8_t head = 0; uint8_t cyl; uint8_t st0; + uint8_t msr; /* Seek to track 0 and check with READ ID */ send_seek(0); @@ -411,18 +423,29 @@ static void test_read_id(void) g_assert(!get_irq(FLOPPY_IRQ)); floppy_send(head << 2 | drive); + msr = inb(FLOPPY_BASE + reg_msr); + if (!get_irq(FLOPPY_IRQ)) { + assert_bit_set(msr, BUSY); + assert_bit_clear(msr, RQM); + } + while (!get_irq(FLOPPY_IRQ)) { /* qemu involves a timer with READ ID... */ clock_step(1000000000LL / 50); } + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, BUSY | RQM | DIO); + st0 = floppy_recv(); floppy_recv(); floppy_recv(); cyl = floppy_recv(); head = floppy_recv(); floppy_recv(); + g_assert(get_irq(FLOPPY_IRQ)); floppy_recv(); + g_assert(!get_irq(FLOPPY_IRQ)); g_assert_cmpint(cyl, ==, 0); g_assert_cmpint(head, ==, 0); @@ -443,18 +466,29 @@ static void test_read_id(void) g_assert(!get_irq(FLOPPY_IRQ)); floppy_send(head << 2 | drive); + msr = inb(FLOPPY_BASE + reg_msr); + if (!get_irq(FLOPPY_IRQ)) { + assert_bit_set(msr, BUSY); + assert_bit_clear(msr, RQM); + } + while (!get_irq(FLOPPY_IRQ)) { /* qemu involves a timer with READ ID... */ clock_step(1000000000LL / 50); } + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, BUSY | RQM | DIO); + st0 = floppy_recv(); floppy_recv(); floppy_recv(); cyl = floppy_recv(); head = floppy_recv(); floppy_recv(); + g_assert(get_irq(FLOPPY_IRQ)); floppy_recv(); + g_assert(!get_irq(FLOPPY_IRQ)); g_assert_cmpint(cyl, ==, 8); g_assert_cmpint(head, ==, 1); diff --git a/tests/q35-test.c b/tests/q35-test.c new file mode 100644 index 0000000000..812abe5480 --- /dev/null +++ b/tests/q35-test.c @@ -0,0 +1,91 @@ +/* + * QTest testcase for Q35 northbridge + * + * Copyright (c) 2015 Red Hat, Inc. + * + * Author: Gerd Hoffmann <kraxel@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <string.h> +#include "libqtest.h" +#include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "qemu/osdep.h" +#include "hw/pci-host/q35.h" + +static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled) +{ + uint8_t smram; + + smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); + if (enabled) { + smram |= mask; + } else { + smram &= ~mask; + } + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram); +} + +static bool smram_test_bit(QPCIDevice *pcidev, uint8_t mask) +{ + uint8_t smram; + + smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); + return smram & mask; +} + +static void test_smram_lock(void) +{ + QPCIBus *pcibus; + QPCIDevice *pcidev; + QDict *response; + + pcibus = qpci_init_pc(); + g_assert(pcibus != NULL); + + pcidev = qpci_device_find(pcibus, 0); + g_assert(pcidev != NULL); + + /* check open is settable */ + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); + + /* lock, check open is cleared & not settable */ + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_LCK, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + + /* reset */ + response = qmp("{'execute': 'system_reset', 'arguments': {} }"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + /* check open is settable again */ + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/q35/smram/lock", test_smram_lock); + + qtest_start("-M q35"); + ret = g_test_run(); + qtest_end(); + + return ret; +} @@ -182,7 +182,7 @@ static int configure_tpm(QemuOpts *opts) return 0; } -static int tpm_init_tpmdev(QemuOpts *opts, void *dummy) +static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp) { return configure_tpm(opts); } @@ -208,12 +208,11 @@ void tpm_cleanup(void) int tpm_init(void) { if (qemu_opts_foreach(qemu_find_opts("tpmdev"), - tpm_init_tpmdev, NULL, 1) != 0) { + tpm_init_tpmdev, NULL, NULL)) { return -1; } atexit(tpm_cleanup); - return 0; } diff --git a/trace-events b/trace-events index 3bb1f042c9..a589650597 100644 --- a/trace-events +++ b/trace-events @@ -1338,6 +1338,10 @@ spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liob spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 spapr_iommu_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64 spapr_iommu_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64 +spapr_iommu_pci_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_pci_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 +spapr_iommu_pci_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_pci_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64 spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, unsigned pgsize) "liobn=%"PRIx64" 0x%"PRIx64" -> 0x%"PRIx64" perm=%u mask=%x" spapr_iommu_new_table(uint64_t liobn, void *tcet, void *table, int fd) "liobn=%"PRIx64" tcet=%p table=%p fd=%d" diff --git a/translate-all.c b/translate-all.c index 536008f52d..e2e7422609 100644 --- a/translate-all.c +++ b/translate-all.c @@ -1042,11 +1042,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * access: the virtual CPU will exit the current TB if code is modified inside * this TB. */ -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access) +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) { while (start < end) { - tb_invalidate_phys_page_range(start, end, is_cpu_write_access); + tb_invalidate_phys_page_range(start, end, 0); start &= TARGET_PAGE_MASK; start += TARGET_PAGE_SIZE; } @@ -1083,12 +1082,6 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, if (!p) { return; } - if (!p->code_bitmap && - ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD && - is_cpu_write_access) { - /* build code bitmap */ - build_page_bitmap(p); - } #if defined(TARGET_HAS_PRECISE_SMC) if (cpu != NULL) { env = cpu->env_ptr; @@ -1158,9 +1151,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, /* if no code remaining, no need to continue to use slow writes */ if (!p->first_tb) { invalidate_page_bitmap(p); - if (is_cpu_write_access) { - tlb_unprotect_code_phys(cpu, start, cpu->mem_io_vaddr); - } + tlb_unprotect_code(start); } #endif #ifdef TARGET_HAS_PRECISE_SMC @@ -1193,6 +1184,11 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) if (!p) { return; } + if (!p->code_bitmap && + ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) { + /* build code bitmap */ + build_page_bitmap(p); + } if (p->code_bitmap) { unsigned int nr; unsigned long b; diff --git a/translate-all.h b/translate-all.h index 02832b2718..038464005f 100644 --- a/translate-all.h +++ b/translate-all.h @@ -21,7 +21,13 @@ /* translate-all.c */ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); -void cpu_unlink_tb(CPUState *cpu); +void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, + int is_cpu_write_access); +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); void tb_check_watchpoint(CPUState *cpu); +#ifdef CONFIG_USER_ONLY +int page_unprotect(target_ulong address, uintptr_t pc, void *puc); +#endif + #endif /* TRANSLATE_ALL_H */ diff --git a/ui/console.c b/ui/console.c index 406c36b864..75fc492f73 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1619,67 +1619,6 @@ bool dpy_cursor_define_supported(QemuConsole *con) return false; } -/* - * Call dpy_gfx_update for all dirity scanlines. Works for - * DisplaySurfaces backed by guest memory (i.e. the ones created - * using qemu_create_displaysurface_guestmem). - */ -void dpy_gfx_update_dirty(QemuConsole *con, - MemoryRegion *address_space, - hwaddr base, - bool invalidate) -{ - DisplaySurface *ds = qemu_console_surface(con); - int width = surface_stride(ds); - int height = surface_height(ds); - hwaddr size = width * height; - MemoryRegionSection mem_section; - MemoryRegion *mem; - ram_addr_t addr; - int first, last, i; - bool dirty; - - mem_section = memory_region_find(address_space, base, size); - mem = mem_section.mr; - if (int128_get64(mem_section.size) != size || - !memory_region_is_ram(mem_section.mr)) { - goto out; - } - assert(mem); - - memory_region_sync_dirty_bitmap(mem); - addr = mem_section.offset_within_region; - - first = -1; - last = -1; - for (i = 0; i < height; i++, addr += width) { - dirty = invalidate || - memory_region_get_dirty(mem, addr, width, DIRTY_MEMORY_VGA); - if (dirty) { - if (first == -1) { - first = i; - } - last = i; - } - if (first != -1 && !dirty) { - assert(last != -1 && last >= first); - dpy_gfx_update(con, 0, first, surface_width(ds), - last - first + 1); - first = -1; - } - } - if (first != -1) { - assert(last != -1 && last >= first); - dpy_gfx_update(con, 0, first, surface_width(ds), - last - first + 1); - } - - memory_region_reset_dirty(mem, mem_section.offset_within_region, size, - DIRTY_MEMORY_VGA); -out: - memory_region_unref(mem); -} - /***********************************************************/ /* register display */ diff --git a/ui/spice-core.c b/ui/spice-core.c index 2e8384e653..a30da3cf9f 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -583,7 +583,8 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, return ret; } -static int add_channel(const char *name, const char *value, void *opaque) +static int add_channel(void *opaque, const char *name, const char *value, + Error **errp) { int security = 0; int rc; @@ -782,7 +783,7 @@ void qemu_spice_init(void) spice_server_set_playback_compression (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1)); - qemu_opt_foreach(opts, add_channel, &tls_port, 0); + qemu_opt_foreach(opts, add_channel, &tls_port, NULL); spice_server_set_name(spice_server, qemu_name); spice_server_set_uuid(spice_server, qemu_uuid); @@ -3770,7 +3770,7 @@ QemuOpts *vnc_parse_func(const char *str) return opts; } -int vnc_init_func(QemuOpts *opts, void *opaque) +int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) { Error *local_err = NULL; char *id = (char *)qemu_opts_id(opts); diff --git a/user-exec.c b/user-exec.c index 8f57e8acb8..ed9a07f159 100644 --- a/user-exec.c +++ b/user-exec.c @@ -22,6 +22,7 @@ #include "tcg.h" #include "qemu/bitops.h" #include "exec/cpu_ldst.h" +#include "translate-all.h" #undef EAX #undef ECX diff --git a/util/bitmap.c b/util/bitmap.c index 9c6bb526f6..300a68e38c 100644 --- a/util/bitmap.c +++ b/util/bitmap.c @@ -11,6 +11,7 @@ #include "qemu/bitops.h" #include "qemu/bitmap.h" +#include "qemu/atomic.h" /* * bitmaps provide an array of bits, implemented using an an @@ -177,6 +178,43 @@ void bitmap_set(unsigned long *map, long start, long nr) } } +void bitmap_set_atomic(unsigned long *map, long start, long nr) +{ + unsigned long *p = map + BIT_WORD(start); + const long size = start + nr; + int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); + + /* First word */ + if (nr - bits_to_set > 0) { + atomic_or(p, mask_to_set); + nr -= bits_to_set; + bits_to_set = BITS_PER_LONG; + mask_to_set = ~0UL; + p++; + } + + /* Full words */ + if (bits_to_set == BITS_PER_LONG) { + while (nr >= BITS_PER_LONG) { + *p = ~0UL; + nr -= BITS_PER_LONG; + p++; + } + } + + /* Last word */ + if (nr) { + mask_to_set &= BITMAP_LAST_WORD_MASK(size); + atomic_or(p, mask_to_set); + } else { + /* If we avoided the full barrier in atomic_or(), issue a + * barrier to account for the assignments in the while loop. + */ + smp_mb(); + } +} + void bitmap_clear(unsigned long *map, long start, long nr) { unsigned long *p = map + BIT_WORD(start); @@ -197,6 +235,51 @@ void bitmap_clear(unsigned long *map, long start, long nr) } } +bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr) +{ + unsigned long *p = map + BIT_WORD(start); + const long size = start + nr; + int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); + unsigned long dirty = 0; + unsigned long old_bits; + + /* First word */ + if (nr - bits_to_clear > 0) { + old_bits = atomic_fetch_and(p, ~mask_to_clear); + dirty |= old_bits & mask_to_clear; + nr -= bits_to_clear; + bits_to_clear = BITS_PER_LONG; + mask_to_clear = ~0UL; + p++; + } + + /* Full words */ + if (bits_to_clear == BITS_PER_LONG) { + while (nr >= BITS_PER_LONG) { + if (*p) { + old_bits = atomic_xchg(p, 0); + dirty |= old_bits; + } + nr -= BITS_PER_LONG; + p++; + } + } + + /* Last word */ + if (nr) { + mask_to_clear &= BITMAP_LAST_WORD_MASK(size); + old_bits = atomic_fetch_and(p, ~mask_to_clear); + dirty |= old_bits & mask_to_clear; + } else { + if (!dirty) { + smp_mb(); + } + } + + return dirty != 0; +} + #define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) /** diff --git a/util/qemu-config.c b/util/qemu-config.c index 30d6dcf526..35adfda496 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -335,7 +335,8 @@ struct ConfigWriteData { FILE *fp; }; -static int config_write_opt(const char *name, const char *value, void *opaque) +static int config_write_opt(void *opaque, const char *name, const char *value, + Error **errp) { struct ConfigWriteData *data = opaque; @@ -343,7 +344,7 @@ static int config_write_opt(const char *name, const char *value, void *opaque) return 0; } -static int config_write_opts(QemuOpts *opts, void *opaque) +static int config_write_opts(void *opaque, QemuOpts *opts, Error **errp) { struct ConfigWriteData *data = opaque; const char *id = qemu_opts_id(opts); @@ -353,7 +354,7 @@ static int config_write_opts(QemuOpts *opts, void *opaque) } else { fprintf(data->fp, "[%s]\n", data->list->name); } - qemu_opt_foreach(opts, config_write_opt, data, 0); + qemu_opt_foreach(opts, config_write_opt, data, NULL); fprintf(data->fp, "\n"); return 0; } @@ -367,7 +368,7 @@ void qemu_config_write(FILE *fp) fprintf(fp, "# qemu config file\n\n"); for (i = 0; lists[i] != NULL; i++) { data.list = lists[i]; - qemu_opts_foreach(data.list, config_write_opts, &data, 0); + qemu_opts_foreach(data.list, config_write_opts, &data, NULL); } } diff --git a/util/qemu-option.c b/util/qemu-option.c index fda4e5fcbf..840f5f7a5b 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -596,18 +596,26 @@ void qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, QTAILQ_INSERT_TAIL(&opts->head, opt, next); } +/** + * For each member of @opts, call @func(@opaque, name, value, @errp). + * @func() may store an Error through @errp, but must return non-zero then. + * When @func() returns non-zero, break the loop and return that value. + * Return zero when the loop completes. + */ int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, - int abort_on_failure) + Error **errp) { QemuOpt *opt; - int rc = 0; + int rc; QTAILQ_FOREACH(opt, &opts->head, next) { - rc = func(opt->name, opt->str, opaque); - if (abort_on_failure && rc != 0) - break; + rc = func(opaque, opt->name, opt->str, errp); + if (rc) { + return rc; + } + assert(!errp || !*errp); } - return rc; + return 0; } QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id) @@ -1046,22 +1054,31 @@ void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) } } -int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, - int abort_on_failure) +/** + * For each member of @list, call @func(@opaque, member, @errp). + * Call it with the current location temporarily set to the member's. + * @func() may store an Error through @errp, but must return non-zero then. + * When @func() returns non-zero, break the loop and return that value. + * Return zero when the loop completes. + */ +int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, + void *opaque, Error **errp) { Location loc; QemuOpts *opts; - int rc = 0; + int rc; loc_push_none(&loc); QTAILQ_FOREACH(opts, &list->head, next) { loc_restore(&opts->loc); - rc |= func(opts, opaque); - if (abort_on_failure && rc != 0) - break; + rc = func(opaque, opts, errp); + if (rc) { + return rc; + } + assert(!errp || !*errp); } loc_pop(&loc); - return rc; + return 0; } static size_t count_opts_list(QemuOptsList *list) diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 22c8c4c5d5..f9ad34e40c 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -45,6 +45,12 @@ QemuOptsList socket_optslist = { .name = "port", .type = QEMU_OPT_STRING, },{ + .name = "localaddr", + .type = QEMU_OPT_STRING, + },{ + .name = "localport", + .type = QEMU_OPT_STRING, + },{ .name = "to", .type = QEMU_OPT_NUMBER, },{ @@ -120,8 +120,6 @@ int main(int argc, char **argv) #include "qom/object_interfaces.h" #include "qapi-event.h" -#define DEFAULT_RAM_SIZE 128 - #define MAX_VIRTIO_CONSOLES 1 #define MAX_SCLP_CONSOLES 1 @@ -470,6 +468,9 @@ static QemuOptsList qemu_icount_opts = { }, { .name = "align", .type = QEMU_OPT_BOOL, + }, { + .name = "sleep", + .type = QEMU_OPT_BOOL, }, { /* end of list */ } }, @@ -514,7 +515,7 @@ static void res_free(void) } } -static int default_driver_check(QemuOpts *opts, void *opaque) +static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) { const char *driver = qemu_opt_get(opts, "driver"); int i; @@ -960,7 +961,7 @@ static int bt_parse(const char *opt) return 1; } -static int parse_sandbox(QemuOpts *opts, void *opaque) +static int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) { /* FIXME: change this to true for 1.3 */ if (qemu_opt_get_bool(opts, "enable", false)) { @@ -980,7 +981,7 @@ static int parse_sandbox(QemuOpts *opts, void *opaque) return 0; } -static int parse_name(QemuOpts *opts, void *opaque) +static int parse_name(void *opaque, QemuOpts *opts, Error **errp) { const char *proc_name; @@ -1008,7 +1009,7 @@ bool usb_enabled(void) } #ifndef _WIN32 -static int parse_add_fd(QemuOpts *opts, void *opaque) +static int parse_add_fd(void *opaque, QemuOpts *opts, Error **errp) { int fd, dupfd, flags; int64_t fdset_id; @@ -1070,7 +1071,7 @@ static int parse_add_fd(QemuOpts *opts, void *opaque) return 0; } -static int cleanup_add_fd(QemuOpts *opts, void *opaque) +static int cleanup_add_fd(void *opaque, QemuOpts *opts, Error **errp) { int fd; @@ -1091,14 +1092,14 @@ static int cleanup_add_fd(QemuOpts *opts, void *opaque) #define MTD_OPTS "" #define SD_OPTS "" -static int drive_init_func(QemuOpts *opts, void *opaque) +static int drive_init_func(void *opaque, QemuOpts *opts, Error **errp) { BlockInterfaceType *block_default_type = opaque; return drive_new(opts, *block_default_type) == NULL; } -static int drive_enable_snapshot(QemuOpts *opts, void *opaque) +static int drive_enable_snapshot(void *opaque, QemuOpts *opts, Error **errp) { if (qemu_opt_get(opts, "snapshot") == NULL) { qemu_opt_set(opts, "snapshot", "on", &error_abort); @@ -1118,7 +1119,7 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, opts = drive_add(type, index, NULL, optstr); if (snapshot) { - drive_enable_snapshot(opts, NULL); + drive_enable_snapshot(NULL, opts, NULL); } dinfo = drive_new(opts, type); @@ -1310,7 +1311,11 @@ void hmp_usb_del(Monitor *mon, const QDict *qdict) MachineState *current_machine; -static void machine_class_init(ObjectClass *oc, void *data) +/* + * Transitional class registration/init used for converting from + * legacy QEMUMachine to MachineClass. + */ +static void qemu_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); QEMUMachine *qm = data; @@ -1333,7 +1338,7 @@ int qemu_register_machine(QEMUMachine *m) TypeInfo ti = { .name = name, .parent = TYPE_MACHINE, - .class_init = machine_class_init, + .class_init = qemu_machine_class_init, .class_data = (void *)m, }; @@ -2122,12 +2127,12 @@ char *qemu_find_file(int type, const char *name) return NULL; } -static int device_help_func(QemuOpts *opts, void *opaque) +static int device_help_func(void *opaque, QemuOpts *opts, Error **errp) { return qdev_device_help(opts); } -static int device_init_func(QemuOpts *opts, void *opaque) +static int device_init_func(void *opaque, QemuOpts *opts, Error **errp) { DeviceState *dev; @@ -2138,7 +2143,7 @@ static int device_init_func(QemuOpts *opts, void *opaque) return 0; } -static int chardev_init_func(QemuOpts *opts, void *opaque) +static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp) { Error *local_err = NULL; @@ -2151,7 +2156,7 @@ static int chardev_init_func(QemuOpts *opts, void *opaque) } #ifdef CONFIG_VIRTFS -static int fsdev_init_func(QemuOpts *opts, void *opaque) +static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp) { int ret; ret = qemu_fsdev_add(opts); @@ -2160,7 +2165,7 @@ static int fsdev_init_func(QemuOpts *opts, void *opaque) } #endif -static int mon_init_func(QemuOpts *opts, void *opaque) +static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) { CharDriverState *chr; const char *chardev; @@ -2495,14 +2500,20 @@ static void qemu_run_exit_notifiers(void) notifier_list_notify(&exit_notifiers, NULL); } +static bool machine_init_done; + void qemu_add_machine_init_done_notifier(Notifier *notify) { notifier_list_add(&machine_init_done_notifiers, notify); + if (machine_init_done) { + notify->notify(notify, NULL); + } } static void qemu_run_machine_init_done_notifiers(void) { notifier_list_notify(&machine_init_done_notifiers, NULL); + machine_init_done = true; } static const QEMUOption *lookup_opt(int argc, char **argv, @@ -2565,8 +2576,9 @@ static void free_and_trace(gpointer mem) free(mem); } -static int machine_set_property(const char *name, const char *value, - void *opaque) +static int machine_set_property(void *opaque, + const char *name, const char *value, + Error **errp) { Object *obj = OBJECT(opaque); Error *local_err = NULL; @@ -2595,7 +2607,7 @@ static int machine_set_property(const char *name, const char *value, return 0; } -static int object_create(QemuOpts *opts, void *opaque) +static int object_create(void *opaque, QemuOpts *opts, Error **errp) { Error *err = NULL; char *type = NULL; @@ -2647,13 +2659,13 @@ out: return 0; } -static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size) +static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, + MachineClass *mc) { uint64_t sz; const char *mem_str; const char *maxmem_str, *slots_str; - const ram_addr_t default_ram_size = (ram_addr_t)DEFAULT_RAM_SIZE * - 1024 * 1024; + const ram_addr_t default_ram_size = mc->default_ram_size; QemuOpts *opts = qemu_find_opts_singleton("memory"); sz = 0; @@ -3769,7 +3781,13 @@ int main(int argc, char **argv, char **envp) machine_class = machine_parse(optarg); } - set_memory_options(&ram_slots, &maxram_size); + if (machine_class == NULL) { + fprintf(stderr, "No machine specified, and there is no default.\n" + "Use -machine help to list supported machines!\n"); + exit(1); + } + + set_memory_options(&ram_slots, &maxram_size, machine_class); loc_set_none(); @@ -3780,30 +3798,28 @@ int main(int argc, char **argv, char **envp) exit(1); } - if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, 0)) { + if (qemu_opts_foreach(qemu_find_opts("sandbox"), + parse_sandbox, NULL, NULL)) { exit(1); } - if (qemu_opts_foreach(qemu_find_opts("name"), parse_name, NULL, 1)) { + if (qemu_opts_foreach(qemu_find_opts("name"), + parse_name, NULL, NULL)) { exit(1); } #ifndef _WIN32 - if (qemu_opts_foreach(qemu_find_opts("add-fd"), parse_add_fd, NULL, 1)) { + if (qemu_opts_foreach(qemu_find_opts("add-fd"), + parse_add_fd, NULL, NULL)) { exit(1); } - if (qemu_opts_foreach(qemu_find_opts("add-fd"), cleanup_add_fd, NULL, 1)) { + if (qemu_opts_foreach(qemu_find_opts("add-fd"), + cleanup_add_fd, NULL, NULL)) { exit(1); } #endif - if (machine_class == NULL) { - fprintf(stderr, "No machine specified, and there is no default.\n" - "Use -machine help to list supported machines!\n"); - exit(1); - } - current_machine = MACHINE(object_new(object_class_get_name( OBJECT_CLASS(machine_class)))); if (machine_help_func(qemu_get_machine_opts(), current_machine)) { @@ -3886,8 +3902,10 @@ int main(int argc, char **argv, char **envp) machine_class->default_machine_opts, 0); } - qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, 0); - qemu_opts_foreach(qemu_find_opts("global"), default_driver_check, NULL, 0); + qemu_opts_foreach(qemu_find_opts("device"), + default_driver_check, NULL, NULL); + qemu_opts_foreach(qemu_find_opts("global"), + default_driver_check, NULL, NULL); if (!vga_model && !default_vga) { vga_interface_type = VGA_DEVICE; @@ -4025,10 +4043,14 @@ int main(int argc, char **argv, char **envp) socket_init(); - if (qemu_opts_foreach(qemu_find_opts("chardev"), chardev_init_func, NULL, 1) != 0) + if (qemu_opts_foreach(qemu_find_opts("chardev"), + chardev_init_func, NULL, NULL)) { exit(1); + } + #ifdef CONFIG_VIRTFS - if (qemu_opts_foreach(qemu_find_opts("fsdev"), fsdev_init_func, NULL, 1) != 0) { + if (qemu_opts_foreach(qemu_find_opts("fsdev"), + fsdev_init_func, NULL, NULL)) { exit(1); } #endif @@ -4038,19 +4060,19 @@ int main(int argc, char **argv, char **envp) exit(1); } - if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) - != 0) { + if (qemu_opts_foreach(qemu_find_opts("device"), + device_help_func, NULL, NULL)) { exit(0); } if (qemu_opts_foreach(qemu_find_opts("object"), - object_create, NULL, 0) != 0) { + object_create, NULL, NULL)) { exit(1); } machine_opts = qemu_get_machine_opts(); if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine, - 1) < 0) { + NULL)) { object_unref(OBJECT(current_machine)); exit(1); } @@ -4178,9 +4200,10 @@ int main(int argc, char **argv, char **envp) /* open the virtual block devices */ if (snapshot) - qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0); + qemu_opts_foreach(qemu_find_opts("drive"), + drive_enable_snapshot, NULL, NULL); if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, - &machine_class->block_default_type, 1) != 0) { + &machine_class->block_default_type, NULL)) { exit(1); } @@ -4191,7 +4214,8 @@ int main(int argc, char **argv, char **envp) parse_numa_opts(machine_class); - if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) { + if (qemu_opts_foreach(qemu_find_opts("mon"), + mon_init_func, NULL, NULL)) { exit(1); } @@ -4257,8 +4281,10 @@ int main(int argc, char **argv, char **envp) } /* init generic devices */ - if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) + if (qemu_opts_foreach(qemu_find_opts("device"), + device_init_func, NULL, NULL)) { exit(1); + } /* Did we create any drives that we failed to create a device for? */ drive_check_orphaned(); @@ -4310,10 +4336,12 @@ int main(int argc, char **argv, char **envp) #ifdef CONFIG_VNC /* init remote displays */ - qemu_opts_foreach(qemu_find_opts("vnc"), vnc_init_func, NULL, 0); + qemu_opts_foreach(qemu_find_opts("vnc"), + vnc_init_func, NULL, NULL); if (show_vnc_port) { - printf("VNC server running on `%s'\n", - vnc_display_local_addr("default")); + char *ret = vnc_display_local_addr("default"); + printf("VNC server running on `%s'\n", ret); + g_free(ret); } #endif #ifdef CONFIG_SPICE @@ -488,7 +488,7 @@ static void xen_set_memory(struct MemoryListener *listener, XenIOState *state = container_of(listener, XenIOState, memory_listener); hwaddr start_addr = section->offset_within_address_space; ram_addr_t size = int128_get64(section->size); - bool log_dirty = memory_region_is_logging(section->mr); + bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); hvmmem_type_t mem_type; if (section->mr == &ram_memory) { @@ -646,21 +646,27 @@ static void xen_sync_dirty_bitmap(XenIOState *state, } static void xen_log_start(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { XenIOState *state = container_of(listener, XenIOState, memory_listener); - xen_sync_dirty_bitmap(state, section->offset_within_address_space, - int128_get64(section->size)); + if (new & ~old & (1 << DIRTY_MEMORY_VGA)) { + xen_sync_dirty_bitmap(state, section->offset_within_address_space, + int128_get64(section->size)); + } } -static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section) +static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, + int old, int new) { XenIOState *state = container_of(listener, XenIOState, memory_listener); - state->log_for_dirtybit = NULL; - /* Disable dirty bit tracking */ - xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL); + if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { + state->log_for_dirtybit = NULL; + /* Disable dirty bit tracking */ + xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL); + } } static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) |