summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--docs/devel/memory.rst14
-rw-r--r--docs/specs/ppc-spapr-hotplug.rst510
-rw-r--r--docs/specs/ppc-spapr-hotplug.txt409
-rw-r--r--docs/specs/ppc-spapr-uv-hcalls.rst89
-rw-r--r--docs/specs/ppc-spapr-uv-hcalls.txt76
-rw-r--r--docs/system/ppc/pseries.rst8
-rw-r--r--hw/core/machine.c2
-rw-r--r--hw/ide/ahci.c10
-rw-r--r--hw/nvme/ctrl.c6
-rw-r--r--hw/nvram/meson.build6
-rw-r--r--hw/pci-host/pnv_phb4.c299
-rw-r--r--hw/pci-host/pnv_phb4_pec.c121
-rw-r--r--hw/rdma/rdma_utils.c14
-rw-r--r--hw/rdma/rdma_utils.h2
-rw-r--r--hw/rdma/trace-events2
-rw-r--r--hw/scsi/megasas.c85
-rw-r--r--hw/scsi/scsi-bus.c12
-rw-r--r--hw/scsi/scsi-disk.c4
-rw-r--r--include/exec/memory.h4
-rw-r--r--include/hw/pci-host/pnv_phb4.h80
-rw-r--r--include/hw/pci/pci.h12
-rw-r--r--include/hw/scsi/scsi.h4
-rw-r--r--include/sysemu/dma.h31
-rw-r--r--softmmu/dma-helpers.c34
-rw-r--r--softmmu/memory.c188
-rw-r--r--stubs/meson.build4
-rw-r--r--target/ppc/cpu-models.c9
-rw-r--r--target/ppc/cpu.h1
-rw-r--r--target/ppc/helper.h1
-rw-r--r--target/ppc/machine.c136
-rw-r--r--target/ppc/misc_helper.c9
-rw-r--r--target/ppc/translate.c16
-rw-r--r--tests/avocado/ppc_74xx.py123
33 files changed, 1225 insertions, 1096 deletions
diff --git a/docs/devel/memory.rst b/docs/devel/memory.rst
index 5dc8a12682..69c5e3f914 100644
--- a/docs/devel/memory.rst
+++ b/docs/devel/memory.rst
@@ -67,11 +67,15 @@ MemoryRegion):
 
   You initialize a pure container with memory_region_init().
 
-- alias: a subsection of another region.  Aliases allow a region to be
-  split apart into discontiguous regions.  Examples of uses are memory banks
-  used when the guest address space is smaller than the amount of RAM
-  addressed, or a memory controller that splits main memory to expose a "PCI
-  hole".  Aliases may point to any type of region, including other aliases,
+- alias: a subsection of another region. Aliases allow a region to be
+  split apart into discontiguous regions. Examples of uses are memory
+  banks used when the guest address space is smaller than the amount
+  of RAM addressed, or a memory controller that splits main memory to
+  expose a "PCI hole". You can also create aliases to avoid trying to
+  add the original region to multiple parents via
+  `memory_region_add_subregion`.
+
+  Aliases may point to any type of region, including other aliases,
   but an alias may not point back to itself, directly or indirectly.
   You initialize these with memory_region_init_alias().
 
