diff options
142 files changed, 5385 insertions, 983 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 diff --git a/Makefile b/Makefile index d94580404c..2d5253680c 100644 --- a/Makefile +++ b/Makefile @@ -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..3e861c8413 100644 --- a/Makefile.target +++ b/Makefile.target @@ -129,7 +129,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..b5d90a41fa 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 */ }; 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/configure b/configure index 4e2f78a173..409edf94fb 100755 --- a/configure +++ b/configure @@ -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" 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 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 diff --git a/hmp.c b/hmp.c index e17852d1f9..514f22fbfa 100644 --- a/hmp.c +++ b/hmp.c @@ -22,6 +22,7 @@ #include "qmp-commands.h" #include "qemu/sockets.h" #include "monitor/monitor.h" +#include "monitor/qdev.h" #include "qapi/opts-visitor.h" #include "qapi/string-output-visitor.h" #include "qapi-visit.h" @@ -1250,6 +1251,23 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) } } +void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *hostname = qdict_get_str(qdict, "hostname"); + bool has_port = qdict_haskey(qdict, "port"); + int port = qdict_get_try_int(qdict, "port", -1); + bool has_tls_port = qdict_haskey(qdict, "tls-port"); + int tls_port = qdict_get_try_int(qdict, "tls-port", -1); + const char *cert_subject = qdict_get_try_str(qdict, "cert-subject"); + + qmp_client_migrate_info(protocol, hostname, + has_port, port, has_tls_port, tls_port, + !!cert_subject, cert_subject, &err); + hmp_handle_error(mon, &err); +} + void hmp_set_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); @@ -1482,6 +1500,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) } } +void hmp_device_add(Monitor *mon, const QDict *qdict) +{ + do_device_add(mon, qdict, NULL); +} + void hmp_device_del(Monitor *mon, const QDict *qdict) { const char *id = qdict_get_str(qdict, "id"); diff --git a/hmp.h b/hmp.h index a158e3fda1..a70ac4fd0f 100644 --- a/hmp.h +++ b/hmp.h @@ -67,6 +67,7 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict); void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict); +void hmp_client_migrate_info(Monitor *mon, const QDict *qdict); void hmp_set_password(Monitor *mon, const QDict *qdict); void hmp_expire_password(Monitor *mon, const QDict *qdict); void hmp_eject(Monitor *mon, const QDict *qdict); @@ -79,6 +80,7 @@ void hmp_block_job_pause(Monitor *mon, const QDict *qdict); void hmp_block_job_resume(Monitor *mon, const QDict *qdict); void hmp_block_job_complete(Monitor *mon, const QDict *qdict); void hmp_migrate(Monitor *mon, const QDict *qdict); +void hmp_device_add(Monitor *mon, const QDict *qdict); void hmp_device_del(Monitor *mon, const QDict *qdict); void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict); void hmp_netdev_add(Monitor *mon, const QDict *qdict); diff --git a/hw/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..799351ea44 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -219,7 +219,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/virt.c b/hw/arm/virt.c index 05db8cb2f7..0a75cc83ee 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) @@ -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/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/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/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..2baff4a660 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" @@ -1006,7 +1007,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 +1025,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 +1055,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 +1094,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 +1127,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 +1372,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/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/lpc_ich9.c b/hw/isa/lpc_ich9.c index dba758595f..18718d772e 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); } @@ -494,7 +491,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/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/vhost.c b/hw/virtio/vhost.c index 54851b7614..01f1e0490f 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; } @@ -601,7 +640,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 { @@ -1060,10 +1099,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 +1111,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 +1140,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/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/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/pc.h b/include/hw/i386/pc.h index 27bd748eab..bec6de1ddf 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, @@ -218,8 +218,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/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/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/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/kvm-all.c b/kvm-all.c index 17a3771efe..b2b1bc3359 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -1228,7 +1228,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()) { diff --git a/monitor.c b/monitor.c index b2561e1e2d..9afee7b946 100644 --- a/monitor.c +++ b/monitor.c @@ -118,25 +118,15 @@ * */ -typedef struct MonitorCompletionData MonitorCompletionData; -struct MonitorCompletionData { - Monitor *mon; - void (*user_print)(Monitor *mon, const QObject *data); -}; - typedef struct mon_cmd_t { const char *name; const char *args_type; const char *params; const char *help; - void (*user_print)(Monitor *mon, const QObject *data); union { void (*cmd)(Monitor *mon, const QDict *qdict); int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data); - int (*cmd_async)(Monitor *mon, const QDict *params, - MonitorCompletion *cb, void *opaque); } mhandler; - int flags; /* @sub_table is a list of 2nd level of commands. If it do not exist, * mhandler should be used. If it exist, sub_table[?].mhandler should be * used, and mhandler of 1st level plays the role of help function. @@ -171,11 +161,16 @@ struct MonFdset { QLIST_ENTRY(MonFdset) next; }; -typedef struct MonitorControl { +typedef struct { QObject *id; JSONMessageParser parser; - int command_mode; -} MonitorControl; + /* + * When a client connects, we're in capabilities negotiation mode. + * When command qmp_capabilities succeeds, we go into command + * mode. + */ + bool in_command_mode; /* are we in command mode? */ +} MonitorQMP; /* * To prevent flooding clients, events can be throttled. The @@ -205,7 +200,7 @@ struct Monitor { int mux_out; ReadLineState *rs; - MonitorControl *mc; + MonitorQMP qmp; CPUState *mon_cpu; BlockCompletionFunc *password_completion_cb; void *password_opaque; @@ -236,21 +231,20 @@ Monitor *default_mon; static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque); -static inline int qmp_cmd_mode(const Monitor *mon) -{ - return (mon->mc ? mon->mc->command_mode : 0); -} - -/* Return true if in control mode, false otherwise */ -static inline int monitor_ctrl_mode(const Monitor *mon) +/** + * Is @mon a QMP monitor? + */ +static inline bool monitor_is_qmp(const Monitor *mon) { return (mon->flags & MONITOR_USE_CONTROL); } -/* Return non-zero iff we have a current monitor, and it is in QMP mode. */ -int monitor_cur_is_qmp(void) +/** + * Is the current monitor, if any, a QMP monitor? + */ +bool monitor_cur_is_qmp(void) { - return cur_mon && monitor_ctrl_mode(cur_mon); + return cur_mon && monitor_is_qmp(cur_mon); } void monitor_read_command(Monitor *mon, int show_prompt) @@ -360,7 +354,7 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) if (!mon) return; - if (monitor_ctrl_mode(mon)) { + if (monitor_is_qmp(mon)) { return; } @@ -387,23 +381,6 @@ static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream, return 0; } -static void monitor_user_noop(Monitor *mon, const QObject *data) { } - -static inline int handler_is_qobject(const mon_cmd_t *cmd) -{ - return cmd->user_print != NULL; -} - -static inline bool handler_is_async(const mon_cmd_t *cmd) -{ - return cmd->flags & MONITOR_CMD_ASYNC; -} - -static inline int monitor_has_error(const Monitor *mon) -{ - return mon->error != NULL; -} - static void monitor_json_emitter(Monitor *mon, const QObject *data) { QString *json; @@ -418,24 +395,25 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data) QDECREF(json); } -static QDict *build_qmp_error_dict(const QError *err) +static QDict *build_qmp_error_dict(Error *err) { QObject *obj; - obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %p } }", - ErrorClass_lookup[err->err_class], - qerror_human(err)); + obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %s } }", + ErrorClass_lookup[error_get_class(err)], + error_get_pretty(err)); return qobject_to_qdict(obj); } -static void monitor_protocol_emitter(Monitor *mon, QObject *data) +static void monitor_protocol_emitter(Monitor *mon, QObject *data, + Error *err) { QDict *qmp; trace_monitor_protocol_emitter(mon); - if (!monitor_has_error(mon)) { + if (!err) { /* success response */ qmp = qdict_new(); if (data) { @@ -447,14 +425,12 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) } } else { /* error response */ - qmp = build_qmp_error_dict(mon->error); - QDECREF(mon->error); - mon->error = NULL; + qmp = build_qmp_error_dict(err); } - if (mon->mc->id) { - qdict_put_obj(qmp, "id", mon->mc->id); - mon->mc->id = NULL; + if (mon->qmp.id) { + qdict_put_obj(qmp, "id", mon->qmp.id); + mon->qmp.id = NULL; } monitor_json_emitter(mon, QOBJECT(qmp)); @@ -474,7 +450,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QObject *data) trace_monitor_protocol_event_emit(event, data); QLIST_FOREACH(mon, &mon_list, entry) { - if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) { + if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) { monitor_json_emitter(mon, data); } } @@ -594,15 +570,11 @@ static void monitor_qapi_event_init(void) static int do_qmp_capabilities(Monitor *mon, const QDict *params, QObject **ret_data) { - /* Will setup QMP capabilities in the future */ - if (monitor_ctrl_mode(mon)) { - mon->mc->command_mode = 1; - } - + mon->qmp.in_command_mode = true; return 0; } -static void handle_user_command(Monitor *mon, const char *cmdline); +static void handle_hmp_command(Monitor *mon, const char *cmdline); static void monitor_data_init(Monitor *mon) { @@ -641,7 +613,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, } } - handle_user_command(&hmp, command_line); + handle_hmp_command(&hmp, command_line); cur_mon = old_mon; qemu_mutex_lock(&hmp.out_lock); @@ -917,45 +889,6 @@ static void hmp_trace_file(Monitor *mon, const QDict *qdict) } #endif -static void user_monitor_complete(void *opaque, QObject *ret_data) -{ - MonitorCompletionData *data = (MonitorCompletionData *)opaque; - - if (ret_data) { - data->user_print(data->mon, ret_data); - } - monitor_resume(data->mon); - g_free(data); -} - -static void qmp_monitor_complete(void *opaque, QObject *ret_data) -{ - monitor_protocol_emitter(opaque, ret_data); -} - -static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, - const QDict *params) -{ - return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon); -} - -static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, - const QDict *params) -{ - int ret; - - MonitorCompletionData *cb_data = g_malloc(sizeof(*cb_data)); - cb_data->mon = mon; - cb_data->user_print = cmd->user_print; - monitor_suspend(mon); - ret = cmd->mhandler.cmd_async(mon, params, - user_monitor_complete, cb_data); - if (ret < 0) { - monitor_resume(mon); - g_free(cb_data); - } -} - static void hmp_info_help(Monitor *mon, const QDict *qdict) { help_cmd(mon, "info"); @@ -1085,39 +1018,33 @@ static void hmp_info_trace_events(Monitor *mon, const QDict *qdict) qapi_free_TraceEventInfoList(events); } -static int client_migrate_info(Monitor *mon, const QDict *qdict, - QObject **ret_data) +void qmp_client_migrate_info(const char *protocol, const char *hostname, + bool has_port, int64_t port, + bool has_tls_port, int64_t tls_port, + bool has_cert_subject, const char *cert_subject, + Error **errp) { - const char *protocol = qdict_get_str(qdict, "protocol"); - const char *hostname = qdict_get_str(qdict, "hostname"); - const char *subject = qdict_get_try_str(qdict, "cert-subject"); - int port = qdict_get_try_int(qdict, "port", -1); - int tls_port = qdict_get_try_int(qdict, "tls-port", -1); - Error *err = NULL; - int ret; - if (strcmp(protocol, "spice") == 0) { - if (!qemu_using_spice(&err)) { - qerror_report_err(err); - error_free(err); - return -1; + if (!qemu_using_spice(errp)) { + return; } - if (port == -1 && tls_port == -1) { - qerror_report(QERR_MISSING_PARAMETER, "port/tls-port"); - return -1; + if (!has_port && !has_tls_port) { + error_set(errp, QERR_MISSING_PARAMETER, "port/tls-port"); + return; } - ret = qemu_spice_migrate_info(hostname, port, tls_port, subject); - if (ret != 0) { - qerror_report(QERR_UNDEFINED_ERROR); - return -1; + if (qemu_spice_migrate_info(hostname, + has_port ? port : -1, + has_tls_port ? tls_port : -1, + cert_subject)) { + error_set(errp, QERR_UNDEFINED_ERROR); + return; } - return 0; + return; } - qerror_report(QERR_INVALID_PARAMETER, "protocol"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "spice"); } static void hmp_logfile(Monitor *mon, const QDict *qdict) @@ -4098,19 +4025,7 @@ void monitor_set_error(Monitor *mon, QError *qerror) } } -static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret) -{ - if (ret && !monitor_has_error(mon)) { - /* - * If it returns failure, it must have passed on error. - * - * Action: Report an internal error to the client if in QMP. - */ - qerror_report(QERR_UNDEFINED_ERROR); - } -} - -static void handle_user_command(Monitor *mon, const char *cmdline) +static void handle_hmp_command(Monitor *mon, const char *cmdline) { QDict *qdict; const mon_cmd_t *cmd; @@ -4118,26 +4033,10 @@ static void handle_user_command(Monitor *mon, const char *cmdline) qdict = qdict_new(); cmd = monitor_parse_command(mon, cmdline, 0, mon->cmd_table, qdict); - if (!cmd) - goto out; - - if (handler_is_async(cmd)) { - user_async_cmd_handler(mon, cmd, qdict); - } else if (handler_is_qobject(cmd)) { - QObject *data = NULL; - - /* XXX: ignores the error code */ - cmd->mhandler.cmd_new(mon, qdict, &data); - assert(!monitor_has_error(mon)); - if (data) { - cmd->user_print(mon, data); - qobject_decref(data); - } - } else { + if (cmd) { cmd->mhandler.cmd(mon, qdict); } -out: QDECREF(qdict); } @@ -4803,19 +4702,21 @@ static int monitor_can_read(void *opaque) return (mon->suspend_cnt == 0) ? 1 : 0; } -static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd) +static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd, + Error **errp) { bool is_cap = cmd->mhandler.cmd_new == do_qmp_capabilities; - if (is_cap && qmp_cmd_mode(mon)) { - qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND, - "Capabilities negotiation is already complete, command " - "'%s' ignored", cmd->name); + + if (is_cap && mon->qmp.in_command_mode) { + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "Capabilities negotiation is already complete, command " + "'%s' ignored", cmd->name); return true; } - if (!is_cap && !qmp_cmd_mode(mon)) { - qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND, - "Expecting capabilities negotiation with " - "'qmp_capabilities' before command '%s'", cmd->name); + if (!is_cap && !mon->qmp.in_command_mode) { + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "Expecting capabilities negotiation with " + "'qmp_capabilities' before command '%s'", cmd->name); return true; } return false; @@ -4831,8 +4732,9 @@ static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd) * the QMP_ACCEPT_UNKNOWNS flag is set, then the * checking is skipped for it. */ -static int check_client_args_type(const QDict *client_args, - const QDict *cmd_args, int flags) +static void check_client_args_type(const QDict *client_args, + const QDict *cmd_args, int flags, + Error **errp) { const QDictEntry *ent; @@ -4849,8 +4751,8 @@ static int check_client_args_type(const QDict *client_args, continue; } /* client arg doesn't exist */ - qerror_report(QERR_INVALID_PARAMETER, client_arg_name); - return -1; + error_set(errp, QERR_INVALID_PARAMETER, client_arg_name); + return; } arg_type = qobject_to_qstring(obj); @@ -4862,9 +4764,9 @@ static int check_client_args_type(const QDict *client_args, case 'B': case 's': if (qobject_type(client_arg) != QTYPE_QSTRING) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "string"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "string"); + return; } break; case 'i': @@ -4872,25 +4774,25 @@ static int check_client_args_type(const QDict *client_args, case 'M': case 'o': if (qobject_type(client_arg) != QTYPE_QINT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "int"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "int"); + return; } break; case 'T': if (qobject_type(client_arg) != QTYPE_QINT && qobject_type(client_arg) != QTYPE_QFLOAT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "number"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "number"); + return; } break; case 'b': case '-': if (qobject_type(client_arg) != QTYPE_QBOOL) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, - "bool"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_TYPE, + client_arg_name, "bool"); + return; } break; case 'O': @@ -4909,16 +4811,15 @@ static int check_client_args_type(const QDict *client_args, abort(); } } - - return 0; } /* * - Check if the client has passed all mandatory args * - Set special flags for argument validation */ -static int check_mandatory_args(const QDict *cmd_args, - const QDict *client_args, int *flags) +static void check_mandatory_args(const QDict *cmd_args, + const QDict *client_args, int *flags, + Error **errp) { const QDictEntry *ent; @@ -4933,12 +4834,10 @@ static int check_mandatory_args(const QDict *cmd_args, } else if (qstring_get_str(type)[0] != '-' && qstring_get_str(type)[1] != '?' && !qdict_haskey(client_args, cmd_arg_name)) { - qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name); - return -1; + error_set(errp, QERR_MISSING_PARAMETER, cmd_arg_name); + return; } } - - return 0; } static QDict *qdict_from_args_type(const char *args_type) @@ -4994,24 +4893,26 @@ out: * 3. Each argument provided by the client must have the type expected * by the command */ -static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args) +static void qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args, + Error **errp) { - int flags, err; + Error *err = NULL; + int flags; QDict *cmd_args; cmd_args = qdict_from_args_type(cmd->args_type); flags = 0; - err = check_mandatory_args(cmd_args, client_args, &flags); + check_mandatory_args(cmd_args, client_args, &flags, &err); if (err) { goto out; } - err = check_client_args_type(client_args, cmd_args, flags); + check_client_args_type(client_args, cmd_args, flags, &err); out: + error_propagate(errp, err); QDECREF(cmd_args); - return err; } /* @@ -5024,14 +4925,14 @@ out: * 5. If the "id" key exists, it can be anything (ie. json-value) * 6. Any argument not listed above is considered invalid */ -static QDict *qmp_check_input_obj(QObject *input_obj) +static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp) { const QDictEntry *ent; int has_exec_key = 0; QDict *input_dict; if (qobject_type(input_obj) != QTYPE_QDICT) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object"); + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "object"); return NULL; } @@ -5043,81 +4944,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); 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/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/qemu-options.hx b/qemu-options.hx index 7edd1f18ce..5eaba11cbc 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -241,8 +241,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", @@ -3239,7 +3238,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 +3256,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/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/cpu.c b/target-i386/cpu.c index 3305e09413..99ad551bee 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -2841,12 +2841,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 +3001,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 */ diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 4ee12ca2e9..26182bdc7e 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -305,7 +305,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 diff --git a/target-i386/kvm.c b/target-i386/kvm.c index a26d25a81f..ca2da84501 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -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-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 6de7759b67..2740ec4eef 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -2216,3 +2216,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 { diff --git a/tcg/tcg.h b/tcg/tcg.h index 8098f824b3..41e486959d 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -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/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.h b/translate-all.h index 02832b2718..b6a07bd5d3 100644 --- a/translate-all.h +++ b/translate-all.h @@ -21,7 +21,6 @@ /* translate-all.c */ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); -void cpu_unlink_tb(CPUState *cpu); void tb_check_watchpoint(CPUState *cpu); #endif /* TRANSLATE_ALL_H */ 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, },{ diff --git a/vl.c b/vl.c index 1d4c0890f2..cdd81b4486 100644 --- a/vl.c +++ b/vl.c @@ -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 @@ -1310,7 +1308,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 +1335,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, }; @@ -2647,13 +2649,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 +3771,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(); @@ -3798,12 +3806,6 @@ int main(int argc, char **argv, char **envp) } #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)) { @@ -4312,8 +4314,9 @@ int main(int argc, char **argv, char **envp) /* init remote displays */ qemu_opts_foreach(qemu_find_opts("vnc"), vnc_init_func, NULL, 0); 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 |