aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-02-21 14:20:42 +0000
committerPeter Maydell <peter.maydell@linaro.org>2020-02-21 14:20:42 +0000
commita8c6af67e1e8d460e2c6e87070807e0a02c0fec2 (patch)
treea5279965eccf32d6e6f925b8c6cfdd509e0f9884 /hw
parent9e6b7f7eb89580cd8dfc1dd4983db9c158517d0a (diff)
parent438bafcac55308eef4f9029c94dbadd2c7ac3bb7 (diff)
downloadqemu-a8c6af67e1e8d460e2c6e87070807e0a02c0fec2.zip
qemu-a8c6af67e1e8d460e2c6e87070807e0a02c0fec2.tar.gz
qemu-a8c6af67e1e8d460e2c6e87070807e0a02c0fec2.tar.bz2
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-5.0-20200221' into staging
ppc patch queue 2020-02-21 Here's the next patch of ppc target patches. Highlights are: * Some fixes for CAS / unplug interactions * Remove some leaks of device trees * Some fixes for the PHB3 and PHB4 devices * Support for NVDIMMs on the pseries machine type * Assorted other fixes and cleanups # gpg: Signature made Fri 21 Feb 2020 03:35:40 GMT # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-5.0-20200221: hw/ppc/virtex_ml507:fix leak of fdevice tree blob spapr: Fix handling of unplugged devices during CAS and migration spapr: Don't use spapr_drc_needed() in CAS code ppc: free 'fdt' after reset the machine target/ppc/cpu.h: Clean up comments in the struct CPUPPCState definition target/ppc/cpu.h: Move fpu related members closer in cpu env target/ppc: Fix typo in comments spapr: Allow changing offset for -kernel image pnv/phb3: Add missing break statement pnv/phb4: Fix error path in pnv_pec_realize() pnv/phb3: Convert 1u to 1ull target/ppc/cpu.h: Remove duplicate includes spapr: Add Hcalls to support PAPR NVDIMM device spapr: Add NVDIMM device support nvdimm: add uuid property to nvdimm mem: move nvdimm_device_list to utilities ppc: function to setup latest class options ppc/pnv: Fix PCI_EXPRESS dependency qtest: Fix rtas dependencies spapr/rtas: Print message from "ibm,os-term" Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi/nvdimm.c28
-rw-r--r--hw/mem/Kconfig2
-rw-r--r--hw/mem/nvdimm.c40
-rw-r--r--hw/pci-host/pnv_phb3_msi.c2
-rw-r--r--hw/pci-host/pnv_phb3_pbcq.c1
-rw-r--r--hw/pci-host/pnv_phb4_pec.c2
-rw-r--r--hw/ppc/Kconfig4
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/e500.c1
-rw-r--r--hw/ppc/pnv.c2
-rw-r--r--hw/ppc/spapr.c116
-rw-r--r--hw/ppc/spapr_drc.c62
-rw-r--r--hw/ppc/spapr_events.c4
-rw-r--r--hw/ppc/spapr_hcall.c14
-rw-r--r--hw/ppc/spapr_nvdimm.c475
-rw-r--r--hw/ppc/spapr_rtas.c7
-rw-r--r--hw/ppc/virtex_ml507.c1
17 files changed, 700 insertions, 63 deletions
diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c
index 9fdad6d..5219dd0 100644
--- a/hw/acpi/nvdimm.c
+++ b/hw/acpi/nvdimm.c
@@ -32,33 +32,7 @@
#include "hw/acpi/bios-linker-loader.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/mem/nvdimm.h"
-
-static int nvdimm_device_list(Object *obj, void *opaque)
-{
- GSList **list = opaque;
-
- if (object_dynamic_cast(obj, TYPE_NVDIMM)) {
- *list = g_slist_append(*list, DEVICE(obj));
- }
-
- object_child_foreach(obj, nvdimm_device_list, opaque);
- return 0;
-}
-
-/*
- * inquire NVDIMM devices and link them into the list which is
- * returned to the caller.
- *
- * Note: it is the caller's responsibility to free the list to avoid
- * memory leak.
- */
-static GSList *nvdimm_get_device_list(void)
-{
- GSList *list = NULL;
-
- object_child_foreach(qdev_get_machine(), nvdimm_device_list, &list);
- return list;
-}
+#include "qemu/nvdimm-utils.h"
#define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig
index 620fd4c..2ad052a 100644
--- a/hw/mem/Kconfig
+++ b/hw/mem/Kconfig
@@ -8,4 +8,4 @@ config MEM_DEVICE
config NVDIMM
bool
default y
- depends on PC
+ depends on (PC || PSERIES)
diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c
index 39f1426..8e426d2 100644
--- a/hw/mem/nvdimm.c
+++ b/hw/mem/nvdimm.c
@@ -69,11 +69,51 @@ out:
error_propagate(errp, local_err);
}
+static void nvdimm_get_uuid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ NVDIMMDevice *nvdimm = NVDIMM(obj);
+ char *value = NULL;
+
+ value = qemu_uuid_unparse_strdup(&nvdimm->uuid);
+
+ visit_type_str(v, name, &value, errp);
+ g_free(value);
+}
+
+
+static void nvdimm_set_uuid(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ NVDIMMDevice *nvdimm = NVDIMM(obj);
+ Error *local_err = NULL;
+ char *value;
+
+ visit_type_str(v, name, &value, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ if (qemu_uuid_parse(value, &nvdimm->uuid) != 0) {
+ error_setg(errp, "Property '%s.%s' has invalid value",
+ object_get_typename(obj), name);
+ goto out;
+ }
+ g_free(value);
+
+out:
+ error_propagate(errp, local_err);
+}
+
+
static void nvdimm_init(Object *obj)
{
object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int",
nvdimm_get_label_size, nvdimm_set_label_size, NULL,
NULL, NULL);
+
+ object_property_add(obj, NVDIMM_UUID_PROP, "QemuUUID", nvdimm_get_uuid,
+ nvdimm_set_uuid, NULL, NULL, NULL);
}
static void nvdimm_finalize(Object *obj)
diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c
index ecfc1b2..d645468 100644
--- a/hw/pci-host/pnv_phb3_msi.c
+++ b/hw/pci-host/pnv_phb3_msi.c
@@ -220,7 +220,7 @@ static void phb3_msi_resend(ICSState *ics)
if ((msi->rba[i] & (1ull << j)) == 0) {
continue;
}
- msi->rba[i] &= ~(1u << j);
+ msi->rba[i] &= ~(1ull << j);
phb3_msi_try_send(msi, i * 64 + j, true);
}
}
diff --git a/hw/pci-host/pnv_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c
index f232228..7b9a121 100644
--- a/hw/pci-host/pnv_phb3_pbcq.c
+++ b/hw/pci-host/pnv_phb3_pbcq.c
@@ -173,6 +173,7 @@ static void pnv_pbcq_pci_xscom_write(void *opaque, hwaddr addr,
case PBCQ_PCI_BAR2:
pbcq->pci_regs[reg] = val & 0xfffffffffc000000ull;
pnv_pbcq_update_map(pbcq);
+ break;
default:
phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__,
addr, val);
diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c
index 68e1db3..911d147 100644
--- a/hw/pci-host/pnv_phb4_pec.c
+++ b/hw/pci-host/pnv_phb4_pec.c
@@ -391,7 +391,7 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp)
object_property_set_int(stk_obj, i, "stack-no", &error_abort);
object_property_set_link(stk_obj, OBJECT(pec), "pec", &error_abort);
- object_property_set_bool(stk_obj, true, "realized", errp);
+ object_property_set_bool(stk_obj, true, "realized", &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 354828b..dd86e66 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -29,6 +29,8 @@ config POWERNV
select XICS
select XIVE
select FDT_PPC
+ select PCI_EXPRESS
+ select MSI_NONBROKEN
config PPC405
bool
@@ -135,8 +137,6 @@ config XIVE_SPAPR
default y
depends on PSERIES
select XIVE
- select PCI
- select PCIE_PORT
config XIVE_KVM
bool
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index a4bac57..c3d3cc5 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -7,7 +7,7 @@ obj-$(CONFIG_PSERIES) += spapr.o spapr_caps.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o
obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o spapr_irq.o
-obj-$(CONFIG_PSERIES) += spapr_tpm_proxy.o
+obj-$(CONFIG_PSERIES) += spapr_tpm_proxy.o spapr_nvdimm.o
obj-$(CONFIG_SPAPR_RNG) += spapr_rng.o
obj-$(call land,$(CONFIG_PSERIES),$(CONFIG_LINUX)) += spapr_pci_vfio.o spapr_pci_nvlink2.o
# IBM PowerNV
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 886442e..af537bb 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -594,6 +594,7 @@ done:
cpu_physical_memory_write(addr, fdt, fdt_size);
}
ret = fdt_size;
+ g_free(fdt);
out:
g_free(pci_map);
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 139c857..e98038b 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -582,6 +582,8 @@ static void pnv_reset(MachineState *machine)
qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt));
cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
+
+ g_free(fdt);
}
static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp)
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index c9b2e0a..828e2cc 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -80,6 +80,7 @@
#include "hw/ppc/spapr_cpu_core.h"
#include "hw/mem/memory-device.h"
#include "hw/ppc/spapr_tpm_proxy.h"
+#include "hw/ppc/spapr_nvdimm.h"
#include "monitor/monitor.h"
@@ -675,6 +676,14 @@ static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt,
size = di->size;
node = di->node;
+ /*
+ * The NVDIMM area is hotpluggable after the NVDIMM is unplugged. The
+ * area is marked hotpluggable in the next iteration for the bigger
+ * chunk including the NVDIMM occupied area.
+ */
+ if (info->value->type == MEMORY_DEVICE_INFO_KIND_NVDIMM)
+ continue;
+
/* Entry for hot-pluggable area */
if (cur_addr < addr) {
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size);
@@ -1055,7 +1064,7 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt)
}
if (spapr->kernel_size) {
- uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR),
+ uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr),
cpu_to_be64(spapr->kernel_size) };
_FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel",
@@ -1243,7 +1252,8 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
/* Build memory reserve map */
if (reset) {
if (spapr->kernel_size) {
- _FDT((fdt_add_mem_rsv(fdt, KERNEL_LOAD_ADDR, spapr->kernel_size)));
+ _FDT((fdt_add_mem_rsv(fdt, spapr->kernel_addr,
+ spapr->kernel_size)));
}
if (spapr->initrd_size) {
_FDT((fdt_add_mem_rsv(fdt, spapr->initrd_base,
@@ -1266,12 +1276,19 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
}
}
+ /* NVDIMM devices */
+ if (mc->nvdimm_supported) {
+ spapr_dt_persistent_memory(fdt);
+ }
+
return fdt;
}
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
{
- return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
+ SpaprMachineState *spapr = opaque;
+
+ return (addr & 0x0fffffff) + spapr->kernel_addr;
}
static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
@@ -2629,6 +2646,7 @@ static void spapr_machine_init(MachineState *machine)
{
SpaprMachineState *spapr = SPAPR_MACHINE(machine);
SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
const char *kernel_filename = machine->kernel_filename;
const char *initrd_filename = machine->initrd_filename;
PCIHostState *phb;
@@ -2861,6 +2879,10 @@ static void spapr_machine_init(MachineState *machine)
"may run and log hardware error on the destination");
}
+ if (mc->nvdimm_supported) {
+ spapr_create_nvdimm_dr_connectors(spapr);
+ }
+
/* Set up RTAS event infrastructure */
spapr_events_init(spapr);
@@ -2948,14 +2970,15 @@ static void spapr_machine_init(MachineState *machine)
uint64_t lowaddr = 0;
spapr->kernel_size = load_elf(kernel_filename, NULL,
- translate_kernel_address, NULL,
+ translate_kernel_address, spapr,
NULL, &lowaddr, NULL, NULL, 1,
PPC_ELF_MACHINE, 0, 0);
if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) {
spapr->kernel_size = load_elf(kernel_filename, NULL,
- translate_kernel_address, NULL, NULL,
+ translate_kernel_address, spapr, NULL,
&lowaddr, NULL, NULL, 0,
- PPC_ELF_MACHINE, 0, 0);
+ PPC_ELF_MACHINE,
+ 0, 0);
spapr->kernel_le = spapr->kernel_size > 0;
}
if (spapr->kernel_size < 0) {
@@ -2969,7 +2992,7 @@ static void spapr_machine_init(MachineState *machine)
/* Try to locate the initrd in the gap between the kernel
* and the firmware. Add a bit of space just in case
*/
- spapr->initrd_base = (KERNEL_LOAD_ADDR + spapr->kernel_size
+ spapr->initrd_base = (spapr->kernel_addr + spapr->kernel_size
+ 0x1ffff) & ~0xffff;
spapr->initrd_size = load_image_targphys(initrd_filename,
spapr->initrd_base,
@@ -3215,6 +3238,18 @@ static void spapr_set_vsmt(Object *obj, Visitor *v, const char *name,
visit_type_uint32(v, name, (uint32_t *)opaque, errp);
}
+static void spapr_get_kernel_addr(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ visit_type_uint64(v, name, (uint64_t *)opaque, errp);
+}
+
+static void spapr_set_kernel_addr(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ visit_type_uint64(v, name, (uint64_t *)opaque, errp);
+}
+
static char *spapr_get_ic_mode(Object *obj, Error **errp)
{
SpaprMachineState *spapr = SPAPR_MACHINE(obj);
@@ -3320,6 +3355,14 @@ static void spapr_instance_init(Object *obj)
object_property_add_bool(obj, "vfio-no-msix-emulation",
spapr_get_msix_emulation, NULL, NULL);
+ object_property_add(obj, "kernel-addr", "uint64", spapr_get_kernel_addr,
+ spapr_set_kernel_addr, NULL, &spapr->kernel_addr,
+ &error_abort);
+ object_property_set_description(obj, "kernel-addr",
+ stringify(KERNEL_LOAD_ADDR)
+ " for -kernel is the default",
+ NULL);
+ spapr->kernel_addr = KERNEL_LOAD_ADDR;
/* The machine class defines the default interrupt controller mode */
spapr->irq = smc->irq;
object_property_add_str(obj, "ic-mode", spapr_get_ic_mode,
@@ -3430,7 +3473,8 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error *local_err = NULL;
SpaprMachineState *ms = SPAPR_MACHINE(hotplug_dev);
PCDIMMDevice *dimm = PC_DIMM(dev);
- uint64_t size, addr;
+ uint64_t size, addr, slot;
+ bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort);
@@ -3439,14 +3483,24 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
goto out;
}
- addr = object_property_get_uint(OBJECT(dimm),
- PC_DIMM_ADDR_PROP, &local_err);
- if (local_err) {
- goto out_unplug;
+ if (!is_nvdimm) {
+ addr = object_property_get_uint(OBJECT(dimm),
+ PC_DIMM_ADDR_PROP, &local_err);
+ if (local_err) {
+ goto out_unplug;
+ }
+ spapr_add_lmbs(dev, addr, size,
+ spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
+ &local_err);
+ } else {
+ slot = object_property_get_uint(OBJECT(dimm),
+ PC_DIMM_SLOT_PROP, &local_err);
+ if (local_err) {
+ goto out_unplug;
+ }
+ spapr_add_nvdimm(dev, slot, &local_err);
}
- spapr_add_lmbs(dev, addr, size, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
- &local_err);
if (local_err) {
goto out_unplug;
}
@@ -3464,6 +3518,8 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
{
const SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev);
SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev);
+ const MachineClass *mc = MACHINE_CLASS(smc);
+ bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
PCDIMMDevice *dimm = PC_DIMM(dev);
Error *local_err = NULL;
uint64_t size;
@@ -3475,16 +3531,27 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
+ if (is_nvdimm && !mc->nvdimm_supported) {
+ error_setg(errp, "NVDIMM hotplug not supported for this machine");
+ return;
+ }
+
size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
- if (size % SPAPR_MEMORY_BLOCK_SIZE) {
+ if (!is_nvdimm && size % SPAPR_MEMORY_BLOCK_SIZE) {
error_setg(errp, "Hotplugged memory size must be a multiple of "
- "%" PRIu64 " MB", SPAPR_MEMORY_BLOCK_SIZE / MiB);
+ "%" PRIu64 " MB", SPAPR_MEMORY_BLOCK_SIZE / MiB);
return;
+ } else if (is_nvdimm) {
+ spapr_nvdimm_validate_opts(NVDIMM(dev), size, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
}
memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP,
@@ -3624,6 +3691,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
int i;
SpaprDrc *drc;
+ if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
+ error_setg(&local_err,
+ "nvdimm device hot unplug is not supported yet.");
+ goto out;
+ }
+
size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &error_abort);
nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE;
@@ -4418,6 +4491,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
smc->update_dt_enabled = true;
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0");
mc->has_hotpluggable_cpus = true;
+ mc->nvdimm_supported = true;
smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED;
fwc->get_dev_path = spapr_get_fw_dev_path;
nc->nmi_monitor_handler = spapr_nmi;
@@ -4485,6 +4559,12 @@ static const TypeInfo spapr_machine_info = {
},
};
+static void spapr_machine_latest_class_options(MachineClass *mc)
+{
+ mc->alias = "pseries";
+ mc->is_default = 1;
+}
+
#define DEFINE_SPAPR_MACHINE(suffix, verstr, latest) \
static void spapr_machine_##suffix##_class_init(ObjectClass *oc, \
void *data) \
@@ -4492,8 +4572,7 @@ static const TypeInfo spapr_machine_info = {
MachineClass *mc = MACHINE_CLASS(oc); \
spapr_machine_##suffix##_class_options(mc); \
if (latest) { \
- mc->alias = "pseries"; \
- mc->is_default = 1; \
+ spapr_machine_latest_class_options(mc); \
} \
} \
static const TypeInfo spapr_machine_##suffix##_info = { \
@@ -4528,6 +4607,7 @@ static void spapr_machine_4_2_class_options(MachineClass *mc)
compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len);
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF;
smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_OFF;
+ mc->nvdimm_supported = false;
}
DEFINE_SPAPR_MACHINE(4_2, "4.2", false);
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 17aeac3..e373d34 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -22,6 +22,7 @@
#include "qemu/error-report.h"
#include "hw/ppc/spapr.h" /* for RTAS return codes */
#include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */
+#include "hw/ppc/spapr_nvdimm.h"
#include "sysemu/device_tree.h"
#include "sysemu/reset.h"
#include "trace.h"
@@ -455,21 +456,46 @@ void spapr_drc_reset(SpaprDrc *drc)
}
}
-bool spapr_drc_needed(void *opaque)
+static bool spapr_drc_unplug_requested_needed(void *opaque)
+{
+ return spapr_drc_unplug_requested(opaque);
+}
+
+static const VMStateDescription vmstate_spapr_drc_unplug_requested = {
+ .name = "spapr_drc/unplug_requested",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = spapr_drc_unplug_requested_needed,
+ .fields = (VMStateField []) {
+ VMSTATE_BOOL(unplug_requested, SpaprDrc),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+bool spapr_drc_transient(SpaprDrc *drc)
{
- SpaprDrc *drc = (SpaprDrc *)opaque;
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
- /* If no dev is plugged in there is no need to migrate the DRC state */
+ /*
+ * If no dev is plugged in there is no need to migrate the DRC state
+ * nor to reset the DRC at CAS.
+ */
if (!drc->dev) {
return false;
}
/*
- * We need to migrate the state if it's not equal to the expected
- * long-term state, which is the same as the coldplugged initial
- * state */
- return (drc->state != drck->ready_state);
+ * We need to reset the DRC at CAS or to migrate the DRC state if it's
+ * not equal to the expected long-term state, which is the same as the
+ * coldplugged initial state, or if an unplug request is pending.
+ */
+ return drc->state != drck->ready_state ||
+ spapr_drc_unplug_requested(drc);
+}
+
+static bool spapr_drc_needed(void *opaque)
+{
+ return spapr_drc_transient(opaque);
}
static const VMStateDescription vmstate_spapr_drc = {
@@ -480,6 +506,10 @@ static const VMStateDescription vmstate_spapr_drc = {
.fields = (VMStateField []) {
VMSTATE_UINT32(state, SpaprDrc),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_spapr_drc_unplug_requested,
+ NULL
}
};
@@ -709,6 +739,17 @@ static void spapr_drc_phb_class_init(ObjectClass *k, void *data)
drck->dt_populate = spapr_phb_dt_populate;
}
+static void spapr_drc_pmem_class_init(ObjectClass *k, void *data)
+{
+ SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
+
+ drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PMEM;
+ drck->typename = "PMEM";
+ drck->drc_name_prefix = "PMEM ";
+ drck->release = NULL;
+ drck->dt_populate = spapr_pmem_dt_populate;
+}
+
static const TypeInfo spapr_dr_connector_info = {
.name = TYPE_SPAPR_DR_CONNECTOR,
.parent = TYPE_DEVICE,
@@ -759,6 +800,12 @@ static const TypeInfo spapr_drc_phb_info = {
.class_init = spapr_drc_phb_class_init,
};
+static const TypeInfo spapr_drc_pmem_info = {
+ .name = TYPE_SPAPR_DRC_PMEM,
+ .parent = TYPE_SPAPR_DRC_LOGICAL,
+ .class_init = spapr_drc_pmem_class_init,
+};
+
/* helper functions for external users */
SpaprDrc *spapr_drc_by_index(uint32_t index)
@@ -1230,6 +1277,7 @@ static void spapr_drc_register_types(void)
type_register_static(&spapr_drc_pci_info);
type_register_static(&spapr_drc_lmb_info);
type_register_static(&spapr_drc_phb_info);
+ type_register_static(&spapr_drc_pmem_info);
spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
rtas_set_indicator);
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 884e455..8b32b7e 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -196,6 +196,7 @@ struct rtas_event_log_v6_hp {
#define RTAS_LOG_V6_HP_TYPE_SLOT 3
#define RTAS_LOG_V6_HP_TYPE_PHB 4
#define RTAS_LOG_V6_HP_TYPE_PCI 5
+#define RTAS_LOG_V6_HP_TYPE_PMEM 6
uint8_t hotplug_action;
#define RTAS_LOG_V6_HP_ACTION_ADD 1
#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
@@ -631,6 +632,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
case SPAPR_DR_CONNECTOR_TYPE_PHB:
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB;
break;
+ case SPAPR_DR_CONNECTOR_TYPE_PMEM:
+ hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PMEM;
+ break;
default:
/* we shouldn't be signaling hotplug events for resources
* that don't support them
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index b8bb66b..6db3dbd 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1640,20 +1640,24 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu,
return best_compat;
}
-static bool spapr_hotplugged_dev_before_cas(void)
+static bool spapr_transient_dev_before_cas(void)
{
- Object *drc_container, *obj;
+ Object *drc_container;
ObjectProperty *prop;
ObjectPropertyIterator iter;
drc_container = container_get(object_get_root(), "/dr-connector");
object_property_iter_init(&iter, drc_container);
while ((prop = object_property_iter_next(&iter))) {
+ SpaprDrc *drc;
+
if (!strstart(prop->type, "link<", NULL)) {
continue;
}
- obj = object_property_get_link(drc_container, prop->name, NULL);
- if (spapr_drc_needed(obj)) {
+ drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container,
+ prop->name, NULL));
+
+ if (spapr_drc_transient(drc)) {
return true;
}
}
@@ -1830,7 +1834,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
spapr_irq_update_active_intc(spapr);
- if (spapr_hotplugged_dev_before_cas()) {
+ if (spapr_transient_dev_before_cas()) {
spapr->cas_reboot = true;
}
diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c
new file mode 100644
index 0000000..74eeb8b
--- /dev/null
+++ b/hw/ppc/spapr_nvdimm.c
@@ -0,0 +1,475 @@
+/*
+ * QEMU PAPR Storage Class Memory Interfaces
+ *
+ * Copyright (c) 2019-2020, IBM Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/ppc/spapr_drc.h"
+#include "hw/ppc/spapr_nvdimm.h"
+#include "hw/mem/nvdimm.h"
+#include "qemu/nvdimm-utils.h"
+#include "hw/ppc/fdt.h"
+#include "qemu/range.h"
+
+void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size,
+ Error **errp)
+{
+ char *uuidstr = NULL;
+ QemuUUID uuid;
+
+ if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) {
+ error_setg(errp, "NVDIMM memory size excluding the label area"
+ " must be a multiple of %" PRIu64 "MB",
+ SPAPR_MINIMUM_SCM_BLOCK_SIZE / MiB);
+ return;
+ }
+
+ uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, NULL);
+ qemu_uuid_parse(uuidstr, &uuid);
+ g_free(uuidstr);
+
+ if (qemu_uuid_is_null(&uuid)) {
+ error_setg(errp, "NVDIMM device requires the uuid to be set");
+ return;
+ }
+}
+
+
+void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp)
+{
+ SpaprDrc *drc;
+ bool hotplugged = spapr_drc_hotplugged(dev);
+ Error *local_err = NULL;
+
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot);
+ g_assert(drc);
+
+ spapr_drc_attach(drc, dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (hotplugged) {
+ spapr_hotplug_req_add_by_index(drc);
+ }
+}
+
+int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
+ void *fdt, int *fdt_start_offset, Error **errp)
+{
+ NVDIMMDevice *nvdimm = NVDIMM(drc->dev);
+
+ *fdt_start_offset = spapr_dt_nvdimm(fdt, 0, nvdimm);
+
+ return 0;
+}
+
+void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr)
+{
+ MachineState *machine = MACHINE(spapr);
+ int i;
+
+ for (i = 0; i < machine->ram_slots; i++) {
+ spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_PMEM, i);
+ }
+}
+
+
+int spapr_dt_nvdimm(void *fdt, int parent_offset,
+ NVDIMMDevice *nvdimm)
+{
+ int child_offset;
+ char *buf;
+ SpaprDrc *drc;
+ uint32_t drc_idx;
+ uint32_t node = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_NODE_PROP,
+ &error_abort);
+ uint64_t slot = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_SLOT_PROP,
+ &error_abort);
+ uint32_t associativity[] = {
+ cpu_to_be32(0x4), /* length */
+ cpu_to_be32(0x0), cpu_to_be32(0x0),
+ cpu_to_be32(0x0), cpu_to_be32(node)
+ };
+ uint64_t lsize = nvdimm->label_size;
+ uint64_t size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP,
+ NULL);
+
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot);
+ g_assert(drc);
+
+ drc_idx = spapr_drc_index(drc);
+
+ buf = g_strdup_printf("ibm,pmemory@%x", drc_idx);
+ child_offset = fdt_add_subnode(fdt, parent_offset, buf);
+ g_free(buf);
+
+ _FDT(child_offset);
+
+ _FDT((fdt_setprop_cell(fdt, child_offset, "reg", drc_idx)));
+ _FDT((fdt_setprop_string(fdt, child_offset, "compatible", "ibm,pmemory")));
+ _FDT((fdt_setprop_string(fdt, child_offset, "device_type", "ibm,pmemory")));
+
+ _FDT((fdt_setprop(fdt, child_offset, "ibm,associativity", associativity,
+ sizeof(associativity))));
+
+ buf = qemu_uuid_unparse_strdup(&nvdimm->uuid);
+ _FDT((fdt_setprop_string(fdt, child_offset, "ibm,unit-guid", buf)));
+ g_free(buf);
+
+ _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,my-drc-index", drc_idx)));
+
+ _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,block-size",
+ SPAPR_MINIMUM_SCM_BLOCK_SIZE)));
+ _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,number-of-blocks",
+ size / SPAPR_MINIMUM_SCM_BLOCK_SIZE)));
+ _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,metadata-size", lsize)));
+
+ _FDT((fdt_setprop_string(fdt, child_offset, "ibm,pmem-application",
+ "operating-system")));
+ _FDT(fdt_setprop(fdt, child_offset, "ibm,cache-flush-required", NULL, 0));
+
+ return child_offset;
+}
+
+void spapr_dt_persistent_memory(void *fdt)
+{
+ int offset = fdt_subnode_offset(fdt, 0, "persistent-memory");
+ GSList *iter, *nvdimms = nvdimm_get_device_list();
+
+ if (offset < 0) {
+ offset = fdt_add_subnode(fdt, 0, "persistent-memory");
+ _FDT(offset);
+ _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1)));
+ _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0)));
+ _FDT((fdt_setprop_string(fdt, offset, "device_type",
+ "ibm,persistent-memory")));
+ }
+
+ /* Create DT entries for cold plugged NVDIMM devices */
+ for (iter = nvdimms; iter; iter = iter->next) {
+ NVDIMMDevice *nvdimm = iter->data;
+
+ spapr_dt_nvdimm(fdt, offset, nvdimm);
+ }
+ g_slist_free(nvdimms);
+
+ return;
+}
+
+static target_ulong h_scm_read_metadata(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ uint32_t drc_index = args[0];
+ uint64_t offset = args[1];
+ uint64_t len = args[2];
+ SpaprDrc *drc = spapr_drc_by_index(drc_index);
+ NVDIMMDevice *nvdimm;
+ NVDIMMClass *ddc;
+ uint64_t data = 0;
+ uint8_t buf[8] = { 0 };
+
+ if (!drc || !drc->dev ||
+ spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) {
+ return H_PARAMETER;
+ }
+
+ if (len != 1 && len != 2 &&
+ len != 4 && len != 8) {
+ return H_P3;
+ }
+
+ nvdimm = NVDIMM(drc->dev);
+ if ((offset + len < offset) ||
+ (nvdimm->label_size < len + offset)) {
+ return H_P2;
+ }
+
+ ddc = NVDIMM_GET_CLASS(nvdimm);
+ ddc->read_label_data(nvdimm, buf, len, offset);
+
+ switch (len) {
+ case 1:
+ data = ldub_p(buf);
+ break;
+ case 2:
+ data = lduw_be_p(buf);
+ break;
+ case 4:
+ data = ldl_be_p(buf);
+ break;
+ case 8:
+ data = ldq_be_p(buf);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ args[0] = data;
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_scm_write_metadata(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ uint32_t drc_index = args[0];
+ uint64_t offset = args[1];
+ uint64_t data = args[2];
+ uint64_t len = args[3];
+ SpaprDrc *drc = spapr_drc_by_index(drc_index);
+ NVDIMMDevice *nvdimm;
+ NVDIMMClass *ddc;
+ uint8_t buf[8] = { 0 };
+
+ if (!drc || !drc->dev ||
+ spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) {
+ return H_PARAMETER;
+ }
+
+ if (len != 1 && len != 2 &&
+ len != 4 && len != 8) {
+ return H_P4;
+ }
+
+ nvdimm = NVDIMM(drc->dev);
+ if ((offset + len < offset) ||
+ (nvdimm->label_size < len + offset)) {
+ return H_P2;
+ }
+
+ switch (len) {
+ case 1:
+ if (data & 0xffffffffffffff00) {
+ return H_P2;
+ }
+ stb_p(buf, data);
+ break;
+ case 2:
+ if (data & 0xffffffffffff0000) {
+ return H_P2;
+ }
+ stw_be_p(buf, data);
+ break;
+ case 4:
+ if (data & 0xffffffff00000000) {
+ return H_P2;
+ }
+ stl_be_p(buf, data);
+ break;
+ case 8:
+ stq_be_p(buf, data);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ ddc = NVDIMM_GET_CLASS(nvdimm);
+ ddc->write_label_data(nvdimm, buf, len, offset);
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_scm_bind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ uint32_t drc_index = args[0];
+ uint64_t starting_idx = args[1];
+ uint64_t no_of_scm_blocks_to_bind = args[2];
+ uint64_t target_logical_mem_addr = args[3];
+ uint64_t continue_token = args[4];
+ uint64_t size;
+ uint64_t total_no_of_scm_blocks;
+ SpaprDrc *drc = spapr_drc_by_index(drc_index);
+ hwaddr addr;
+ NVDIMMDevice *nvdimm;
+
+ if (!drc || !drc->dev ||
+ spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) {
+ return H_PARAMETER;
+ }
+
+ /*
+ * Currently continue token should be zero qemu has already bound
+ * everything and this hcall doesnt return H_BUSY.
+ */
+ if (continue_token > 0) {
+ return H_P5;
+ }
+
+ /* Currently qemu assigns the address. */
+ if (target_logical_mem_addr != 0xffffffffffffffff) {
+ return H_OVERLAP;
+ }
+
+ nvdimm = NVDIMM(drc->dev);
+
+ size = object_property_get_uint(OBJECT(nvdimm),
+ PC_DIMM_SIZE_PROP, &error_abort);
+
+ total_no_of_scm_blocks = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE;
+
+ if (starting_idx > total_no_of_scm_blocks) {
+ return H_P2;
+ }
+
+ if (((starting_idx + no_of_scm_blocks_to_bind) < starting_idx) ||
+ ((starting_idx + no_of_scm_blocks_to_bind) > total_no_of_scm_blocks)) {
+ return H_P3;
+ }
+
+ addr = object_property_get_uint(OBJECT(nvdimm),
+ PC_DIMM_ADDR_PROP, &error_abort);
+
+ addr += starting_idx * SPAPR_MINIMUM_SCM_BLOCK_SIZE;
+
+ /* Already bound, Return target logical address in R5 */
+ args[1] = addr;
+ args[2] = no_of_scm_blocks_to_bind;
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_scm_unbind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ uint32_t drc_index = args[0];
+ uint64_t starting_scm_logical_addr = args[1];
+ uint64_t no_of_scm_blocks_to_unbind = args[2];
+ uint64_t continue_token = args[3];
+ uint64_t size_to_unbind;
+ Range blockrange = range_empty;
+ Range nvdimmrange = range_empty;
+ SpaprDrc *drc = spapr_drc_by_index(drc_index);
+ NVDIMMDevice *nvdimm;
+ uint64_t size, addr;
+
+ if (!drc || !drc->dev ||
+ spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) {
+ return H_PARAMETER;
+ }
+
+ /* continue_token should be zero as this hcall doesn't return H_BUSY. */
+ if (continue_token > 0) {
+ return H_P4;
+ }
+
+ /* Check if starting_scm_logical_addr is block aligned */
+ if (!QEMU_IS_ALIGNED(starting_scm_logical_addr,
+ SPAPR_MINIMUM_SCM_BLOCK_SIZE)) {
+ return H_P2;
+ }
+
+ size_to_unbind = no_of_scm_blocks_to_unbind * SPAPR_MINIMUM_SCM_BLOCK_SIZE;
+ if (no_of_scm_blocks_to_unbind == 0 || no_of_scm_blocks_to_unbind !=
+ size_to_unbind / SPAPR_MINIMUM_SCM_BLOCK_SIZE) {
+ return H_P3;
+ }
+
+ nvdimm = NVDIMM(drc->dev);
+ size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP,
+ &error_abort);
+ addr = object_property_get_int(OBJECT(nvdimm), PC_DIMM_ADDR_PROP,
+ &error_abort);
+
+ range_init_nofail(&nvdimmrange, addr, size);
+ range_init_nofail(&blockrange, starting_scm_logical_addr, size_to_unbind);
+
+ if (!range_contains_range(&nvdimmrange, &blockrange)) {
+ return H_P3;
+ }
+
+ args[1] = no_of_scm_blocks_to_unbind;
+
+ /* let unplug take care of actual unbind */
+ return H_SUCCESS;
+}
+
+#define H_UNBIND_SCOPE_ALL 0x1
+#define H_UNBIND_SCOPE_DRC 0x2
+
+static target_ulong h_scm_unbind_all(PowerPCCPU *cpu, SpaprMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ uint64_t target_scope = args[0];
+ uint32_t drc_index = args[1];
+ uint64_t continue_token = args[2];
+ NVDIMMDevice *nvdimm;
+ uint64_t size;
+ uint64_t no_of_scm_blocks_unbound = 0;
+
+ /* continue_token should be zero as this hcall doesn't return H_BUSY. */
+ if (continue_token > 0) {
+ return H_P4;
+ }
+
+ if (target_scope == H_UNBIND_SCOPE_DRC) {
+ SpaprDrc *drc = spapr_drc_by_index(drc_index);
+
+ if (!drc || !drc->dev ||
+ spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) {
+ return H_P2;
+ }
+
+ nvdimm = NVDIMM(drc->dev);
+ size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP,
+ &error_abort);
+
+ no_of_scm_blocks_unbound = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE;
+ } else if (target_scope == H_UNBIND_SCOPE_ALL) {
+ GSList *list, *nvdimms;
+
+ nvdimms = nvdimm_get_device_list();
+ for (list = nvdimms; list; list = list->next) {
+ nvdimm = list->data;
+ size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP,
+ &error_abort);
+
+ no_of_scm_blocks_unbound += size / SPAPR_MINIMUM_SCM_BLOCK_SIZE;
+ }
+ g_slist_free(nvdimms);
+ } else {
+ return H_PARAMETER;
+ }
+
+ args[1] = no_of_scm_blocks_unbound;
+
+ /* let unplug take care of actual unbind */
+ return H_SUCCESS;
+}
+
+static void spapr_scm_register_types(void)
+{
+ /* qemu/scm specific hcalls */
+ spapr_register_hypercall(H_SCM_READ_METADATA, h_scm_read_metadata);
+ spapr_register_hypercall(H_SCM_WRITE_METADATA, h_scm_write_metadata);
+ spapr_register_hypercall(H_SCM_BIND_MEM, h_scm_bind_mem);
+ spapr_register_hypercall(H_SCM_UNBIND_MEM, h_scm_unbind_mem);
+ spapr_register_hypercall(H_SCM_UNBIND_ALL, h_scm_unbind_all);
+}
+
+type_init(spapr_scm_register_types)
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 883fe28..656fdd2 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -345,6 +345,13 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu,
target_ulong args,
uint32_t nret, target_ulong rets)
{
+ target_ulong msgaddr = rtas_ld(args, 0);
+ char msg[512];
+
+ cpu_physical_memory_read(msgaddr, msg, sizeof(msg) - 1);
+ msg[sizeof(msg) - 1] = 0;
+
+ error_report("OS terminated: %s", msg);
qemu_system_guest_panicked(NULL);
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index 91dd00e..4eef700 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -188,6 +188,7 @@ static int xilinx_load_device_tree(hwaddr addr,
if (r < 0)
fprintf(stderr, "couldn't set /chosen/bootargs\n");
cpu_physical_memory_write(addr, fdt, fdt_size);
+ g_free(fdt);
return fdt_size;
}