diff --git a/docs/specs/ppc-spapr-hotplug.rst b/docs/specs/ppc-spapr-hotplug.rst
new file mode 100644
index 0000000000..f84dc55ad9
--- /dev/null
+++ b/docs/specs/ppc-spapr-hotplug.rst
@@ -0,0 +1,510 @@
+=============================
+sPAPR Dynamic Reconfiguration
+=============================
+
+sPAPR or pSeries guests make use of a facility called dynamic reconfiguration
+to handle hot plugging of dynamic "physical" resources like PCI cards, or
+"logical"/para-virtual 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 section 13 of the Linux on Power Architecture
+Reference document ([LoPAR]_). This document provides a summary of that
+information as it applies to the implementation within QEMU.
+
+Dynamic-reconfiguration Connectors
+==================================
+
+To manage hot plug/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 four 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 hot plugged PHB. In this case the
+arrays would be fetched as part of the device tree retrieval interfaces
+for hot plugged resources described under :ref:`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: big-endian (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 the Linux on
+    Power Architecture Reference ([LoPAR]_) 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 the [LoPAR]_ 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 (the hypervisor
+    portion of PowerVM):
+
+      ``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:
+
+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``
+------------------------
+
+Set the power level for a specified power domain.
+
+  ``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.
+
+``rtas-get-power-level``
+------------------------
+
+Get the power level for a specified power domain.
+
+  ``arg[0]``: integer identifying power domain.
+
+  ``output[0]``: status, ``0`` on success.
+
+  ``output[1]``: current power level.
+
+``rtas-set-indicator``
+----------------------
+
+Set the state of an indicator or sensor.
+
+  ``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.
+
+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 inaccessible 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 hot plug.
+
+    ``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
+  hot plug/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:``
+--------------------------
+
+Used to read an indicator or sensor value.
+
+  ``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
+
+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``
+--------------------------------
+
+Used to fetch an OpenFirmware device tree description of the resource associated
+with a particular DRC.
+
+  ``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 device tree sibling node.
+
+    ``2``: instruct guest to prepare for next device tree child node.
+
+    ``3``: instruct guest to prepare for next device tree property.
+
+    ``4``: instruct guest to ascend to parent device tree 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.
+
+The DRC index is encoded in the first 4-bytes of the first work area buffer.
+Work area (``wa``) 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.
+
+Hot plug/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 KVM on Power, hence the reason for extending the notification
+framework to support hotplug events.
+
+The format for these EPOW-signalled events is described below under
+:ref:`hot-plug-unplug-event-structure`. Note that these events are not formally
+part of the PAPR+ specification, and have been superseded by a newer format,
+also described below under :ref:`hot-plug-unplug-event-structure`, and so are
+now deemed a "legacy" format. The formats are similar, but the "modern" format
+contains additional fields/flags, which are denoted for the purposes of this
+documentation with ``#ifdef GUEST_SUPPORTS_MODERN`` guards.
+
+QEMU should assume support only for "legacy" fields/flags unless the guest
+advertises support for the "modern" format via
+``ibm,client-architecture-support`` hcall by setting byte 5, bit 6 of it's
+``ibm,architecture-vec-5`` option vector structure (as described by [LoPAR]_,
+section B.5.2.3). As with "legacy" format events, "modern" format events are
+surfaced to the guest via check-exception RTAS calls, but use a dedicated event
+source to signal the guest. This event source is advertised to the guest by the
+addition of a ``hot-plug-events`` node under ``/event-sources`` node of the
+guest's device tree using the standard format described in [LoPAR]_,
+section B.5.12.2.
+
+.. _hot-plug-unplug-event-structure:
+
+Hot plug/unplug event structure
+===============================
+
+The hot plug specific payload in QEMU is implemented as follows (with all values
+encoded in big-endian format):
+
+.. code-block:: c
+
+   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
+   #ifdef GUEST_SUPPORTS_MODERN
+   #define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4
+   #endif
+       uint8_t hotplug_identifier;         /* type of the resource identifier,
+                                            * which serves as the discriminator
+                                            * for the 'drc' union field below
+                                            */
+   #ifdef GUEST_SUPPORTS_MODERN
+       uint8_t capabilities;               /* capability flags, currently unused
+                                            * by QEMU
+                                            */
+   #else
+       uint8_t reserved;
+   #endif
+       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)
+                                            */
+   #ifdef GUEST_SUPPORTS_MODERN
+           struct {
+               uint32_t count;             /* number of DR resources to take
+                                            * action on
+                                            */
+               uint32_t index;             /* DRC index of first resource to take
+                                            * action on. guest will take action
+                                            * on DRC index <index> through
+                                            * DRC index <index + count - 1> in
+                                            * sequential order
+                                            */
+           } count_indexed;
+   #endif
+           char name[1];                   /* string representing the name of the
+                                            * DRC to take action on
+                                            */
+       } drc;
+   } QEMU_PACKED;
+
+``ibm,lrdr-capacity``
+=====================
+
+``ibm,lrdr-capacity`` is a property in the /rtas device tree node that
+identifies the dynamic reconfiguration capabilities of the guest. It consists
+of a triple consisting of ``<phys>``, ``<size>`` and ``<maxcpus>``.
+
+  ``<phys>``, encoded in BE format represents the maximum address in bytes and
+  hence the maximum memory that can be allocated to the guest.
+
+  ``<size>``, encoded in BE format represents the size increments in which
+  memory can be hot-plugged to the guest.
+
+  ``<maxcpus>``, a BE-encoded integer, represents the maximum number of
+  processors that the guest can have.
+
+``pseries`` guests use this property to note the maximum allowed CPUs for the
+guest.
+
+``ibm,dynamic-reconfiguration-memory``
+======================================
+
+``ibm,dynamic-reconfiguration-memory`` is a device tree node that represents
+dynamically reconfigurable logical memory blocks (LMB). This node is generated
+only when the guest advertises the support for it via
+``ibm,client-architecture-support`` call. Memory that is not dynamically
+reconfigurable is represented by ``/memory`` nodes. The properties of this node
+that are of interest to the sPAPR memory hotplug implementation in QEMU are
+described here.
+
+``ibm,lmb-size``
+----------------
+
+This 64-bit integer defines the size of each dynamically reconfigurable LMB.
+
+``ibm,associativity-lookup-arrays``
+-----------------------------------
+
+This property defines a lookup array in which the NUMA associativity
+information for each LMB can be found. It is a property encoded array
+that begins with an integer M, the number of associativity lists followed
+by an integer N, the number of entries per associativity list and terminated
+by M associativity lists each of length N integers.
+
+This property provides the same information as given by ``ibm,associativity``
+property in a ``/memory`` node. Each assigned LMB has an index value between
+0 and M-1 which is used as an index into this table to select which
+associativity list to use for the LMB. This index value for each LMB is defined
+in ``ibm,dynamic-memory`` property.
+
+``ibm,dynamic-memory``
+----------------------
+
+This property describes the dynamically reconfigurable memory. It is a
+property encoded array that has an integer N, the number of LMBs followed
+by N LMB list entries.
+
+Each LMB list entry consists of the following elements:
+
+- Logical address of the start of the LMB encoded as a 64-bit integer. This
+  corresponds to ``reg`` property in ``/memory`` node.
+- DRC index of the LMB that corresponds to ``ibm,my-drc-index`` property
+  in a ``/memory`` node.
+- Four bytes reserved for expansion.
+- Associativity list index for the LMB that is used as an index into
+  ``ibm,associativity-lookup-arrays`` property described earlier. This is used
+  to retrieve the right associativity list to be used for this LMB.
+- A 32-bit flags word. The bit at bit position ``0x00000008`` defines whether
+  the LMB is assigned to the partition as of boot time.
+
+``ibm,dynamic-memory-v2``
+-------------------------
+
+This property describes the dynamically reconfigurable memory. This is
+an alternate and newer way to describe dynamically reconfigurable memory.
+It is a property encoded array that has an integer N (the number of
+LMB set entries) followed by N LMB set entries. There is an LMB set entry
+for each sequential group of LMBs that share common attributes.
+
+Each LMB set entry consists of the following elements:
+
+- Number of sequential LMBs in the entry represented by a 32-bit integer.
+- Logical address of the first LMB in the set encoded as a 64-bit integer.
+- DRC index of the first LMB in the set.
+- Associativity list index that is used as an index into
+  ``ibm,associativity-lookup-arrays`` property described earlier. This
+  is used to retrieve the right associativity list to be used for all
+  the LMBs in this set.
+- A 32-bit flags word that applies to all the LMBs in the set.
diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt
deleted file mode 100644
index d4fb2d46d9..0000000000
--- a/docs/specs/ppc-spapr-hotplug.txt
+++ /dev/null
@@ -1,409 +0,0 @@
-= 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.
-
-The format for these EPOW-signalled events is described below under
-"hotplug/unplug event structure". Note that these events are not
-formally part of the PAPR+ specification, and have been superseded by a
-newer format, also described below under "hotplug/unplug event structure",
-and so are now deemed a "legacy" format. The formats are similar, but the
-"modern" format contains additional fields/flags, which are denoted for the
-purposes of this documentation with "#ifdef GUEST_SUPPORTS_MODERN" guards.
-
-QEMU should assume support only for "legacy" fields/flags unless the guest
-advertises support for the "modern" format via ibm,client-architecture-support
-hcall by setting byte 5, bit 6 of it's ibm,architecture-vec-5 option vector
-structure (as described by LoPAPR v11, B.6.2.3). As with "legacy" format events,
-"modern" format events are surfaced to the guest via check-exception RTAS calls,
-but use a dedicated event source to signal the guest. This event source is
-advertised to the guest by the addition of a "hot-plug-events" node under
-"/event-sources" node of the guest's device tree using the standard format
-described in LoPAPR v11, B.6.12.1.
-
-== hotplug/unplug event structure ==
-
-The hotplug-specific payload in QEMU is 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
-#ifdef GUEST_SUPPORTS_MODERN
-#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4
-#endif
-    uint8_t hotplug_identifier;         /* type of the resource identifier,
-                                         * which serves as the discriminator
-                                         * for the 'drc' union field below
-                                         */
-#ifdef GUEST_SUPPORTS_MODERN
-    uint8_t capabilities;               /* capability flags, currently unused
-                                         * by QEMU
-                                         */
-#else
-    uint8_t reserved;
-#endif
-    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)
-                                         */
-#ifdef GUEST_SUPPORTS_MODERN
-        struct {
-            uint32_t count;             /* number of DR resources to take
-                                         * action on
-                                         */
-            uint32_t index;             /* DRC index of first resource to take
-                                         * action on. guest will take action
-                                         * on DRC index <index> through
-                                         * DRC index <index + count - 1> in
-                                         * sequential order
-                                         */
-        } count_indexed;
-#endif
-        char name[1];                   /* string representing the name of the
-                                         * DRC to take action on
-                                         */
-    } drc;
-} QEMU_PACKED;
-
-== ibm,lrdr-capacity ==
-
-ibm,lrdr-capacity is a property in the /rtas device tree node that identifies
-the dynamic reconfiguration capabilities of the guest. It consists of a triple
-consisting of <phys>, <size> and <maxcpus>.
-
-  <phys>, encoded in BE format represents the maximum address in bytes and
-  hence the maximum memory that can be allocated to the guest.
-
-  <size>, encoded in BE format represents the size increments in which
-  memory can be hot-plugged to the guest.
-
-  <maxcpus>, a BE-encoded integer, represents the maximum number of
-  processors that the guest can have.
-
-pseries guests use this property to note the maximum allowed CPUs for the
-guest.
-
-== ibm,dynamic-reconfiguration-memory ==
-
-ibm,dynamic-reconfiguration-memory is a device tree node that represents
-dynamically reconfigurable logical memory blocks (LMB). This node
-is generated only when the guest advertises the support for it via
-ibm,client-architecture-support call. Memory that is not dynamically
-reconfigurable is represented by /memory nodes. The properties of this
-node that are of interest to the sPAPR memory hotplug implementation
-in QEMU are described here.
-
-ibm,lmb-size
-
-This 64bit integer defines the size of each dynamically reconfigurable LMB.
-
-ibm,associativity-lookup-arrays
-
-This property defines a lookup array in which the NUMA associativity
-information for each LMB can be found. It is a property encoded array
-that begins with an integer M, the number of associativity lists followed
-by an integer N, the number of entries per associativity list and terminated
-by M associativity lists each of length N integers.
-
-This property provides the same information as given by ibm,associativity
-property in a /memory node. Each assigned LMB has an index value between
-0 and M-1 which is used as an index into this table to select which
-associativity list to use for the LMB. This index value for each LMB
-is defined in ibm,dynamic-memory property.
-
-ibm,dynamic-memory
-
-This property describes the dynamically reconfigurable memory. It is a
-property encoded array that has an integer N, the number of LMBs followed
-by N LMB list entries.
-
-Each LMB list entry consists of the following elements:
-
-- Logical address of the start of the LMB encoded as a 64bit integer. This
-  corresponds to reg property in /memory node.
-- DRC index of the LMB that corresponds to ibm,my-drc-index property
-  in a /memory node.
-- Four bytes reserved for expansion.
-- Associativity list index for the LMB that is used as an index into
-  ibm,associativity-lookup-arrays property described earlier. This
-  is used to retrieve the right associativity list to be used for this
-  LMB.
-- A 32bit flags word. The bit at bit position 0x00000008 defines whether
-  the LMB is assigned to the partition as of boot time.
-
-ibm,dynamic-memory-v2
-
-This property describes the dynamically reconfigurable memory. This is
-an alternate and newer way to describe dynamically reconfigurable memory.
-It is a property encoded array that has an integer N (the number of
-LMB set entries) followed by N LMB set entries. There is an LMB set entry
-for each sequential group of LMBs that share common attributes.
-
-Each LMB set entry consists of the following elements:
-
-- Number of sequential LMBs in the entry represented by a 32bit integer.
-- Logical address of the first LMB in the set encoded as a 64bit integer.
-- DRC index of the first LMB in the set.
-- Associativity list index that is used as an index into
-  ibm,associativity-lookup-arrays property described earlier. This
-  is used to retrieve the right associativity list to be used for all
-  the LMBs in this set.
-- A 32bit flags word that applies to all the LMBs in the set.
-
-[1] http://thread.gmane.org/gmane.linux.ports.ppc.embedded/75350/focus=106867
diff --git a/docs/specs/ppc-spapr-uv-hcalls.rst b/docs/specs/ppc-spapr-uv-hcalls.rst
new file mode 100644
index 0000000000..a00288deb3
--- /dev/null
+++ b/docs/specs/ppc-spapr-uv-hcalls.rst
@@ -0,0 +1,89 @@
+===================================
+Hypervisor calls and the Ultravisor
+===================================
+
+On PPC64 systems supporting Protected Execution Facility (PEF), system memory
+can be placed in a secured region where only an ultravisor running in firmware
+can provide access to. pSeries guests on such systems can communicate with
+the ultravisor (via ultracalls) to switch to a secure virtual machine (SVM) mode
+where the guest's memory is relocated to this secured region, making its memory
+inaccessible to normal processes/guests running on the host.
+
+The various ultracalls/hypercalls relating to SVM mode are currently only
+documented internally, but are planned for direct inclusion into the Linux on
+Power Architecture Reference document ([LoPAR]_). An internal ACR has been filed
+to reserve a hypercall number range specific to this use case to avoid any
+future conflicts with the IBM internally maintained Power Architecture Platform
+Reference (PAPR+) documentation specification. This document summarizes some of
+these details as they relate to QEMU.
+
+Hypercalls needed by the ultravisor
+===================================
+
+Switching to SVM mode involves a number of hcalls issued by the ultravisor to
+the hypervisor to orchestrate the movement of guest memory to secure memory and
+various other aspects of the SVM mode. Numbers are assigned for these hcalls
+within the reserved range ``0xEF00-0xEF80``. The below documents the hcalls
+relevant to QEMU.
+
+``H_TPM_COMM`` (``0xef10``)
+---------------------------
+
+SVM file systems are encrypted using a symmetric key. This key is then
+wrapped/encrypted using the public key of a trusted system which has the private
+key stored in the system's TPM. An Ultravisor will use this hcall to
+unwrap/unseal the symmetric key using the system's TPM device or a TPM Resource
+Manager associated with the device.
+
+The Ultravisor sets up a separate session key with the TPM in advance during
+host system boot. All sensitive in and out values will be encrypted using the
+session key. Though the hypervisor will see the in and out buffers in raw form,
+any sensitive contents will generally be encrypted using this session key.
+
+Arguments:
+
+  ``r3``: ``H_TPM_COMM`` (``0xef10``)
+
+  ``r4``: ``TPM`` operation, one of:
+
+    ``TPM_COMM_OP_EXECUTE`` (``0x1``): send a request to a TPM and receive a
+    response, opening a new TPM session if one has not already been opened.
+
+    ``TPM_COMM_OP_CLOSE_SESSION`` (``0x2``): close the existing TPM session, if
+    any.
+
+  ``r5``: ``in_buffer``, guest physical address of buffer containing the
+  request. Caller may use the same address for both request and response.
+
+  ``r6``: ``in_size``, size of the in buffer. Must be less than or equal to
+  4 KB.
+
+  ``r7``: ``out_buffer``, guest physical address of buffer to store the
+  response. Caller may use the same address for both request and response.
+
+  ``r8``: ``out_size``, size of the out buffer. Must be at least 4 KB, as this
+  is the maximum request/response size supported by most TPM implementations,
+  including the TPM Resource Manager in the linux kernel.
+
+Return values:
+
+  ``r3``: one of the following values:
+
+    ``H_Success``: request processed successfully.
+
+    ``H_PARAMETER``: invalid TPM operation.
+
+    ``H_P2``: ``in_buffer`` is invalid.
+
+    ``H_P3``: ``in_size`` is invalid.
+
+    ``H_P4``: ``out_buffer`` is invalid.
+
+    ``H_P5``: ``out_size`` is invalid.
+
+    ``H_RESOURCE``: problem communicating with TPM.
+
+    ``H_FUNCTION``: TPM access is not currently allowed/configured.
+
+    ``r4``: For ``TPM_COMM_OP_EXECUTE``, the size of the response will be stored
+    here upon success.
diff --git a/docs/specs/ppc-spapr-uv-hcalls.txt b/docs/specs/ppc-spapr-uv-hcalls.txt
deleted file mode 100644
index 389c2740d7..0000000000
--- a/docs/specs/ppc-spapr-uv-hcalls.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-On PPC64 systems supporting Protected Execution Facility (PEF), system
-memory can be placed in a secured region where only an "ultravisor"
-running in firmware can provide to access it. pseries guests on such
-systems can communicate with the ultravisor (via ultracalls) to switch to a
-secure VM mode (SVM) where the guest's memory is relocated to this secured
-region, making its memory inaccessible to normal processes/guests running on
-the host.
-
-The various ultracalls/hypercalls relating to SVM mode are currently
-only documented internally, but are planned for direct inclusion into the
-public OpenPOWER version of the PAPR specification (LoPAPR/LoPAR). An internal
-ACR has been filed to reserve a hypercall number range specific to this
-use-case to avoid any future conflicts with the internally-maintained PAPR
-specification. This document summarizes some of these details as they relate
-to QEMU.
-
-== hypercalls needed by the ultravisor ==
-
-Switching to SVM mode involves a number of hcalls issued by the ultravisor
-to the hypervisor to orchestrate the movement of guest memory to secure
-memory and various other aspects SVM mode. Numbers are assigned for these
-hcalls within the reserved range 0xEF00-0xEF80. The below documents the
-hcalls relevant to QEMU.
-
-- H_TPM_COMM (0xef10)
-
-  For TPM_COMM_OP_EXECUTE operation:
-    Send a request to a TPM and receive a response, opening a new TPM session
-    if one has not already been opened.
-
-  For TPM_COMM_OP_CLOSE_SESSION operation:
-    Close the existing TPM session, if any.
-
-  Arguments:
-
-    r3 : H_TPM_COMM (0xef10)
-    r4 : TPM operation, one of:
-         TPM_COMM_OP_EXECUTE (0x1)
-         TPM_COMM_OP_CLOSE_SESSION (0x2)
-    r5 : in_buffer, guest physical address of buffer containing the request
-         - Caller may use the same address for both request and response
-    r6 : in_size, size of the in buffer
-         - Must be less than or equal to 4KB
-    r7 : out_buffer, guest physical address of buffer to store the response
-         - Caller may use the same address for both request and response
-    r8 : out_size, size of the out buffer
-         - Must be at least 4KB, as this is the maximum request/response size
-           supported by most TPM implementations, including the TPM Resource
-           Manager in the linux kernel.
-
-  Return values:
-
-    r3 : H_Success    request processed successfully
-         H_PARAMETER  invalid TPM operation
-         H_P2         in_buffer is invalid
-         H_P3         in_size is invalid
-         H_P4         out_buffer is invalid
-         H_P5         out_size is invalid
-         H_RESOURCE   problem communicating with TPM
-         H_FUNCTION   TPM access is not currently allowed/configured
-    r4 : For TPM_COMM_OP_EXECUTE, the size of the response will be stored here
-         upon success.
-
-  Use-case/notes:
-
-    SVM filesystems are encrypted using a symmetric key. This key is then
-    wrapped/encrypted using the public key of a trusted system which has the
-    private key stored in the system's TPM. An Ultravisor will use this
-    hcall to unwrap/unseal the symmetric key using the system's TPM device
-    or a TPM Resource Manager associated with the device.
-
-    The Ultravisor sets up a separate session key with the TPM in advance
-    during host system boot. All sensitive in and out values will be
-    encrypted using the session key. Though the hypervisor will see the 'in'
-    and 'out' buffers in raw form, any sensitive contents will generally be
-    encrypted using this session key.
diff --git a/docs/system/ppc/pseries.rst b/docs/system/ppc/pseries.rst
index 1689324815..569237dc0c 100644
--- a/docs/system/ppc/pseries.rst
+++ b/docs/system/ppc/pseries.rst
@@ -110,16 +110,12 @@ can  also be found in QEMU documentation:
 .. toctree::
    :maxdepth: 1
 
+   ../../specs/ppc-spapr-hotplug.rst
    ../../specs/ppc-spapr-hcalls.rst
    ../../specs/ppc-spapr-numa.rst
+   ../../specs/ppc-spapr-uv-hcalls.rst
    ../../specs/ppc-spapr-xive.rst
 
-Other documentation available in QEMU docs directory:
-
-* Hot plug (``/docs/specs/ppc-spapr-hotplug.txt``).
-* Hypervisor calls needed by the Ultravisor
-  (``/docs/specs/ppc-spapr-uv-hcalls.txt``).
-
 Switching between the KVM-PR and KVM-HV kernel module
 =====================================================
 
diff --git a/hw/core/machine.c b/hw/core/machine.c
index debcdc0e70..d856485cb4 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -1091,7 +1091,7 @@ MemoryRegion *machine_consume_memdev(MachineState *machine,
 {
     MemoryRegion *ret = host_memory_backend_get_memory(backend);
 
-    if (memory_region_is_mapped(ret)) {
+    if (host_memory_backend_is_mapped(backend)) {
         error_report("memory backend %s can't be used multiple times.",
                      object_get_canonical_path_component(OBJECT(backend)));
         exit(EXIT_FAILURE);
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 205dfdc662..7ce001cacd 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1159,7 +1159,7 @@ static void process_ncq_command(AHCIState *s, int port, const uint8_t *cmd_fis,
     ahci_populate_sglist(ad, &ncq_tfs->sglist, ncq_tfs->cmdh, size, 0);
 
     if (ncq_tfs->sglist.size < size) {
-        error_report("ahci: PRDT length for NCQ command (0x%zx) "
+        error_report("ahci: PRDT length for NCQ command (0x" DMA_ADDR_FMT ") "
                      "is smaller than the requested size (0x%zx)",
                      ncq_tfs->sglist.size, size);
         ncq_err(ncq_tfs);
@@ -1384,9 +1384,9 @@ static void ahci_pio_transfer(const IDEDMA *dma)
         const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
 
         if (is_write) {
-            dma_buf_write(s->data_ptr, size, &s->sg, attrs);
+            dma_buf_write(s->data_ptr, size, NULL, &s->sg, attrs);
         } else {
-            dma_buf_read(s->data_ptr, size, &s->sg, attrs);
+            dma_buf_read(s->data_ptr, size, NULL, &s->sg, attrs);
         }
     }
 
@@ -1479,9 +1479,9 @@ static int ahci_dma_rw_buf(const IDEDMA *dma, bool is_write)
     }
 
     if (is_write) {
-        dma_buf_read(p, l, &s->sg, MEMTXATTRS_UNSPECIFIED);
+        dma_buf_read(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
     } else {
-        dma_buf_write(p, l, &s->sg, MEMTXATTRS_UNSPECIFIED);
+        dma_buf_write(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
     }
 
     /* free sglist, update byte count */
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 462f79a1f6..1f62116af9 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -1147,12 +1147,12 @@ static uint16_t nvme_tx(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr, uint32_t len,
 
     if (sg->flags & NVME_SG_DMA) {
         const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
-        uint64_t residual;
+        dma_addr_t residual;
 
         if (dir == NVME_TX_DIRECTION_TO_DEVICE) {
-            residual = dma_buf_write(ptr, len, &sg->qsg, attrs);
+            dma_buf_write(ptr, len, &residual, &sg->qsg, attrs);
         } else {
-            residual = dma_buf_read(ptr, len, &sg->qsg, attrs);
+            dma_buf_read(ptr, len, &residual, &sg->qsg, attrs);
         }
 
         if (unlikely(residual)) {
diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build
index 202a5466e6..f5ee9f6b88 100644
--- a/hw/nvram/meson.build
+++ b/hw/nvram/meson.build
@@ -1,5 +1,7 @@
-# QOM interfaces must be available anytime QOM is used.
-qom_ss.add(files('fw_cfg-interface.c'))
+if have_system or have_tools
+  # QOM interfaces must be available anytime QOM is used.
+  qom_ss.add(files('fw_cfg-interface.c'))
+endif
 
 softmmu_ss.add(files('fw_cfg.c'))
 softmmu_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c'))
diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c
index a7b638831e..a78add75b0 100644
--- a/hw/pci-host/pnv_phb4.c
+++ b/hw/pci-host/pnv_phb4.c
@@ -22,7 +22,6 @@
 #include "hw/irq.h"
 #include "hw/qdev-properties.h"
 #include "qom/object.h"
-#include "sysemu/sysemu.h"
 #include "trace.h"
 
 #define phb_error(phb, fmt, ...)                                        \
@@ -228,16 +227,16 @@ static void pnv_phb4_check_mbt(PnvPHB4 *phb, uint32_t index)
     /* TODO: Figure out how to implemet/decode AOMASK */
 
     /* Check if it matches an enabled MMIO region in the PEC stack */
-    if (memory_region_is_mapped(&phb->stack->mmbar0) &&
-        base >= phb->stack->mmio0_base &&
-        (base + size) <= (phb->stack->mmio0_base + phb->stack->mmio0_size)) {
-        parent = &phb->stack->mmbar0;
-        base -= phb->stack->mmio0_base;
-    } else if (memory_region_is_mapped(&phb->stack->mmbar1) &&
-        base >= phb->stack->mmio1_base &&
-        (base + size) <= (phb->stack->mmio1_base + phb->stack->mmio1_size)) {
-        parent = &phb->stack->mmbar1;
-        base -= phb->stack->mmio1_base;
+    if (memory_region_is_mapped(&phb->mmbar0) &&
+        base >= phb->mmio0_base &&
+        (base + size) <= (phb->mmio0_base + phb->mmio0_size)) {
+        parent = &phb->mmbar0;
+        base -= phb->mmio0_base;
+    } else if (memory_region_is_mapped(&phb->mmbar1) &&
+        base >= phb->mmio1_base &&
+        (base + size) <= (phb->mmio1_base + phb->mmio1_size)) {
+        parent = &phb->mmbar1;
+        base -= phb->mmio1_base;
     } else {
         phb_error(phb, "PHB MBAR %d out of parent bounds", index);
         return;
@@ -673,7 +672,7 @@ static uint64_t pnv_phb4_reg_read(void *opaque, hwaddr off, unsigned size)
 
     switch (off) {
     case PHB_VERSION:
-        return phb->version;
+        return PNV_PHB4_PEC_GET_CLASS(phb->pec)->version;
 
         /* Read-only */
     case PHB_PHB4_GEN_CAP:
@@ -861,44 +860,65 @@ const MemoryRegionOps pnv_phb4_xscom_ops = {
 static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
                                             unsigned size)
 {
-    PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
+    PnvPHB4 *phb = PNV_PHB4(opaque);
     uint32_t reg = addr >> 3;
 
     /* TODO: add list of allowed registers and error out if not */
-    return stack->nest_regs[reg];
+    return phb->nest_regs[reg];
 }
 
-static void pnv_phb4_update_regions(PnvPhb4PecStack *stack)
+/*
+ * Return the 'stack_no' of a PHB4. 'stack_no' is the order
+ * the PHB4 occupies in the PEC. This is the reverse of what
+ * pnv_phb4_pec_get_phb_id() does.
+ *
+ * E.g. a phb with phb_id = 4 and pec->index = 1 (PEC1) will
+ * be the second phb (stack_no = 1) of the PEC.
+ */
+static int pnv_phb4_get_phb_stack_no(PnvPHB4 *phb)
 {
-    PnvPHB4 *phb = stack->phb;
+    PnvPhb4PecState *pec = phb->pec;
+    PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
+    int index = pec->index;
+    int stack_no = phb->phb_id;
+
+    while (index--) {
+        stack_no -= pecc->num_phbs[index];
+    }
+
+    return stack_no;
+}
 
+static void pnv_phb4_update_regions(PnvPHB4 *phb)
+{
     /* Unmap first always */
     if (memory_region_is_mapped(&phb->mr_regs)) {
-        memory_region_del_subregion(&stack->phbbar, &phb->mr_regs);
+        memory_region_del_subregion(&phb->phbbar, &phb->mr_regs);
     }
     if (memory_region_is_mapped(&phb->xsrc.esb_mmio)) {
-        memory_region_del_subregion(&stack->intbar, &phb->xsrc.esb_mmio);
+        memory_region_del_subregion(&phb->intbar, &phb->xsrc.esb_mmio);
     }
 
     /* Map registers if enabled */
-    if (memory_region_is_mapped(&stack->phbbar)) {
-        memory_region_add_subregion(&stack->phbbar, 0, &phb->mr_regs);
+    if (memory_region_is_mapped(&phb->phbbar)) {
+        memory_region_add_subregion(&phb->phbbar, 0, &phb->mr_regs);
     }
 
     /* Map ESB if enabled */
-    if (memory_region_is_mapped(&stack->intbar)) {
-        memory_region_add_subregion(&stack->intbar, 0, &phb->xsrc.esb_mmio);
+    if (memory_region_is_mapped(&phb->intbar)) {
+        memory_region_add_subregion(&phb->intbar, 0, &phb->xsrc.esb_mmio);
     }
 
     /* Check/update m32 */
     pnv_phb4_check_all_mbt(phb);
 }
 
-static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
+static void pnv_pec_phb_update_map(PnvPHB4 *phb)
 {
-    PnvPhb4PecState *pec = stack->pec;
+    PnvPhb4PecState *pec = phb->pec;
     MemoryRegion *sysmem = get_system_memory();
-    uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN];
+    uint64_t bar_en = phb->nest_regs[PEC_NEST_STK_BAR_EN];
+    int stack_no = pnv_phb4_get_phb_stack_no(phb);
     uint64_t bar, mask, size;
     char name[64];
 
@@ -911,106 +931,106 @@ static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
      */
 
     /* Handle unmaps */
-    if (memory_region_is_mapped(&stack->mmbar0) &&
+    if (memory_region_is_mapped(&phb->mmbar0) &&
         !(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
-        memory_region_del_subregion(sysmem, &stack->mmbar0);
+        memory_region_del_subregion(sysmem, &phb->mmbar0);
     }
-    if (memory_region_is_mapped(&stack->mmbar1) &&
+    if (memory_region_is_mapped(&phb->mmbar1) &&
         !(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
-        memory_region_del_subregion(sysmem, &stack->mmbar1);
+        memory_region_del_subregion(sysmem, &phb->mmbar1);
     }
-    if (memory_region_is_mapped(&stack->phbbar) &&
+    if (memory_region_is_mapped(&phb->phbbar) &&
         !(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
-        memory_region_del_subregion(sysmem, &stack->phbbar);
+        memory_region_del_subregion(sysmem, &phb->phbbar);
     }
-    if (memory_region_is_mapped(&stack->intbar) &&
+    if (memory_region_is_mapped(&phb->intbar) &&
         !(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
-        memory_region_del_subregion(sysmem, &stack->intbar);
+        memory_region_del_subregion(sysmem, &phb->intbar);
     }
 
     /* Update PHB */
-    pnv_phb4_update_regions(stack);
+    pnv_phb4_update_regions(phb);
 
     /* Handle maps */
-    if (!memory_region_is_mapped(&stack->mmbar0) &&
+    if (!memory_region_is_mapped(&phb->mmbar0) &&
         (bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
-        bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8;
-        mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK];
+        bar = phb->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8;
+        mask = phb->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK];
         size = ((~mask) >> 8) + 1;
-        snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio0",
-                 pec->chip_id, pec->index, stack->stack_no);
-        memory_region_init(&stack->mmbar0, OBJECT(stack), name, size);
-        memory_region_add_subregion(sysmem, bar, &stack->mmbar0);
-        stack->mmio0_base = bar;
-        stack->mmio0_size = size;
-    }
-    if (!memory_region_is_mapped(&stack->mmbar1) &&
+        snprintf(name, sizeof(name), "pec-%d.%d-phb-%d-mmio0",
+                 pec->chip_id, pec->index, stack_no);
+        memory_region_init(&phb->mmbar0, OBJECT(phb), name, size);
+        memory_region_add_subregion(sysmem, bar, &phb->mmbar0);
+        phb->mmio0_base = bar;
+        phb->mmio0_size = size;
+    }
+    if (!memory_region_is_mapped(&phb->mmbar1) &&
         (bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
-        bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8;
-        mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK];
+        bar = phb->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8;
+        mask = phb->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK];
         size = ((~mask) >> 8) + 1;
-        snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio1",
-                 pec->chip_id, pec->index, stack->stack_no);
-        memory_region_init(&stack->mmbar1, OBJECT(stack), name, size);
-        memory_region_add_subregion(sysmem, bar, &stack->mmbar1);
-        stack->mmio1_base = bar;
-        stack->mmio1_size = size;
-    }
-    if (!memory_region_is_mapped(&stack->phbbar) &&
+        snprintf(name, sizeof(name), "pec-%d.%d-phb-%d-mmio1",
+                 pec->chip_id, pec->index, stack_no);
+        memory_region_init(&phb->mmbar1, OBJECT(phb), name, size);
+        memory_region_add_subregion(sysmem, bar, &phb->mmbar1);
+        phb->mmio1_base = bar;
+        phb->mmio1_size = size;
+    }
+    if (!memory_region_is_mapped(&phb->phbbar) &&
         (bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
-        bar = stack->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8;
+        bar = phb->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8;
         size = PNV_PHB4_NUM_REGS << 3;
-        snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-phb",
-                 pec->chip_id, pec->index, stack->stack_no);
-        memory_region_init(&stack->phbbar, OBJECT(stack), name, size);
-        memory_region_add_subregion(sysmem, bar, &stack->phbbar);
+        snprintf(name, sizeof(name), "pec-%d.%d-phb-%d",
+                 pec->chip_id, pec->index, stack_no);
+        memory_region_init(&phb->phbbar, OBJECT(phb), name, size);
+        memory_region_add_subregion(sysmem, bar, &phb->phbbar);
     }
-    if (!memory_region_is_mapped(&stack->intbar) &&
+    if (!memory_region_is_mapped(&phb->intbar) &&
         (bar_en & PEC_NEST_STK_BAR_EN_INT)) {
-        bar = stack->nest_regs[PEC_NEST_STK_INT_BAR] >> 8;
+        bar = phb->nest_regs[PEC_NEST_STK_INT_BAR] >> 8;
         size = PNV_PHB4_MAX_INTs << 16;
-        snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-int",
-                 stack->pec->chip_id, stack->pec->index, stack->stack_no);
-        memory_region_init(&stack->intbar, OBJECT(stack), name, size);
-        memory_region_add_subregion(sysmem, bar, &stack->intbar);
+        snprintf(name, sizeof(name), "pec-%d.%d-phb-%d-int",
+                 phb->pec->chip_id, phb->pec->index, stack_no);
+        memory_region_init(&phb->intbar, OBJECT(phb), name, size);
+        memory_region_add_subregion(sysmem, bar, &phb->intbar);
     }
 
     /* Update PHB */
-    pnv_phb4_update_regions(stack);
+    pnv_phb4_update_regions(phb);
 }
 
 static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr,
                                          uint64_t val, unsigned size)
 {
-    PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
-    PnvPhb4PecState *pec = stack->pec;
+    PnvPHB4 *phb = PNV_PHB4(opaque);
+    PnvPhb4PecState *pec = phb->pec;
     uint32_t reg = addr >> 3;
 
     switch (reg) {
     case PEC_NEST_STK_PCI_NEST_FIR:
-        stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val;
+        phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val;
         break;
     case PEC_NEST_STK_PCI_NEST_FIR_CLR:
-        stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val;
+        phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val;
         break;
     case PEC_NEST_STK_PCI_NEST_FIR_SET:
-        stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val;
+        phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val;
         break;
     case PEC_NEST_STK_PCI_NEST_FIR_MSK:
-        stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val;
+        phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val;
         break;
     case PEC_NEST_STK_PCI_NEST_FIR_MSKC:
-        stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val;
+        phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val;
         break;
     case PEC_NEST_STK_PCI_NEST_FIR_MSKS:
-        stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val;
+        phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val;
         break;
     case PEC_NEST_STK_PCI_NEST_FIR_ACT0:
     case PEC_NEST_STK_PCI_NEST_FIR_ACT1:
-        stack->nest_regs[reg] = val;
+        phb->nest_regs[reg] = val;
         break;
     case PEC_NEST_STK_PCI_NEST_FIR_WOF:
-        stack->nest_regs[reg] = 0;
+        phb->nest_regs[reg] = 0;
         break;
     case PEC_NEST_STK_ERR_REPORT_0:
     case PEC_NEST_STK_ERR_REPORT_1:
@@ -1018,39 +1038,39 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr,
         /* Flag error ? */
         break;
     case PEC_NEST_STK_PBCQ_MODE:
-        stack->nest_regs[reg] = val & 0xff00000000000000ull;
+        phb->nest_regs[reg] = val & 0xff00000000000000ull;
         break;
     case PEC_NEST_STK_MMIO_BAR0:
     case PEC_NEST_STK_MMIO_BAR0_MASK:
     case PEC_NEST_STK_MMIO_BAR1:
     case PEC_NEST_STK_MMIO_BAR1_MASK:
-        if (stack->nest_regs[PEC_NEST_STK_BAR_EN] &
+        if (phb->nest_regs[PEC_NEST_STK_BAR_EN] &
             (PEC_NEST_STK_BAR_EN_MMIO0 |
              PEC_NEST_STK_BAR_EN_MMIO1)) {
             phb_pec_error(pec, "Changing enabled BAR unsupported\n");
         }
-        stack->nest_regs[reg] = val & 0xffffffffff000000ull;
+        phb->nest_regs[reg] = val & 0xffffffffff000000ull;
         break;
     case PEC_NEST_STK_PHB_REGS_BAR:
-        if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
+        if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
             phb_pec_error(pec, "Changing enabled BAR unsupported\n");
         }
-        stack->nest_regs[reg] = val & 0xffffffffffc00000ull;
+        phb->nest_regs[reg] = val & 0xffffffffffc00000ull;
         break;
     case PEC_NEST_STK_INT_BAR:
-        if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
+        if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
             phb_pec_error(pec, "Changing enabled BAR unsupported\n");
         }
-        stack->nest_regs[reg] = val & 0xfffffff000000000ull;
+        phb->nest_regs[reg] = val & 0xfffffff000000000ull;
         break;
     case PEC_NEST_STK_BAR_EN:
-        stack->nest_regs[reg] = val & 0xf000000000000000ull;
-        pnv_pec_stk_update_map(stack);
+        phb->nest_regs[reg] = val & 0xf000000000000000ull;
+        pnv_pec_phb_update_map(phb);
         break;
     case PEC_NEST_STK_DATA_FRZ_TYPE:
     case PEC_NEST_STK_PBCQ_TUN_BAR:
         /* Not used for now */
-        stack->nest_regs[reg] = val;
+        phb->nest_regs[reg] = val;
         break;
     default:
         qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx
@@ -1071,54 +1091,54 @@ static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = {
 static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr,
                                            unsigned size)
 {
-    PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
+    PnvPHB4 *phb = PNV_PHB4(opaque);
     uint32_t reg = addr >> 3;
 
     /* TODO: add list of allowed registers and error out if not */
-    return stack->pci_regs[reg];
+    return phb->pci_regs[reg];
 }
 
 static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr,
                                         uint64_t val, unsigned size)
 {
-    PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
+    PnvPHB4 *phb = PNV_PHB4(opaque);
     uint32_t reg = addr >> 3;
 
     switch (reg) {
     case PEC_PCI_STK_PCI_FIR:
-        stack->pci_regs[reg] = val;
+        phb->pci_regs[reg] = val;
         break;
     case PEC_PCI_STK_PCI_FIR_CLR:
-        stack->pci_regs[PEC_PCI_STK_PCI_FIR] &= val;
+        phb->pci_regs[PEC_PCI_STK_PCI_FIR] &= val;
         break;
     case PEC_PCI_STK_PCI_FIR_SET:
-        stack->pci_regs[PEC_PCI_STK_PCI_FIR] |= val;
+        phb->pci_regs[PEC_PCI_STK_PCI_FIR] |= val;
         break;
     case PEC_PCI_STK_PCI_FIR_MSK:
-        stack->pci_regs[reg] = val;
+        phb->pci_regs[reg] = val;
         break;
     case PEC_PCI_STK_PCI_FIR_MSKC:
-        stack->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val;
+        phb->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val;
         break;
     case PEC_PCI_STK_PCI_FIR_MSKS:
-        stack->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val;
+        phb->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val;
         break;
     case PEC_PCI_STK_PCI_FIR_ACT0:
     case PEC_PCI_STK_PCI_FIR_ACT1:
-        stack->pci_regs[reg] = val;
+        phb->pci_regs[reg] = val;
         break;
     case PEC_PCI_STK_PCI_FIR_WOF:
-        stack->pci_regs[reg] = 0;
+        phb->pci_regs[reg] = 0;
         break;
     case PEC_PCI_STK_ETU_RESET:
-        stack->pci_regs[reg] = val & 0x8000000000000000ull;
+        phb->pci_regs[reg] = val & 0x8000000000000000ull;
         /* TODO: Implement reset */
         break;
     case PEC_PCI_STK_PBAIB_ERR_REPORT:
         break;
     case PEC_PCI_STK_PBAIB_TX_CMD_CRED:
     case PEC_PCI_STK_PBAIB_TX_DAT_CRED:
-        stack->pci_regs[reg] = val;
+        phb->pci_regs[reg] = val;
         break;
     default:
         qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx
@@ -1362,7 +1382,7 @@ int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index)
     int offset = 0;
 
     while (index--) {
-        offset += pecc->num_stacks[index];
+        offset += pecc->num_phbs[index];
     }
 
     return offset + stack_index;
@@ -1459,9 +1479,9 @@ static AddressSpace *pnv_phb4_dma_iommu(PCIBus *bus, void *opaque, int devfn)
 
 static void pnv_phb4_xscom_realize(PnvPHB4 *phb)
 {
-    PnvPhb4PecStack *stack = phb->stack;
-    PnvPhb4PecState *pec = stack->pec;
+    PnvPhb4PecState *pec = phb->pec;
     PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
+    int stack_no = pnv_phb4_get_phb_stack_no(phb);
     uint32_t pec_nest_base;
     uint32_t pec_pci_base;
     char name[64];
@@ -1469,22 +1489,22 @@ static void pnv_phb4_xscom_realize(PnvPHB4 *phb)
     assert(pec);
 
     /* Initialize the XSCOM regions for the stack registers */
-    snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-stack-%d",
-             pec->chip_id, pec->index, stack->stack_no);
-    pnv_xscom_region_init(&stack->nest_regs_mr, OBJECT(stack),
-                          &pnv_pec_stk_nest_xscom_ops, stack, name,
+    snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-phb-%d",
+             pec->chip_id, pec->index, stack_no);
+    pnv_xscom_region_init(&phb->nest_regs_mr, OBJECT(phb),
+                          &pnv_pec_stk_nest_xscom_ops, phb, name,
                           PHB4_PEC_NEST_STK_REGS_COUNT);
 
-    snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d",
-             pec->chip_id, pec->index, stack->stack_no);
-    pnv_xscom_region_init(&stack->pci_regs_mr, OBJECT(stack),
-                          &pnv_pec_stk_pci_xscom_ops, stack, name,
+    snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-phb-%d",
+             pec->chip_id, pec->index, stack_no);
+    pnv_xscom_region_init(&phb->pci_regs_mr, OBJECT(phb),
+                          &pnv_pec_stk_pci_xscom_ops, phb, name,
                           PHB4_PEC_PCI_STK_REGS_COUNT);
 
     /* PHB pass-through */
-    snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d-phb",
-             pec->chip_id, pec->index, stack->stack_no);
-    pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(phb),
+    snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-phb-%d",
+             pec->chip_id, pec->index, stack_no);
+    pnv_xscom_region_init(&phb->phb_regs_mr, OBJECT(phb),
                           &pnv_phb4_xscom_ops, phb, name, 0x40);
 
     pec_nest_base = pecc->xscom_nest_base(pec);
@@ -1492,15 +1512,15 @@ static void pnv_phb4_xscom_realize(PnvPHB4 *phb)
 
     /* Populate the XSCOM address space. */
     pnv_xscom_add_subregion(pec->chip,
-                            pec_nest_base + 0x40 * (stack->stack_no + 1),
-                            &stack->nest_regs_mr);
+                            pec_nest_base + 0x40 * (stack_no + 1),
+                            &phb->nest_regs_mr);
     pnv_xscom_add_subregion(pec->chip,
-                            pec_pci_base + 0x40 * (stack->stack_no + 1),
-                            &stack->pci_regs_mr);
+                            pec_pci_base + 0x40 * (stack_no + 1),
+                            &phb->pci_regs_mr);
     pnv_xscom_add_subregion(pec->chip,
                             pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
-                            0x40 * stack->stack_no,
-                            &stack->phb_regs_mr);
+                            0x40 * stack_no,
+                            &phb->phb_regs_mr);
 }
 
 static void pnv_phb4_instance_init(Object *obj)
@@ -1513,8 +1533,8 @@ static void pnv_phb4_instance_init(Object *obj)
     object_initialize_child(obj, "source", &phb->xsrc, TYPE_XIVE_SOURCE);
 }
 
-static PnvPhb4PecStack *pnv_phb4_get_stack(PnvChip *chip, PnvPHB4 *phb,
-                                           Error **errp)
+static PnvPhb4PecState *pnv_phb4_get_pec(PnvChip *chip, PnvPHB4 *phb,
+                                         Error **errp)
 {
     Pnv9Chip *chip9 = PNV9_CHIP(chip);
     int chip_id = phb->chip_id;
@@ -1523,14 +1543,14 @@ static PnvPhb4PecStack *pnv_phb4_get_stack(PnvChip *chip, PnvPHB4 *phb,
 
     for (i = 0; i < chip->num_pecs; i++) {
         /*
-         * For each PEC, check the amount of stacks it supports
-         * and see if the given phb4 index matches a stack.
+         * For each PEC, check the amount of phbs it supports
+         * and see if the given phb4 index matches an index.
          */
         PnvPhb4PecState *pec = &chip9->pecs[i];
 
-        for (j = 0; j < pec->num_stacks; j++) {
+        for (j = 0; j < pec->num_phbs; j++) {
             if (index == pnv_phb4_pec_get_phb_id(pec, j)) {
-                return &pec->stacks[j];
+                return pec;
             }
         }
     }
@@ -1552,10 +1572,9 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
     char name[32];
 
     /* User created PHB */
-    if (!phb->stack) {
+    if (!phb->pec) {
         PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
         PnvChip *chip = pnv_get_chip(pnv, phb->chip_id);
-        PnvPhb4PecClass *pecc;
         BusState *s;
 
         if (!chip) {
@@ -1563,23 +1582,12 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
             return;
         }
 
-        phb->stack = pnv_phb4_get_stack(chip, phb, &local_err);
+        phb->pec = pnv_phb4_get_pec(chip, phb, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
             return;
         }
 
-        /* All other phb properties but 'version' are already set */
-        pecc = PNV_PHB4_PEC_GET_CLASS(phb->stack->pec);
-        object_property_set_int(OBJECT(phb), "version", pecc->version,
-                                &error_fatal);
-
-        /*
-         * Assign stack->phb since pnv_phb4_update_regions() uses it
-         * to access the phb.
-         */
-        phb->stack->phb = phb;
-
         /*
          * Reparent user created devices to the chip to build
          * correctly the device tree.
@@ -1624,12 +1632,6 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
     pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb);
     pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
 
-    /* Add a single Root port if running with defaults */
-    if (defaults_enabled()) {
-        pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb),
-                                 TYPE_PNV_PHB4_ROOT_PORT);
-    }
-
     /* Setup XIVE Source */
     if (phb->big_phb) {
         nr_irqs = PNV_PHB4_MAX_INTs;
@@ -1680,9 +1682,8 @@ static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno)
 static Property pnv_phb4_properties[] = {
         DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0),
         DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0),
-        DEFINE_PROP_UINT64("version", PnvPHB4, version, 0),
-        DEFINE_PROP_LINK("stack", PnvPHB4, stack, TYPE_PNV_PHB4_PEC_STACK,
-                         PnvPhb4PecStack *),
+        DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC,
+                         PnvPhb4PecState *),
         DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c
index 7fe7f1f007..40d89fda56 100644
--- a/hw/pci-host/pnv_phb4_pec.c
+++ b/hw/pci-host/pnv_phb4_pec.c
@@ -112,15 +112,28 @@ static const MemoryRegionOps pnv_pec_pci_xscom_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
-static void pnv_pec_instance_init(Object *obj)
+static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec,
+                                        int stack_no,
+                                        Error **errp)
 {
-    PnvPhb4PecState *pec = PNV_PHB4_PEC(obj);
-    int i;
+    PnvPHB4 *phb = PNV_PHB4(qdev_new(TYPE_PNV_PHB4));
+    int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no);
+
+    object_property_set_link(OBJECT(phb), "pec", OBJECT(pec),
+                             &error_abort);
+    object_property_set_int(OBJECT(phb), "chip-id", pec->chip_id,
+                            &error_fatal);
+    object_property_set_int(OBJECT(phb), "index", phb_id,
+                            &error_fatal);
 
-    for (i = 0; i < PHB4_PEC_MAX_STACKS; i++) {
-        object_initialize_child(obj, "stack[*]", &pec->stacks[i],
-                                TYPE_PNV_PHB4_PEC_STACK);
+    if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) {
+        return;
     }
+
+    /* Add a single Root port if running with defaults */
+    pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb),
+                             PNV_PHB4_PEC_GET_CLASS(pec)->rp_model);
+
 }
 
 static void pnv_pec_realize(DeviceState *dev, Error **errp)
@@ -135,22 +148,14 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    pec->num_stacks = pecc->num_stacks[pec->index];
-
-    /* Create stacks */
-    for (i = 0; i < pec->num_stacks; i++) {
-        PnvPhb4PecStack *stack = &pec->stacks[i];
-        Object *stk_obj = OBJECT(stack);
+    pec->num_phbs = pecc->num_phbs[pec->index];
 
-        object_property_set_int(stk_obj, "stack-no", i, &error_abort);
-        object_property_set_link(stk_obj, "pec", OBJECT(pec), &error_abort);
-        if (!qdev_realize(DEVICE(stk_obj), NULL, errp)) {
-            return;
+    /* Create PHBs if running with defaults */
+    if (defaults_enabled()) {
+        for (i = 0; i < pec->num_phbs; i++) {
+            pnv_pec_default_phb_realize(pec, i, errp);
         }
     }
-    for (; i < PHB4_PEC_MAX_STACKS; i++) {
-        object_unparent(OBJECT(&pec->stacks[i]));
-    }
 
     /* Initialize the XSCOM regions for the PEC registers */
     snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest", pec->chip_id,
@@ -195,7 +200,7 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt,
     _FDT((fdt_setprop(fdt, offset, "compatible", pecc->compat,
                       pecc->compat_size)));
 
-    for (i = 0; i < pec->num_stacks; i++) {
+    for (i = 0; i < pec->num_phbs; i++) {
         int phb_id = pnv_phb4_pec_get_phb_id(pec, i);
         int stk_offset;
 
@@ -231,11 +236,11 @@ static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec)
 }
 
 /*
- * PEC0 -> 1 stack
- * PEC1 -> 2 stacks
- * PEC2 -> 3 stacks
+ * PEC0 -> 1 phb
+ * PEC1 -> 2 phb
+ * PEC2 -> 3 phbs
  */
-static const uint32_t pnv_pec_num_stacks[] = { 1, 2, 3 };
+static const uint32_t pnv_pec_num_phbs[] = { 1, 2, 3 };
 
 static void pnv_pec_class_init(ObjectClass *klass, void *data)
 {
@@ -260,14 +265,14 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data)
     pecc->stk_compat = stk_compat;
     pecc->stk_compat_size = sizeof(stk_compat);
     pecc->version = PNV_PHB4_VERSION;
-    pecc->num_stacks = pnv_pec_num_stacks;
+    pecc->num_phbs = pnv_pec_num_phbs;
+    pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT;
 }
 
 static const TypeInfo pnv_pec_type_info = {
     .name          = TYPE_PNV_PHB4_PEC,
     .parent        = TYPE_DEVICE,
     .instance_size = sizeof(PnvPhb4PecState),
-    .instance_init = pnv_pec_instance_init,
     .class_init    = pnv_pec_class_init,
     .class_size    = sizeof(PnvPhb4PecClass),
     .interfaces    = (InterfaceInfo[]) {
@@ -276,73 +281,9 @@ static const TypeInfo pnv_pec_type_info = {
     }
 };
 
-static void pnv_pec_stk_default_phb_realize(PnvPhb4PecStack *stack,
-                                            Error **errp)
-{
-    PnvPhb4PecState *pec = stack->pec;
-    PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
-    int phb_id = pnv_phb4_pec_get_phb_id(pec, stack->stack_no);
-
-    stack->phb = PNV_PHB4(qdev_new(TYPE_PNV_PHB4));
-
-    object_property_set_int(OBJECT(stack->phb), "chip-id", pec->chip_id,
-                            &error_fatal);
-    object_property_set_int(OBJECT(stack->phb), "index", phb_id,
-                            &error_fatal);
-    object_property_set_int(OBJECT(stack->phb), "version", pecc->version,
-                            &error_fatal);
-    object_property_set_link(OBJECT(stack->phb), "stack", OBJECT(stack),
-                             &error_abort);
-
-    if (!sysbus_realize(SYS_BUS_DEVICE(stack->phb), errp)) {
-        return;
-    }
-}
-
-static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
-{
-    PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev);
-
-    if (!defaults_enabled()) {
-        return;
-    }
-
-    pnv_pec_stk_default_phb_realize(stack, errp);
-}
-
-static Property pnv_pec_stk_properties[] = {
-        DEFINE_PROP_UINT32("stack-no", PnvPhb4PecStack, stack_no, 0),
-        DEFINE_PROP_LINK("pec", PnvPhb4PecStack, pec, TYPE_PNV_PHB4_PEC,
-                         PnvPhb4PecState *),
-        DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pnv_pec_stk_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    device_class_set_props(dc, pnv_pec_stk_properties);
-    dc->realize = pnv_pec_stk_realize;
-    dc->user_creatable = false;
-
-    /* TODO: reset regs ? */
-}
-
-static const TypeInfo pnv_pec_stk_type_info = {
-    .name          = TYPE_PNV_PHB4_PEC_STACK,
-    .parent        = TYPE_DEVICE,
-    .instance_size = sizeof(PnvPhb4PecStack),
-    .class_init    = pnv_pec_stk_class_init,
-    .interfaces    = (InterfaceInfo[]) {
-        { TYPE_PNV_XSCOM_INTERFACE },
-        { }
-    }
-};
-
 static void pnv_pec_register_types(void)
 {
     type_register_static(&pnv_pec_type_info);
-    type_register_static(&pnv_pec_stk_type_info);
 }
 
 type_init(pnv_pec_register_types);
diff --git a/hw/rdma/rdma_utils.c b/hw/rdma/rdma_utils.c
index 98df58f689..5a7ef63ad2 100644
--- a/hw/rdma/rdma_utils.c
+++ b/hw/rdma/rdma_utils.c
@@ -17,29 +17,29 @@
 #include "trace.h"
 #include "rdma_utils.h"
 
-void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen)
+void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len)
 {
     void *p;
-    hwaddr len = plen;
+    dma_addr_t pci_len = len;
 
     if (!addr) {
         rdma_error_report("addr is NULL");
         return NULL;
     }
 
-    p = pci_dma_map(dev, addr, &len, DMA_DIRECTION_TO_DEVICE);
+    p = pci_dma_map(dev, addr, &pci_len, DMA_DIRECTION_TO_DEVICE);
     if (!p) {
         rdma_error_report("pci_dma_map fail, addr=0x%"PRIx64", len=%"PRId64,
-                          addr, len);
+                          addr, pci_len);
         return NULL;
     }
 
-    if (len != plen) {
-        rdma_pci_dma_unmap(dev, p, len);
+    if (pci_len != len) {
+        rdma_pci_dma_unmap(dev, p, pci_len);
         return NULL;
     }
 
-    trace_rdma_pci_dma_map(addr, p, len);
+    trace_rdma_pci_dma_map(addr, p, pci_len);
 
     return p;
 }
diff --git a/hw/rdma/rdma_utils.h b/hw/rdma/rdma_utils.h
index 9fd0efd940..0c6414e7e0 100644
--- a/hw/rdma/rdma_utils.h
+++ b/hw/rdma/rdma_utils.h
@@ -38,7 +38,7 @@ typedef struct RdmaProtectedGSList {
     GSList *list;
 } RdmaProtectedGSList;
 
-void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen);
+void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len);
 void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len);
 void rdma_protected_gqueue_init(RdmaProtectedGQueue *list);
 void rdma_protected_gqueue_destroy(RdmaProtectedGQueue *list);
diff --git a/hw/rdma/trace-events b/hw/rdma/trace-events
index 9accb14973..c23175120e 100644
--- a/hw/rdma/trace-events
+++ b/hw/rdma/trace-events
@@ -27,5 +27,5 @@ rdma_rm_alloc_qp(uint32_t rm_qpn, uint32_t backend_qpn, uint8_t qp_type) "rm_qpn
 rdma_rm_modify_qp(uint32_t qpn, uint32_t attr_mask, int qp_state, uint8_t sgid_idx) "qpn=0x%x, attr_mask=0x%x, qp_state=%d, sgid_idx=%d"
 
 # rdma_utils.c
-rdma_pci_dma_map(uint64_t addr, void *vaddr, uint64_t len) "0x%"PRIx64" -> %p (len=%" PRId64")"
+rdma_pci_dma_map(uint64_t addr, void *vaddr, uint64_t len) "0x%"PRIx64" -> %p (len=%" PRIu64")"
 rdma_pci_dma_unmap(void *vaddr) "%p"
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index 83c321ec20..c9da5ce0b5 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -739,6 +739,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
     size_t dcmd_size = sizeof(info);
     BusChild *kid;
     int num_pd_disks = 0;
+    dma_addr_t residual;
 
     memset(&info, 0x0, dcmd_size);
     if (cmd->iov_size < dcmd_size) {
@@ -849,7 +850,9 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
                                        MFI_INFO_PDMIX_SATA |
                                        MFI_INFO_PDMIX_LD);
 
-    cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -857,6 +860,7 @@ static int megasas_mfc_get_defaults(MegasasState *s, MegasasCmd *cmd)
 {
     struct mfi_defaults info;
     size_t dcmd_size = sizeof(struct mfi_defaults);
+    dma_addr_t residual;
 
     memset(&info, 0x0, dcmd_size);
     if (cmd->iov_size < dcmd_size) {
@@ -879,7 +883,9 @@ static int megasas_mfc_get_defaults(MegasasState *s, MegasasCmd *cmd)
     info.disable_preboot_cli = 1;
     info.cluster_disable = 1;
 
-    cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -887,6 +893,7 @@ static int megasas_dcmd_get_bios_info(MegasasState *s, MegasasCmd *cmd)
 {
     struct mfi_bios_data info;
     size_t dcmd_size = sizeof(info);
+    dma_addr_t residual;
 
     memset(&info, 0x0, dcmd_size);
     if (cmd->iov_size < dcmd_size) {
@@ -900,7 +907,9 @@ static int megasas_dcmd_get_bios_info(MegasasState *s, MegasasCmd *cmd)
         info.expose_all_drives = 1;
     }
 
-    cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -908,10 +917,13 @@ static int megasas_dcmd_get_fw_time(MegasasState *s, MegasasCmd *cmd)
 {
     uint64_t fw_time;
     size_t dcmd_size = sizeof(fw_time);
+    dma_addr_t residual;
 
     fw_time = cpu_to_le64(megasas_fw_time());
 
-    cmd->iov_size -= dma_buf_read(&fw_time, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(&fw_time, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -931,6 +943,7 @@ static int megasas_event_info(MegasasState *s, MegasasCmd *cmd)
 {
     struct mfi_evt_log_state info;
     size_t dcmd_size = sizeof(info);
+    dma_addr_t residual;
 
     memset(&info, 0, dcmd_size);
 
@@ -938,7 +951,9 @@ static int megasas_event_info(MegasasState *s, MegasasCmd *cmd)
     info.shutdown_seq_num = cpu_to_le32(s->shutdown_event);
     info.boot_seq_num = cpu_to_le32(s->boot_event);
 
-    cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -968,6 +983,7 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
     size_t dcmd_size = sizeof(info);
     BusChild *kid;
     uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks;
+    dma_addr_t residual;
 
     memset(&info, 0, dcmd_size);
     offset = 8;
@@ -1007,7 +1023,9 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
     info.size = cpu_to_le32(offset);
     info.count = cpu_to_le32(num_pd_disks);
 
-    cmd->iov_size -= dma_buf_read(&info, offset, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(&info, offset, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -1034,7 +1052,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
     uint64_t pd_size;
     uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
     uint8_t cmdbuf[6];
-    size_t len, resid;
+    size_t len;
+    dma_addr_t residual;
 
     if (!cmd->iov_buf) {
         cmd->iov_buf = g_malloc0(dcmd_size);
@@ -1101,9 +1120,11 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
     info->connected_port_bitmap = 0x1;
     info->device_speed = 1;
     info->link_speed = 1;
-    resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(cmd->iov_buf, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     g_free(cmd->iov_buf);
-    cmd->iov_size = dcmd_size - resid;
+    cmd->iov_size = dcmd_size - residual;
     cmd->iov_buf = NULL;
     return MFI_STAT_OK;
 }
@@ -1138,7 +1159,8 @@ static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd)
 static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
 {
     struct mfi_ld_list info;
-    size_t dcmd_size = sizeof(info), resid;
+    size_t dcmd_size = sizeof(info);
+    dma_addr_t residual;
     uint32_t num_ld_disks = 0, max_ld_disks;
     uint64_t ld_size;
     BusChild *kid;
@@ -1173,8 +1195,9 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
     info.ld_count = cpu_to_le32(num_ld_disks);
     trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks);
 
-    resid = dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
-    cmd->iov_size = dcmd_size - resid;
+    dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size = dcmd_size - residual;
     return MFI_STAT_OK;
 }
 
@@ -1182,7 +1205,8 @@ static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd)
 {
     uint16_t flags;
     struct mfi_ld_targetid_list info;
-    size_t dcmd_size = sizeof(info), resid;
+    size_t dcmd_size = sizeof(info);
+    dma_addr_t residual;
     uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns;
     BusChild *kid;
 
@@ -1222,8 +1246,9 @@ static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd)
     info.size = dcmd_size;
     trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks);
 
-    resid = dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
-    cmd->iov_size = dcmd_size - resid;
+    dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size = dcmd_size - residual;
     return MFI_STAT_OK;
 }
 
@@ -1233,7 +1258,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
     struct mfi_ld_info *info = cmd->iov_buf;
     size_t dcmd_size = sizeof(struct mfi_ld_info);
     uint8_t cdb[6];
-    ssize_t len, resid;
+    ssize_t len;
+    dma_addr_t residual;
     uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
     uint64_t ld_size;
 
@@ -1272,9 +1298,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
     info->ld_config.span[0].num_blocks = info->size;
     info->ld_config.span[0].array_ref = cpu_to_le16(sdev_id);
 
-    resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(cmd->iov_buf, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
     g_free(cmd->iov_buf);
-    cmd->iov_size = dcmd_size - resid;
+    cmd->iov_size = dcmd_size - residual;
     cmd->iov_buf = NULL;
     return MFI_STAT_OK;
 }
@@ -1317,6 +1344,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
     struct mfi_config_data *info;
     int num_pd_disks = 0, array_offset, ld_offset;
     BusChild *kid;
+    dma_addr_t residual;
 
     if (cmd->iov_size > 4096) {
         return MFI_STAT_INVALID_PARAMETER;
@@ -1391,7 +1419,9 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
         ld_offset += sizeof(struct mfi_ld_config);
     }
 
-    cmd->iov_size -= dma_buf_read(data, info->size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(data, info->size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -1399,6 +1429,7 @@ static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd)
 {
     struct mfi_ctrl_props info;
     size_t dcmd_size = sizeof(info);
+    dma_addr_t residual;
 
     memset(&info, 0x0, dcmd_size);
     if (cmd->iov_size < dcmd_size) {
@@ -1421,7 +1452,9 @@ static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd)
     info.ecc_bucket_leak_rate = cpu_to_le16(1440);
     info.expose_encl_devices = 1;
 
-    cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+                 MEMTXATTRS_UNSPECIFIED);
+    cmd->iov_size -= residual;
     return MFI_STAT_OK;
 }
 
@@ -1466,7 +1499,7 @@ static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd)
                                             dcmd_size);
         return MFI_STAT_INVALID_PARAMETER;
     }
-    dma_buf_write(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+    dma_buf_write(&info, dcmd_size, NULL, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
     trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
     return MFI_STAT_OK;
 }
@@ -1606,13 +1639,13 @@ static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
 }
 
 static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
-                                        SCSIRequest *req, size_t resid)
+                                        SCSIRequest *req, dma_addr_t residual)
 {
     int retval = MFI_STAT_OK;
     int lun = req->lun;
 
     trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun);
-    cmd->iov_size -= resid;
+    cmd->iov_size -= residual;
     switch (cmd->dcmd_opcode) {
     case MFI_DCMD_PD_GET_INFO:
         retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
@@ -1854,12 +1887,12 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
     }
 }
 
-static void megasas_command_complete(SCSIRequest *req, size_t resid)
+static void megasas_command_complete(SCSIRequest *req, size_t residual)
 {
     MegasasCmd *cmd = req->hba_private;
     uint8_t cmd_status = MFI_STAT_OK;
 
-    trace_megasas_command_complete(cmd->index, req->status, resid);
+    trace_megasas_command_complete(cmd->index, req->status, residual);
 
     if (req->io_canceled) {
         return;
@@ -1869,7 +1902,7 @@ static void megasas_command_complete(SCSIRequest *req, size_t resid)
         /*
          * Internal command complete
          */
-        cmd_status = megasas_finish_internal_dcmd(cmd, req, resid);
+        cmd_status = megasas_finish_internal_dcmd(cmd, req, residual);
         if (cmd_status == MFI_STAT_INVALID_STATUS) {
             return;
         }
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 2b5e9dca31..4057e04ce8 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -760,7 +760,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
     }
 
     req->cmd = cmd;
-    req->resid = req->cmd.xfer;
+    req->residual = req->cmd.xfer;
 
     switch (buf[0]) {
     case INQUIRY:
@@ -1408,7 +1408,7 @@ void scsi_req_data(SCSIRequest *req, int len)
     trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
     assert(req->cmd.mode != SCSI_XFER_NONE);
     if (!req->sg) {
-        req->resid -= len;
+        req->residual -= len;
         req->bus->info->transfer_data(req, len);
         return;
     }
@@ -1421,9 +1421,11 @@ void scsi_req_data(SCSIRequest *req, int len)
 
     buf = scsi_req_get_buf(req);
     if (req->cmd.mode == SCSI_XFER_FROM_DEV) {
-        req->resid = dma_buf_read(buf, len, req->sg, MEMTXATTRS_UNSPECIFIED);
+        dma_buf_read(buf, len, &req->residual, req->sg,
+                     MEMTXATTRS_UNSPECIFIED);
     } else {
-        req->resid = dma_buf_write(buf, len, req->sg, MEMTXATTRS_UNSPECIFIED);
+        dma_buf_write(buf, len, &req->residual, req->sg,
+                      MEMTXATTRS_UNSPECIFIED);
     }
     scsi_req_continue(req);
 }
@@ -1512,7 +1514,7 @@ void scsi_req_complete(SCSIRequest *req, int status)
 
     scsi_req_ref(req);
     scsi_req_dequeue(req);
-    req->bus->info->complete(req, req->resid);
+    req->bus->info->complete(req, req->residual);
 
     /* Cancelled requests might end up being completed instead of cancelled */
     notifier_list_notify(&req->cancel_notifiers, req);
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index d4914178ea..9c0dc7b946 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -420,7 +420,7 @@ static void scsi_do_read(SCSIDiskReq *r, int ret)
 
     if (r->req.sg) {
         dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
-        r->req.resid -= r->req.sg->size;
+        r->req.residual -= r->req.sg->size;
         r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
                                   r->req.sg, r->sector << BDRV_SECTOR_BITS,
                                   BDRV_SECTOR_SIZE,
@@ -580,7 +580,7 @@ static void scsi_write_data(SCSIRequest *req)
 
     if (r->req.sg) {
         dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
-        r->req.resid -= r->req.sg->size;
+        r->req.residual -= r->req.sg->size;
         r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
                                   r->req.sg, r->sector << BDRV_SECTOR_BITS,
                                   BDRV_SECTOR_SIZE,
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 20f1b27377..63be794a06 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -738,6 +738,7 @@ struct MemoryRegion {
     const MemoryRegionOps *ops;
     void *opaque;
     MemoryRegion *container;
+    int mapped_via_alias; /* Mapped via an alias, container might be NULL */
     Int128 size;
     hwaddr addr;
     void (*destructor)(MemoryRegion *mr);
@@ -2296,7 +2297,8 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr);
 
 /**
  * memory_region_is_mapped: returns true if #MemoryRegion is mapped
- * into any address space.
+ * into another memory region, which does not necessarily imply that it is
+ * mapped into an address space.
  *
  * @mr: a #MemoryRegion which should be checked if it's mapped
  */
diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h
index 4b7ce8a723..0c7635dec5 100644
--- a/include/hw/pci-host/pnv_phb4.h
+++ b/include/hw/pci-host/pnv_phb4.h
@@ -84,6 +84,9 @@ struct PnvPHB4 {
 
     uint64_t version;
 
+    /* The owner PEC */
+    PnvPhb4PecState *pec;
+
     char bus_path[8];
 
     /* Main register images */
@@ -107,6 +110,29 @@ struct PnvPHB4 {
     MemoryRegion pci_mmio;
     MemoryRegion pci_io;
 
+    /* PCI registers (excluding pass-through) */
+#define PHB4_PEC_PCI_STK_REGS_COUNT  0xf
+    uint64_t pci_regs[PHB4_PEC_PCI_STK_REGS_COUNT];
+    MemoryRegion pci_regs_mr;
+
+    /* Nest registers */
+#define PHB4_PEC_NEST_STK_REGS_COUNT  0x17
+    uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT];
+    MemoryRegion nest_regs_mr;
+
+    /* PHB pass-through XSCOM */
+    MemoryRegion phb_regs_mr;
+
+    /* Memory windows from PowerBus to PHB */
+    MemoryRegion phbbar;
+    MemoryRegion intbar;
+    MemoryRegion mmbar0;
+    MemoryRegion mmbar1;
+    uint64_t mmio0_base;
+    uint64_t mmio0_size;
+    uint64_t mmio1_base;
+    uint64_t mmio1_size;
+
     /* On-chip IODA tables */
     uint64_t ioda_LIST[PNV_PHB4_MAX_LSIs];
     uint64_t ioda_MIST[PNV_PHB4_MAX_MIST];
@@ -125,8 +151,6 @@ struct PnvPHB4 {
     XiveSource xsrc;
     qemu_irq *qirqs;
 
-    PnvPhb4PecStack *stack;
-
     QLIST_HEAD(, PnvPhb4DMASpace) dma_spaces;
 };
 
@@ -140,49 +164,6 @@ extern const MemoryRegionOps pnv_phb4_xscom_ops;
 #define TYPE_PNV_PHB4_PEC "pnv-phb4-pec"
 OBJECT_DECLARE_TYPE(PnvPhb4PecState, PnvPhb4PecClass, PNV_PHB4_PEC)
 
-#define TYPE_PNV_PHB4_PEC_STACK "pnv-phb4-pec-stack"
-OBJECT_DECLARE_SIMPLE_TYPE(PnvPhb4PecStack, PNV_PHB4_PEC_STACK)
-
-/* Per-stack data */
-struct PnvPhb4PecStack {
-    DeviceState parent;
-
-    /* My own stack number */
-    uint32_t stack_no;
-
-    /* Nest registers */
-#define PHB4_PEC_NEST_STK_REGS_COUNT  0x17
-    uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT];
-    MemoryRegion nest_regs_mr;
-
-    /* PCI registers (excluding pass-through) */
-#define PHB4_PEC_PCI_STK_REGS_COUNT  0xf
-    uint64_t pci_regs[PHB4_PEC_PCI_STK_REGS_COUNT];
-    MemoryRegion pci_regs_mr;
-
-    /* PHB pass-through XSCOM */
-    MemoryRegion phb_regs_mr;
-
-    /* Memory windows from PowerBus to PHB */
-    MemoryRegion mmbar0;
-    MemoryRegion mmbar1;
-    MemoryRegion phbbar;
-    MemoryRegion intbar;
-    uint64_t mmio0_base;
-    uint64_t mmio0_size;
-    uint64_t mmio1_base;
-    uint64_t mmio1_size;
-
-    /* The owner PEC */
-    PnvPhb4PecState *pec;
-
-    /*
-     * PHB4 pointer. pnv_phb4_update_regions() needs to access
-     * the PHB4 via a PnvPhb4PecStack pointer.
-     */
-    PnvPHB4 *phb;
-};
-
 struct PnvPhb4PecState {
     DeviceState parent;
 
@@ -202,10 +183,8 @@ struct PnvPhb4PecState {
     uint64_t pci_regs[PHB4_PEC_PCI_REGS_COUNT];
     MemoryRegion pci_regs_mr;
 
-    /* Stacks */
-    #define PHB4_PEC_MAX_STACKS     3
-    uint32_t num_stacks;
-    PnvPhb4PecStack stacks[PHB4_PEC_MAX_STACKS];
+    /* PHBs */
+    uint32_t num_phbs;
 
     PnvChip *chip;
 };
@@ -223,7 +202,8 @@ struct PnvPhb4PecClass {
     const char *stk_compat;
     int stk_compat_size;
     uint64_t version;
-    const uint32_t *num_stacks;
+    const uint32_t *num_phbs;
+    const char *rp_model;
 };
 
 #endif /* PCI_HOST_PNV_PHB4_H */
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 483d5c7c72..023abc0f79 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -881,6 +881,18 @@ PCI_DMA_DEFINE_LDST(q_be, q_be, 64);
 
 #undef PCI_DMA_DEFINE_LDST
 
+/**
+ * pci_dma_map: Map device PCI address space range into host virtual address
+ * @dev: #PCIDevice to be accessed
+ * @addr: address within that device's address space
+ * @plen: pointer to length of buffer; updated on return to indicate
+ *        if only a subset of the requested range has been mapped
+ * @dir: indicates the transfer direction
+ *
+ * Return: A host pointer, or %NULL if the resources needed to
+ *         perform the mapping are exhausted (in that case *@plen
+ *         is set to zero).
+ */
 static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr,
                                 dma_addr_t *plen, DMADirection dir)
 {
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index 2ef80af6dc..1ffb367f94 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -30,7 +30,7 @@ struct SCSIRequest {
     int16_t           status;
     int16_t           host_status;
     void              *hba_private;
-    size_t            resid;
+    uint64_t          residual;
     SCSICommand       cmd;
     NotifierList      cancel_notifiers;
 
@@ -125,7 +125,7 @@ struct SCSIBusInfo {
                      void *hba_private);
     void (*transfer_data)(SCSIRequest *req, uint32_t arg);
     void (*fail)(SCSIRequest *req);
-    void (*complete)(SCSIRequest *req, size_t resid);
+    void (*complete)(SCSIRequest *req, size_t residual);
     void (*cancel)(SCSIRequest *req);
     void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense);
     QEMUSGList *(*get_sg_list)(SCSIRequest *req);
diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h
index b3faef41b2..a1ac5bc1b5 100644
--- a/include/sysemu/dma.h
+++ b/include/sysemu/dma.h
@@ -15,24 +15,11 @@
 #include "block/block.h"
 #include "block/accounting.h"
 
-typedef struct ScatterGatherEntry ScatterGatherEntry;
-
 typedef enum {
     DMA_DIRECTION_TO_DEVICE = 0,
     DMA_DIRECTION_FROM_DEVICE = 1,
 } DMADirection;
 
-struct QEMUSGList {
-    ScatterGatherEntry *sg;
-    int nsg;
-    int nalloc;
-    size_t size;
-    DeviceState *dev;
-    AddressSpace *as;
-};
-
-#ifndef CONFIG_USER_ONLY
-
 /*
  * When an IOMMU is present, bus addresses become distinct from
  * CPU/memory physical addresses and may be a different size.  Because
@@ -45,6 +32,17 @@ typedef uint64_t dma_addr_t;
 #define DMA_ADDR_BITS 64
 #define DMA_ADDR_FMT "%" PRIx64
 
+typedef struct ScatterGatherEntry ScatterGatherEntry;
+
+struct QEMUSGList {
+    ScatterGatherEntry *sg;
+    int nsg;
+    int nalloc;
+    dma_addr_t size;
+    DeviceState *dev;
+    AddressSpace *as;
+};
+
 static inline void dma_barrier(AddressSpace *as, DMADirection dir)
 {
     /*
@@ -288,7 +286,6 @@ void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint,
                       AddressSpace *as);
 void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len);
 void qemu_sglist_destroy(QEMUSGList *qsg);
-#endif
 
 typedef BlockAIOCB *DMAIOFunc(int64_t offset, QEMUIOVector *iov,
                               BlockCompletionFunc *cb, void *cb_opaque,
@@ -304,8 +301,10 @@ BlockAIOCB *dma_blk_read(BlockBackend *blk,
 BlockAIOCB *dma_blk_write(BlockBackend *blk,
                           QEMUSGList *sg, uint64_t offset, uint32_t align,
                           BlockCompletionFunc *cb, void *opaque);
-uint64_t dma_buf_read(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs);
-uint64_t dma_buf_write(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs);
+MemTxResult dma_buf_read(void *ptr, dma_addr_t len, dma_addr_t *residual,
+                         QEMUSGList *sg, MemTxAttrs attrs);
+MemTxResult dma_buf_write(void *ptr, dma_addr_t len, dma_addr_t *residual,
+                          QEMUSGList *sg, MemTxAttrs attrs);
 
 void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
                     QEMUSGList *sg, enum BlockAcctType type);
diff --git a/softmmu/dma-helpers.c b/softmmu/dma-helpers.c
index b0be156479..1c6fba6a11 100644
--- a/softmmu/dma-helpers.c
+++ b/softmmu/dma-helpers.c
@@ -294,49 +294,43 @@ BlockAIOCB *dma_blk_write(BlockBackend *blk,
 }
 
 
-static MemTxResult dma_buf_rw(void *buf, int32_t len, uint64_t *residp,
+static MemTxResult dma_buf_rw(void *buf, dma_addr_t len, dma_addr_t *residual,
                               QEMUSGList *sg, DMADirection dir,
                               MemTxAttrs attrs)
 {
     uint8_t *ptr = buf;
-    uint64_t resid;
+    dma_addr_t xresidual;
     int sg_cur_index;
     MemTxResult res = MEMTX_OK;
 
-    resid = sg->size;
+    xresidual = sg->size;
     sg_cur_index = 0;
-    len = MIN(len, resid);
+    len = MIN(len, xresidual);
     while (len > 0) {
         ScatterGatherEntry entry = sg->sg[sg_cur_index++];
-        int32_t xfer = MIN(len, entry.len);
+        dma_addr_t xfer = MIN(len, entry.len);
         res |= dma_memory_rw(sg->as, entry.base, ptr, xfer, dir, attrs);
         ptr += xfer;
         len -= xfer;
-        resid -= xfer;
+        xresidual -= xfer;
     }
 
-    if (residp) {
-        *residp = resid;
+    if (residual) {
+        *residual = xresidual;
     }
     return res;
 }
 
-uint64_t dma_buf_read(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs)
+MemTxResult dma_buf_read(void *ptr, dma_addr_t len, dma_addr_t *residual,
+                         QEMUSGList *sg, MemTxAttrs attrs)
 {
-    uint64_t resid;
-
-    dma_buf_rw(ptr, len, &resid, sg, DMA_DIRECTION_FROM_DEVICE, attrs);
-
-    return resid;
+    return dma_buf_rw(ptr, len, residual, sg, DMA_DIRECTION_FROM_DEVICE, attrs);
 }
 
-uint64_t dma_buf_write(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs)
+MemTxResult dma_buf_write(void *ptr, dma_addr_t len, dma_addr_t *residual,
+                          QEMUSGList *sg, MemTxAttrs attrs)
 {
-    uint64_t resid;
-
-    dma_buf_rw(ptr, len, &resid, sg, DMA_DIRECTION_TO_DEVICE, attrs);
-
-    return resid;
+    return dma_buf_rw(ptr, len, residual, sg, DMA_DIRECTION_TO_DEVICE, attrs);
 }
 
 void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
diff --git a/softmmu/memory.c b/softmmu/memory.c
index 7340e19ff5..678dc62f06 100644
--- a/softmmu/memory.c
+++ b/softmmu/memory.c
@@ -1444,6 +1444,11 @@ MemTxResult memory_region_dispatch_read(MemoryRegion *mr,
     unsigned size = memop_size(op);
     MemTxResult r;
 
+    if (mr->alias) {
+        return memory_region_dispatch_read(mr->alias,
+                                           mr->alias_offset + addr,
+                                           pval, op, attrs);
+    }
     if (!memory_region_access_valid(mr, addr, size, false, attrs)) {
         *pval = unassigned_mem_read(mr, addr, size);
         return MEMTX_DECODE_ERROR;
@@ -1488,6 +1493,11 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr,
 {
     unsigned size = memop_size(op);
 
+    if (mr->alias) {
+        return memory_region_dispatch_write(mr->alias,
+                                            mr->alias_offset + addr,
+                                            data, op, attrs);
+    }
     if (!memory_region_access_valid(mr, addr, size, true, attrs)) {
         unassigned_mem_write(mr, addr, data, size);
         return MEMTX_DECODE_ERROR;
@@ -2535,8 +2545,13 @@ static void memory_region_add_subregion_common(MemoryRegion *mr,
                                                hwaddr offset,
                                                MemoryRegion *subregion)
 {
+    MemoryRegion *alias;
+
     assert(!subregion->container);
     subregion->container = mr;
+    for (alias = subregion->alias; alias; alias = alias->alias) {
+        alias->mapped_via_alias++;
+    }
     subregion->addr = offset;
     memory_region_update_container_subregions(subregion);
 }
@@ -2561,9 +2576,15 @@ void memory_region_add_subregion_overlap(MemoryRegion *mr,
 void memory_region_del_subregion(MemoryRegion *mr,
                                  MemoryRegion *subregion)
 {
+    MemoryRegion *alias;
+
     memory_region_transaction_begin();
     assert(subregion->container == mr);
     subregion->container = NULL;
+    for (alias = subregion->alias; alias; alias = alias->alias) {
+        alias->mapped_via_alias--;
+        assert(alias->mapped_via_alias >= 0);
+    }
     QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link);
     memory_region_unref(subregion);
     memory_region_update_pending |= mr->enabled && subregion->enabled;
@@ -2660,7 +2681,7 @@ static FlatRange *flatview_lookup(FlatView *view, AddrRange addr)
 
 bool memory_region_is_mapped(MemoryRegion *mr)
 {
-    return mr->container ? true : false;
+    return !!mr->container || mr->mapped_via_alias;
 }
 
 /* Same as memory_region_find, but it does not add a reference to the
@@ -2773,6 +2794,8 @@ static VMChangeStateEntry *vmstate_change;
 
 void memory_global_dirty_log_start(unsigned int flags)
 {
+    unsigned int old_flags = global_dirty_tracking;
+
     if (vmstate_change) {
         qemu_del_vm_change_state_handler(vmstate_change);
         vmstate_change = NULL;
@@ -2781,15 +2804,14 @@ void memory_global_dirty_log_start(unsigned int flags)
     assert(flags && !(flags & (~GLOBAL_DIRTY_MASK)));
     assert(!(global_dirty_tracking & flags));
     global_dirty_tracking |= flags;
-
     trace_global_dirty_changed(global_dirty_tracking);
 
-    MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
-
-    /* Refresh DIRTY_MEMORY_MIGRATION bit.  */
-    memory_region_transaction_begin();
-    memory_region_update_pending = true;
-    memory_region_transaction_commit();
+    if (!old_flags) {
+        MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
+        memory_region_transaction_begin();
+        memory_region_update_pending = true;
+        memory_region_transaction_commit();
+    }
 }
 
 static void memory_global_dirty_log_do_stop(unsigned int flags)
@@ -2800,12 +2822,12 @@ static void memory_global_dirty_log_do_stop(unsigned int flags)
 
     trace_global_dirty_changed(global_dirty_tracking);
 
-    /* Refresh DIRTY_MEMORY_MIGRATION bit.  */
-    memory_region_transaction_begin();
-    memory_region_update_pending = true;
-    memory_region_transaction_commit();
-
-    MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
+    if (!global_dirty_tracking) {
+        memory_region_transaction_begin();
+        memory_region_update_pending = true;
+        memory_region_transaction_commit();
+        MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
+    }
 }
 
 static void memory_vm_change_state_handler(void *opaque, bool running,
@@ -3274,58 +3296,115 @@ static gboolean mtree_info_flatview_free(gpointer key, gpointer value,
     return true;
 }
 
-void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
+static void mtree_info_flatview(bool dispatch_tree, bool owner)
 {
-    MemoryRegionListHead ml_head;
-    MemoryRegionList *ml, *ml2;
+    struct FlatViewInfo fvi = {
+        .counter = 0,
+        .dispatch_tree = dispatch_tree,
+        .owner = owner,
+    };
     AddressSpace *as;
+    FlatView *view;
+    GArray *fv_address_spaces;
+    GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
+    AccelClass *ac = ACCEL_GET_CLASS(current_accel());
 
-    if (flatview) {
-        FlatView *view;
-        struct FlatViewInfo fvi = {
-            .counter = 0,
-            .dispatch_tree = dispatch_tree,
-            .owner = owner,
-        };
-        GArray *fv_address_spaces;
-        GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
-        AccelClass *ac = ACCEL_GET_CLASS(current_accel());
-
-        if (ac->has_memory) {
-            fvi.ac = ac;
+    if (ac->has_memory) {
+        fvi.ac = ac;
+    }
+
+    /* Gather all FVs in one table */
+    QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+        view = address_space_get_flatview(as);
+
+        fv_address_spaces = g_hash_table_lookup(views, view);
+        if (!fv_address_spaces) {
+            fv_address_spaces = g_array_new(false, false, sizeof(as));
+            g_hash_table_insert(views, view, fv_address_spaces);
         }
 
-        /* Gather all FVs in one table */
-        QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
-            view = address_space_get_flatview(as);
+        g_array_append_val(fv_address_spaces, as);
+    }
 
-            fv_address_spaces = g_hash_table_lookup(views, view);
-            if (!fv_address_spaces) {
-                fv_address_spaces = g_array_new(false, false, sizeof(as));
-                g_hash_table_insert(views, view, fv_address_spaces);
-            }
+    /* Print */
+    g_hash_table_foreach(views, mtree_print_flatview, &fvi);
 
-            g_array_append_val(fv_address_spaces, as);
-        }
+    /* Free */
+    g_hash_table_foreach_remove(views, mtree_info_flatview_free, 0);
+    g_hash_table_unref(views);
+}
+
+struct AddressSpaceInfo {
+    MemoryRegionListHead *ml_head;
+    bool owner;
+    bool disabled;
+};
 
-        /* Print */
-        g_hash_table_foreach(views, mtree_print_flatview, &fvi);
+/* Returns negative value if a < b; zero if a = b; positive value if a > b. */
+static gint address_space_compare_name(gconstpointer a, gconstpointer b)
+{
+    const AddressSpace *as_a = a;
+    const AddressSpace *as_b = b;
 
-        /* Free */
-        g_hash_table_foreach_remove(views, mtree_info_flatview_free, 0);
-        g_hash_table_unref(views);
+    return g_strcmp0(as_a->name, as_b->name);
+}
 
-        return;
-    }
+static void mtree_print_as_name(gpointer data, gpointer user_data)
+{
+    AddressSpace *as = data;
+
+    qemu_printf("address-space: %s\n", as->name);
+}
+
+static void mtree_print_as(gpointer key, gpointer value, gpointer user_data)
+{
+    MemoryRegion *mr = key;
+    GSList *as_same_root_mr_list = value;
+    struct AddressSpaceInfo *asi = user_data;
+
+    g_slist_foreach(as_same_root_mr_list, mtree_print_as_name, NULL);
+    mtree_print_mr(mr, 1, 0, asi->ml_head, asi->owner, asi->disabled);
+    qemu_printf("\n");
+}
+
+static gboolean mtree_info_as_free(gpointer key, gpointer value,
+                                   gpointer user_data)
+{
+    GSList *as_same_root_mr_list = value;
+
+    g_slist_free(as_same_root_mr_list);
+
+    return true;
+}
+
+static void mtree_info_as(bool dispatch_tree, bool owner, bool disabled)
+{
+    MemoryRegionListHead ml_head;
+    MemoryRegionList *ml, *ml2;
+    AddressSpace *as;
+    GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
+    GSList *as_same_root_mr_list;
+    struct AddressSpaceInfo asi = {
+        .ml_head = &ml_head,
+        .owner = owner,
+        .disabled = disabled,
+    };
 
     QTAILQ_INIT(&ml_head);
 
     QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
-        qemu_printf("address-space: %s\n", as->name);
-        mtree_print_mr(as->root, 1, 0, &ml_head, owner, disabled);
-        qemu_printf("\n");
+        /* Create hashtable, key=AS root MR, value = list of AS */
+        as_same_root_mr_list = g_hash_table_lookup(views, as->root);
+        as_same_root_mr_list = g_slist_insert_sorted(as_same_root_mr_list, as,
+                                                     address_space_compare_name);
+        g_hash_table_insert(views, as->root, as_same_root_mr_list);
     }
 
+    /* print address spaces */
+    g_hash_table_foreach(views, mtree_print_as, &asi);
+    g_hash_table_foreach_remove(views, mtree_info_as_free, 0);
+    g_hash_table_unref(views);
+
     /* print aliased regions */
     QTAILQ_FOREACH(ml, &ml_head, mrqueue) {
         qemu_printf("memory-region: %s\n", memory_region_name(ml->mr));
@@ -3338,6 +3417,15 @@ void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
     }
 }
 
+void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
+{
+    if (flatview) {
+        mtree_info_flatview(dispatch_tree, owner);
+    } else {
+        mtree_info_as(dispatch_tree, owner, disabled);
+    }
+}
+
 void memory_region_init_ram(MemoryRegion *mr,
                             Object *owner,
                             const char *name,
diff --git a/stubs/meson.build b/stubs/meson.build
index 71469c1d50..d359cbe1ad 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -11,7 +11,6 @@ stub_ss.add(files('icount.c'))
 stub_ss.add(files('dump.c'))
 stub_ss.add(files('error-printf.c'))
 stub_ss.add(files('fdset.c'))
-stub_ss.add(files('fw_cfg.c'))
 stub_ss.add(files('gdbstub.c'))
 stub_ss.add(files('get-vm-name.c'))
 if linux_io_uring.found()
@@ -27,7 +26,6 @@ stub_ss.add(files('migr-blocker.c'))
 stub_ss.add(files('module-opts.c'))
 stub_ss.add(files('monitor.c'))
 stub_ss.add(files('monitor-core.c'))
-stub_ss.add(files('pci-bus.c'))
 stub_ss.add(files('qemu-timer-notify-cb.c'))
 stub_ss.add(files('qmp_memory_device.c'))
 stub_ss.add(files('qmp-command-available.c'))
@@ -51,6 +49,8 @@ if have_block
   stub_ss.add(files('replay-tools.c'))
 endif
 if have_system
+  stub_ss.add(files('fw_cfg.c'))
+  stub_ss.add(files('pci-bus.c'))
   stub_ss.add(files('semihost.c'))
   stub_ss.add(files('usb-dev-stub.c'))
   stub_ss.add(files('xen-hw-stub.c'))
diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c
index c9fcb6119f..764afe5a2a 100644
--- a/target/ppc/cpu-models.c
+++ b/target/ppc/cpu-models.c
@@ -636,13 +636,13 @@
                 "PowerPC 7410 v1.3 (G4)")
     POWERPC_DEF("7410_v1.4",     CPU_POWERPC_7410_v14,               7410,
                 "PowerPC 7410 v1.4 (G4)")
-    POWERPC_DEF("7448_v1.0",     CPU_POWERPC_7448_v10,               7400,
+    POWERPC_DEF("7448_v1.0",     CPU_POWERPC_7448_v10,               7445,
                 "PowerPC 7448 v1.0 (G4)")
-    POWERPC_DEF("7448_v1.1",     CPU_POWERPC_7448_v11,               7400,
+    POWERPC_DEF("7448_v1.1",     CPU_POWERPC_7448_v11,               7445,
                 "PowerPC 7448 v1.1 (G4)")
-    POWERPC_DEF("7448_v2.0",     CPU_POWERPC_7448_v20,               7400,
+    POWERPC_DEF("7448_v2.0",     CPU_POWERPC_7448_v20,               7445,
                 "PowerPC 7448 v2.0 (G4)")
-    POWERPC_DEF("7448_v2.1",     CPU_POWERPC_7448_v21,               7400,
+    POWERPC_DEF("7448_v2.1",     CPU_POWERPC_7448_v21,               7445,
                 "PowerPC 7448 v2.1 (G4)")
     POWERPC_DEF("7450_v1.0",     CPU_POWERPC_7450_v10,               7450,
                 "PowerPC 7450 v1.0 (G4)")
@@ -750,7 +750,6 @@
 /* PowerPC CPU aliases                                                     */
 
 PowerPCCPUAlias ppc_cpu_aliases[] = {
-    { "403", "403gc" },
     { "405", "405d4" },
     { "405cr", "405crc" },
     { "405gp", "405gpd" },
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index f99cd0ea92..2560b70c5f 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1133,7 +1133,6 @@ struct CPUPPCState {
     int nb_pids;     /* Number of available PID registers */
     int tlb_type;    /* Type of TLB we're dealing with */
     ppc_tlb_t tlb;   /* TLB is optional. Allocate them only if needed */
-    target_ulong pb[4]; /* 403 dedicated access protection registers */
     bool tlb_dirty;  /* Set to non-zero when modifying TLB */
     bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */
     uint32_t tlb_need_flush; /* Delayed flush needed */
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index f9c72dcd50..d318837ea5 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -703,7 +703,6 @@ DEF_HELPER_FLAGS_2(store_hdecr, TCG_CALL_NO_RWG, void, env, tl)
 DEF_HELPER_FLAGS_2(store_vtb, TCG_CALL_NO_RWG, void, env, tl)
 DEF_HELPER_FLAGS_2(store_tbu40, TCG_CALL_NO_RWG, void, env, tl)
 DEF_HELPER_2(store_hid0_601, void, env, tl)
-DEF_HELPER_3(store_403_pbr, void, env, i32, tl)
 DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env)
 DEF_HELPER_FLAGS_2(store_40x_pit, TCG_CALL_NO_RWG, void, env, tl)
 DEF_HELPER_FLAGS_2(store_40x_tcr, TCG_CALL_NO_RWG, void, env, tl)
diff --git a/target/ppc/machine.c b/target/ppc/machine.c
index 756d8de5d8..733a22d744 100644
--- a/target/ppc/machine.c
+++ b/target/ppc/machine.c
@@ -23,117 +23,6 @@ static void post_load_update_msr(CPUPPCState *env)
     pmu_update_summaries(env);
 }
 
-static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
-{
-    PowerPCCPU *cpu = opaque;
-    CPUPPCState *env = &cpu->env;
-    unsigned int i, j;
-    target_ulong sdr1;
-    uint32_t fpscr, vscr;
-#if defined(TARGET_PPC64)
-    int32_t slb_nr;
-#endif
-    target_ulong xer;
-
-    for (i = 0; i < 32; i++) {
-        qemu_get_betls(f, &env->gpr[i]);
-    }
-#if !defined(TARGET_PPC64)
-    for (i = 0; i < 32; i++) {
-        qemu_get_betls(f, &env->gprh[i]);
-    }
-#endif
-    qemu_get_betls(f, &env->lr);
-    qemu_get_betls(f, &env->ctr);
-    for (i = 0; i < 8; i++) {
-        qemu_get_be32s(f, &env->crf[i]);
-    }
-    qemu_get_betls(f, &xer);
-    cpu_write_xer(env, xer);
-    qemu_get_betls(f, &env->reserve_addr);
-    qemu_get_betls(f, &env->msr);
-    for (i = 0; i < 4; i++) {
-        qemu_get_betls(f, &env->tgpr[i]);
-    }
-    for (i = 0; i < 32; i++) {
-        union {
-            float64 d;
-            uint64_t l;
-        } u;
-        u.l = qemu_get_be64(f);
-        *cpu_fpr_ptr(env, i) = u.d;
-    }
-    qemu_get_be32s(f, &fpscr);
-    env->fpscr = fpscr;
-    qemu_get_sbe32s(f, &env->access_type);
-#if defined(TARGET_PPC64)
-    qemu_get_betls(f, &env->spr[SPR_ASR]);
-    qemu_get_sbe32s(f, &slb_nr);
-#endif
-    qemu_get_betls(f, &sdr1);
-    for (i = 0; i < 32; i++) {
-        qemu_get_betls(f, &env->sr[i]);
-    }
-    for (i = 0; i < 2; i++) {
-        for (j = 0; j < 8; j++) {
-            qemu_get_betls(f, &env->DBAT[i][j]);
-        }
-    }
-    for (i = 0; i < 2; i++) {
-        for (j = 0; j < 8; j++) {
-            qemu_get_betls(f, &env->IBAT[i][j]);
-        }
-    }
-    qemu_get_sbe32s(f, &env->nb_tlb);
-    qemu_get_sbe32s(f, &env->tlb_per_way);
-    qemu_get_sbe32s(f, &env->nb_ways);
-    qemu_get_sbe32s(f, &env->last_way);
-    qemu_get_sbe32s(f, &env->id_tlbs);
-    qemu_get_sbe32s(f, &env->nb_pids);
-    if (env->tlb.tlb6) {
-        /* XXX assumes 6xx */
-        for (i = 0; i < env->nb_tlb; i++) {
-            qemu_get_betls(f, &env->tlb.tlb6[i].pte0);
-            qemu_get_betls(f, &env->tlb.tlb6[i].pte1);
-            qemu_get_betls(f, &env->tlb.tlb6[i].EPN);
-        }
-    }
-    for (i = 0; i < 4; i++) {
-        qemu_get_betls(f, &env->pb[i]);
-    }
-    for (i = 0; i < 1024; i++) {
-        qemu_get_betls(f, &env->spr[i]);
-    }
-    if (!cpu->vhyp) {
-        ppc_store_sdr1(env, sdr1);
-    }
-    qemu_get_be32s(f, &vscr);
-    ppc_store_vscr(env, vscr);
-    qemu_get_be64s(f, &env->spe_acc);
-    qemu_get_be32s(f, &env->spe_fscr);
-    qemu_get_betls(f, &env->msr_mask);
-    qemu_get_be32s(f, &env->flags);
-    qemu_get_sbe32s(f, &env->error_code);
-    qemu_get_be32s(f, &env->pending_interrupts);
-    qemu_get_be32s(f, &env->irq_input_state);
-    for (i = 0; i < POWERPC_EXCP_NB; i++) {
-        qemu_get_betls(f, &env->excp_vectors[i]);
-    }
-    qemu_get_betls(f, &env->excp_prefix);
-    qemu_get_betls(f, &env->ivor_mask);
-    qemu_get_betls(f, &env->ivpr_mask);
-    qemu_get_betls(f, &env->hreset_vector);
-    qemu_get_betls(f, &env->nip);
-    qemu_get_sbetl(f); /* Discard unused hflags */
-    qemu_get_sbetl(f); /* Discard unused hflags_nmsr */
-    qemu_get_sbe32(f); /* Discard unused mmu_idx */
-    qemu_get_sbe32(f); /* Discard unused power_mode */
-
-    post_load_update_msr(env);
-
-    return 0;
-}
-
 static int get_avr(QEMUFile *f, void *pv, size_t size,
                    const VMStateField *field)
 {
@@ -709,25 +598,6 @@ static bool tlbemb_needed(void *opaque)
     return env->nb_tlb && (env->tlb_type == TLB_EMB);
 }
 
-static bool pbr403_needed(void *opaque)
-{
-    PowerPCCPU *cpu = opaque;
-    uint32_t pvr = cpu->env.spr[SPR_PVR];
-
-    return (pvr & 0xffff0000) == 0x00200000;
-}
-
-static const VMStateDescription vmstate_pbr403 = {
-    .name = "cpu/pbr403",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .needed = pbr403_needed,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINTTL_ARRAY(env.pb, PowerPCCPU, 4),
-        VMSTATE_END_OF_LIST()
-    },
-};
-
 static const VMStateDescription vmstate_tlbemb = {
     .name = "cpu/tlb6xx",
     .version_id = 1,
@@ -739,13 +609,8 @@ static const VMStateDescription vmstate_tlbemb = {
                                             env.nb_tlb,
                                             vmstate_tlbemb_entry,
                                             ppcemb_tlb_t),
-        /* 403 protection registers */
         VMSTATE_END_OF_LIST()
     },
-    .subsections = (const VMStateDescription*[]) {
-        &vmstate_pbr403,
-        NULL
-    }
 };
 
 static const VMStateDescription vmstate_tlbmas_entry = {
@@ -808,7 +673,6 @@ const VMStateDescription vmstate_ppc_cpu = {
     .version_id = 5,
     .minimum_version_id = 5,
     .minimum_version_id_old = 4,
-    .load_state_old = cpu_load_old,
     .pre_save = cpu_pre_save,
     .post_load = cpu_post_load,
     .fields = (VMStateField[]) {
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index c33f5f39b9..1bcefa7c84 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -226,15 +226,6 @@ void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
     }
 }
 
-void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value)
-{
-    if (likely(env->pb[num] != value)) {
-        env->pb[num] = value;
-        /* Should be optimized */
-        tlb_flush(env_cpu(env));
-    }
-}
-
 void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val)
 {
     /* Bits 26 & 27 affect single-stepping. */
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 40232201bb..9d2adc0cae 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -911,22 +911,8 @@ void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn)
 }
 #endif
 
-/* PowerPC 403 specific registers */
-/* PBL1 / PBU1 / PBL2 / PBU2 */
+/* PIR */
 #if !defined(CONFIG_USER_ONLY)
-void spr_read_403_pbr(DisasContext *ctx, int gprn, int sprn)
-{
-    tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env,
-                  offsetof(CPUPPCState, pb[sprn - SPR_403_PBL1]));
-}
-
-void spr_write_403_pbr(DisasContext *ctx, int sprn, int gprn)
-{
-    TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1);
-    gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]);
-    tcg_temp_free_i32(t0);
-}
-
 void spr_write_pir(DisasContext *ctx, int sprn, int gprn)
 {
     TCGv t0 = tcg_temp_new();
diff --git a/tests/avocado/ppc_74xx.py b/tests/avocado/ppc_74xx.py
new file mode 100644
index 0000000000..556a9a7da9
--- /dev/null
+++ b/tests/avocado/ppc_74xx.py
@@ -0,0 +1,123 @@
+# Smoke tests for 74xx cpus (aka G4).
+#
+# Copyright (c) 2021, IBM Corp.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later.  See the COPYING file in the top-level directory.
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class ppc74xxCpu(QemuSystemTest):
+    """
+    :avocado: tags=arch:ppc
+    """
+    timeout = 5
+
+    def test_ppc_7400(self):
+        """
+        :avocado: tags=cpu:7400
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7410(self):
+        """
+        :avocado: tags=cpu:7410
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,74xx')
+
+    def test_ppc_7441(self):
+        """
+        :avocado: tags=cpu:7441
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7445(self):
+        """
+        :avocado: tags=cpu:7445
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7447(self):
+        """
+        :avocado: tags=cpu:7447
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7447a(self):
+        """
+        :avocado: tags=cpu:7447a
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7448(self):
+        """
+        :avocado: tags=cpu:7448
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,MPC86xx')
+
+    def test_ppc_7450(self):
+        """
+        :avocado: tags=cpu:7450
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7451(self):
+        """
+        :avocado: tags=cpu:7451
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7455(self):
+        """
+        :avocado: tags=cpu:7455
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7457(self):
+        """
+        :avocado: tags=cpu:7457
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+    def test_ppc_7457a(self):
+        """
+        :avocado: tags=cpu:7457a
+        """
+        self.vm.set_console()
+        self.vm.launch()
+        wait_for_console_pattern(self, '>> OpenBIOS')
+        wait_for_console_pattern(self, '>> CPU type PowerPC,G4')