summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-03-18 15:07:57 +0000
committerPeter Maydell <peter.maydell@linaro.org>2020-03-18 15:07:57 +0000
commitb319df553707a3d44c7d027a5d5562f672a768a9 (patch)
tree6a23d058509b70cf64d7c87ca96a252d3409e5dc
parentd649689a8ecb2e276cc20d3af6d416e3c299cb17 (diff)
parent6961eae79f58385482775dc0a6c3d553f633662d (diff)
downloadqemu-b319df553707a3d44c7d027a5d5562f672a768a9.zip
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-5.0-20200317' into staging
ppc patch queue 2020-03-17 Here's my final pull request for the qemu-5.0 soft freeze. Sorry this is just under the wire - I hit some last minute problems that took a while to fix up and retest. Highlights are: * Numerous fixes for the FWNMI feature * A handful of cleanups to the device tree construction code * Numerous fixes for the spapr-vscsi device * A number of fixes and cleanups for real mode (MMU off) softmmu handling * Fixes for handling of the PAPR RMA * Better handling of hotplug/unplug events during boot * Assorted other fixes # gpg: Signature made Tue 17 Mar 2020 09:55:07 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-20200317: (45 commits) pseries: Update SLOF firmware image ppc/spapr: Ignore common "ibm,nmi-interlock" Linux bug ppc/spapr: Implement FWNMI System Reset delivery target/ppc: allow ppc_cpu_do_system_reset to take an alternate vector ppc/spapr: Allow FWNMI on TCG ppc/spapr: Fix FWNMI machine check interrupt delivery ppc/spapr: Add FWNMI System Reset state ppc/spapr: Change FWNMI names ppc/spapr: Fix FWNMI machine check failure handling spapr: Rename DT functions to newer naming convention spapr: Move creation of ibm,architecture-vec-5 property spapr: Move creation of ibm,dynamic-reconfiguration-memory dt node spapr/rtas: Reserve space for RTAS blob and log pseries: Update SLOF firmware image ppc/spapr: Move GPRs setup to one place target/ppc: Fix rlwinm on ppc64 spapr/xive: use SPAPR_IRQ_IPI to define IPI ranges exposed to the guest hw/scsi/spapr_vscsi: Convert debug fprintf() to trace event hw/scsi/spapr_vscsi: Prevent buffer overflow hw/scsi/spapr_vscsi: Do not mix SRP IU size with DMA buffer size ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--docs/system/deprecated.rst7
-rw-r--r--hw/intc/spapr_xive.c4
-rw-r--r--hw/ppc/pnv_lpc.c2
-rw-r--r--hw/ppc/spapr.c848
-rw-r--r--hw/ppc/spapr_caps.c19
-rw-r--r--hw/ppc/spapr_cpu_core.c16
-rw-r--r--hw/ppc/spapr_events.c51
-rw-r--r--hw/ppc/spapr_hcall.c15
-rw-r--r--hw/ppc/spapr_nvdimm.c7
-rw-r--r--hw/ppc/spapr_ovec.c4
-rw-r--r--hw/ppc/spapr_rtas.c45
-rw-r--r--hw/scsi/spapr_vscsi.c72
-rw-r--r--hw/scsi/trace-events1
-rw-r--r--hw/scsi/viosrp.h3
-rw-r--r--include/hw/ppc/spapr.h34
-rw-r--r--include/hw/ppc/spapr_cpu_core.h4
-rw-r--r--include/hw/ppc/spapr_ovec.h4
-rw-r--r--pc-bios/README2
-rw-r--r--pc-bios/slof.binbin931032 -> 965008 bytes
m---------roms/SLOF0
-rw-r--r--target/ppc/cpu-qom.h1
-rw-r--r--target/ppc/cpu.h28
-rw-r--r--target/ppc/excp_helper.c79
-rw-r--r--target/ppc/kvm.c5
-rw-r--r--target/ppc/kvm_ppc.h7
-rw-r--r--target/ppc/mmu-hash64.c319
-rw-r--r--target/ppc/translate.c20
-rw-r--r--target/ppc/translate_init.inc.c116
-rw-r--r--tests/qtest/libqos/libqos-spapr.h3
29 files changed, 863 insertions, 853 deletions
diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
index 0838338d8f..412b1b166d 100644
--- a/docs/system/deprecated.rst
+++ b/docs/system/deprecated.rst
@@ -289,6 +289,13 @@ The RISC-V no MMU cpus have been depcreated. The two CPUs: ``rv32imacu-nommu`` a
``rv64imacu-nommu`` should no longer be used. Instead the MMU status can be specified
via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs.
+``compat`` property of server class POWER CPUs (since 5.0)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+The ``compat`` property used to set backwards compatibility modes for
+the processor has been deprecated. The ``max-cpu-compat`` property of
+the ``pseries`` machine type should be used instead.
+
System emulator devices
-----------------------
diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c
index 20c8155557..6608d7220a 100644
--- a/hw/intc/spapr_xive.c
+++ b/hw/intc/spapr_xive.c
@@ -677,8 +677,8 @@ static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers,
uint64_t timas[2 * 2];
/* Interrupt number ranges for the IPIs */
uint32_t lisn_ranges[] = {
- cpu_to_be32(0),
- cpu_to_be32(nr_servers),
+ cpu_to_be32(SPAPR_IRQ_IPI),
+ cpu_to_be32(SPAPR_IRQ_IPI + nr_servers),
};
/*
* EQ size - the sizes of pages supported by the system 4K, 64K,
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index f150deca34..b5ffa48dac 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -829,7 +829,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
bool hostboot_mode = !!pnv->fw_load_addr;
/* let isa_bus_new() create its own bridge on SysBus otherwise
- * devices speficied on the command line won't find the bus and
+ * devices specified on the command line won't find the bus and
* will fail to create.
*/
isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err);
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 41c0f2401f..9a2bd501aa 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -103,7 +103,7 @@
#define FW_OVERHEAD 0x2800000
#define KERNEL_LOAD_ADDR FW_MAX_SIZE
-#define MIN_RMA_SLOF 128UL
+#define MIN_RMA_SLOF (128 * MiB)
#define PHANDLE_INTC 0x00001111
@@ -217,10 +217,9 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu)
sizeof(associativity));
}
-/* Populate the "ibm,pa-features" property */
-static void spapr_populate_pa_features(SpaprMachineState *spapr,
- PowerPCCPU *cpu,
- void *fdt, int offset)
+static void spapr_dt_pa_features(SpaprMachineState *spapr,
+ PowerPCCPU *cpu,
+ void *fdt, int offset)
{
uint8_t pa_features_206[] = { 6, 0,
0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 };
@@ -315,8 +314,8 @@ static void add_str(GString *s, const gchar *s1)
g_string_append_len(s, s1, strlen(s1) + 1);
}
-static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
- hwaddr size)
+static int spapr_dt_memory_node(void *fdt, int nodeid, hwaddr start,
+ hwaddr size)
{
uint32_t associativity[] = {
cpu_to_be32(0x4), /* length */
@@ -341,257 +340,6 @@ static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
return off;
}
-static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt)
-{
- MachineState *machine = MACHINE(spapr);
- hwaddr mem_start, node_size;
- int i, nb_nodes = machine->numa_state->num_nodes;
- NodeInfo *nodes = machine->numa_state->nodes;
-
- for (i = 0, mem_start = 0; i < nb_nodes; ++i) {
- if (!nodes[i].node_mem) {
- continue;
- }
- if (mem_start >= machine->ram_size) {
- node_size = 0;
- } else {
- node_size = nodes[i].node_mem;
- if (node_size > machine->ram_size - mem_start) {
- node_size = machine->ram_size - mem_start;
- }
- }
- if (!mem_start) {
- /* spapr_machine_init() checks for rma_size <= node0_size
- * already */
- spapr_populate_memory_node(fdt, i, 0, spapr->rma_size);
- mem_start += spapr->rma_size;
- node_size -= spapr->rma_size;
- }
- for ( ; node_size; ) {
- hwaddr sizetmp = pow2floor(node_size);
-
- /* mem_start != 0 here */
- if (ctzl(mem_start) < ctzl(sizetmp)) {
- sizetmp = 1ULL << ctzl(mem_start);
- }
-
- spapr_populate_memory_node(fdt, i, mem_start, sizetmp);
- node_size -= sizetmp;
- mem_start += sizetmp;
- }
- }
-
- return 0;
-}
-
-static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
- SpaprMachineState *spapr)
-{
- MachineState *ms = MACHINE(spapr);
- PowerPCCPU *cpu = POWERPC_CPU(cs);
- CPUPPCState *env = &cpu->env;
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
- int index = spapr_get_vcpu_id(cpu);
- uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
- 0xffffffff, 0xffffffff};
- uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq()
- : SPAPR_TIMEBASE_FREQ;
- uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
- uint32_t page_sizes_prop[64];
- size_t page_sizes_prop_size;
- unsigned int smp_threads = ms->smp.threads;
- uint32_t vcpus_per_socket = smp_threads * ms->smp.cores;
- uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
- int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu));
- SpaprDrc *drc;
- int drc_index;
- uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ];
- int i;
-
- drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index);
- if (drc) {
- drc_index = spapr_drc_index(drc);
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index)));
- }
-
- _FDT((fdt_setprop_cell(fdt, offset, "reg", index)));
- _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
-
- _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
- _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size",
- env->dcache_line_size)));
- _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size",
- env->dcache_line_size)));
- _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size",
- env->icache_line_size)));
- _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size",
- env->icache_line_size)));
-
- if (pcc->l1_dcache_size) {
- _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
- pcc->l1_dcache_size)));
- } else {
- warn_report("Unknown L1 dcache size for cpu");
- }
- if (pcc->l1_icache_size) {
- _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
- pcc->l1_icache_size)));
- } else {
- warn_report("Unknown L1 icache size for cpu");
- }
-
- _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
- _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
- _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size)));
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size)));
- _FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
- _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
-
- if (env->spr_cb[SPR_PURR].oea_read) {
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1)));
- }
- if (env->spr_cb[SPR_SPURR].oea_read) {
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1)));
- }
-
- if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) {
- _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
- segs, sizeof(segs))));
- }
-
- /* Advertise VSX (vector extensions) if available
- * 1 == VMX / Altivec available
- * 2 == VSX available
- *
- * Only CPUs for which we create core types in spapr_cpu_core.c
- * are possible, and all of those have VMX */
- if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) {
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2)));
- } else {
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1)));
- }
-
- /* Advertise DFP (Decimal Floating Point) if available
- * 0 / no property == no DFP
- * 1 == DFP available */
- if (spapr_get_cap(spapr, SPAPR_CAP_DFP) != 0) {
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
- }
-
- page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop,
- sizeof(page_sizes_prop));
- if (page_sizes_prop_size) {
- _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
- page_sizes_prop, page_sizes_prop_size)));
- }
-
- spapr_populate_pa_features(spapr, cpu, fdt, offset);
-
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
- cs->cpu_index / vcpus_per_socket)));
-
- _FDT((fdt_setprop(fdt, offset, "ibm,pft-size",
- pft_size_prop, sizeof(pft_size_prop))));
-
- if (ms->numa_state->num_nodes > 1) {
- _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cpu));
- }
-
- _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt));
-
- if (pcc->radix_page_info) {
- for (i = 0; i < pcc->radix_page_info->count; i++) {
- radix_AP_encodings[i] =
- cpu_to_be32(pcc->radix_page_info->entries[i]);
- }
- _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings",
- radix_AP_encodings,
- pcc->radix_page_info->count *
- sizeof(radix_AP_encodings[0]))));
- }
-
- /*
- * We set this property to let the guest know that it can use the large
- * decrementer and its width in bits.
- */
- if (spapr_get_cap(spapr, SPAPR_CAP_LARGE_DECREMENTER) != SPAPR_CAP_OFF)
- _FDT((fdt_setprop_u32(fdt, offset, "ibm,dec-bits",
- pcc->lrg_decr_bits)));
-}
-
-static void spapr_populate_cpus_dt_node(void *fdt, SpaprMachineState *spapr)
-{
- CPUState **rev;
- CPUState *cs;
- int n_cpus;
- int cpus_offset;
- char *nodename;
- int i;
-
- cpus_offset = fdt_add_subnode(fdt, 0, "cpus");
- _FDT(cpus_offset);
- _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
- _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
-
- /*
- * We walk the CPUs in reverse order to ensure that CPU DT nodes
- * created by fdt_add_subnode() end up in the right order in FDT
- * for the guest kernel the enumerate the CPUs correctly.
- *
- * The CPU list cannot be traversed in reverse order, so we need
- * to do extra work.
- */
- n_cpus = 0;
- rev = NULL;
- CPU_FOREACH(cs) {
- rev = g_renew(CPUState *, rev, n_cpus + 1);
- rev[n_cpus++] = cs;
- }
-
- for (i = n_cpus - 1; i >= 0; i--) {
- CPUState *cs = rev[i];
- PowerPCCPU *cpu = POWERPC_CPU(cs);
- int index = spapr_get_vcpu_id(cpu);
- DeviceClass *dc = DEVICE_GET_CLASS(cs);
- int offset;
-
- if (!spapr_is_thread0_in_vcore(spapr, cpu)) {
- continue;
- }
-
- nodename = g_strdup_printf("%s@%x", dc->fw_name, index);
- offset = fdt_add_subnode(fdt, cpus_offset, nodename);
- g_free(nodename);
- _FDT(offset);
- spapr_populate_cpu_dt(cs, fdt, offset, spapr);
- }
-
- g_free(rev);
-}
-
-static int spapr_rng_populate_dt(void *fdt)
-{
- int node;
- int ret;
-
- node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities");
- if (node <= 0) {
- return -1;
- }
- ret = fdt_setprop_string(fdt, node, "device_type",
- "ibm,platform-facilities");
- ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1);
- ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0);
-
- node = fdt_add_subnode(fdt, node, "ibm,random-v1");
- if (node <= 0) {
- return -1;
- }
- ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random");
-
- return ret ? -1 : 0;
-}
-
static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr)
{
MemoryDeviceInfoList *info;
@@ -642,9 +390,8 @@ spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr,
return elem;
}
-/* ibm,dynamic-memory-v2 */
-static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt,
- int offset, MemoryDeviceInfoList *dimms)
+static int spapr_dt_dynamic_memory_v2(SpaprMachineState *spapr, void *fdt,
+ int offset, MemoryDeviceInfoList *dimms)
{
MachineState *machine = MACHINE(spapr);
uint8_t *int_buf, *cur_index;
@@ -735,8 +482,7 @@ static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt,
return 0;
}
-/* ibm,dynamic-memory */
-static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt,
+static int spapr_dt_dynamic_memory(SpaprMachineState *spapr, void *fdt,
int offset, MemoryDeviceInfoList *dimms)
{
MachineState *machine = MACHINE(spapr);
@@ -805,7 +551,8 @@ static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt,
* Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation
* of this device tree node.
*/
-static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt)
+static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr,
+ void *fdt)
{
MachineState *machine = MACHINE(spapr);
int nb_numa_nodes = machine->numa_state->num_nodes;
@@ -844,9 +591,9 @@ static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt)
/* ibm,dynamic-memory or ibm,dynamic-memory-v2 */
dimms = qmp_memory_device_list();
if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) {
- ret = spapr_populate_drmem_v2(spapr, fdt, offset, dimms);
+ ret = spapr_dt_dynamic_memory_v2(spapr, fdt, offset, dimms);
} else {
- ret = spapr_populate_drmem_v1(spapr, fdt, offset, dimms);
+ ret = spapr_dt_dynamic_memory(spapr, fdt, offset, dimms);
}
qapi_free_MemoryDeviceInfoList(dimms);
@@ -877,30 +624,267 @@ static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt)
return ret;
}
-static int spapr_dt_cas_updates(SpaprMachineState *spapr, void *fdt,
- SpaprOptionVector *ov5_updates)
+static int spapr_dt_memory(SpaprMachineState *spapr, void *fdt)
{
+ MachineState *machine = MACHINE(spapr);
SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
- int ret = 0, offset;
+ hwaddr mem_start, node_size;
+ int i, nb_nodes = machine->numa_state->num_nodes;
+ NodeInfo *nodes = machine->numa_state->nodes;
+
+ for (i = 0, mem_start = 0; i < nb_nodes; ++i) {
+ if (!nodes[i].node_mem) {
+ continue;
+ }
+ if (mem_start >= machine->ram_size) {
+ node_size = 0;
+ } else {
+ node_size = nodes[i].node_mem;
+ if (node_size > machine->ram_size - mem_start) {
+ node_size = machine->ram_size - mem_start;
+ }
+ }
+ if (!mem_start) {
+ /* spapr_machine_init() checks for rma_size <= node0_size
+ * already */
+ spapr_dt_memory_node(fdt, i, 0, spapr->rma_size);
+ mem_start += spapr->rma_size;
+ node_size -= spapr->rma_size;
+ }
+ for ( ; node_size; ) {
+ hwaddr sizetmp = pow2floor(node_size);
+
+ /* mem_start != 0 here */
+ if (ctzl(mem_start) < ctzl(sizetmp)) {
+ sizetmp = 1ULL << ctzl(mem_start);
+ }
+
+ spapr_dt_memory_node(fdt, i, mem_start, sizetmp);
+ node_size -= sizetmp;
+ mem_start += sizetmp;
+ }
+ }
/* Generate ibm,dynamic-reconfiguration-memory node if required */
- if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) {
+ if (spapr_ovec_test(spapr->ov5_cas, OV5_DRCONF_MEMORY)) {
+ int ret;
+
g_assert(smc->dr_lmb_enabled);
- ret = spapr_populate_drconf_memory(spapr, fdt);
+ ret = spapr_dt_dynamic_reconfiguration_memory(spapr, fdt);
if (ret) {
return ret;
}
}
- offset = fdt_path_offset(fdt, "/chosen");
- if (offset < 0) {
- offset = fdt_add_subnode(fdt, 0, "chosen");
- if (offset < 0) {
- return offset;
+ return 0;
+}
+
+static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset,
+ SpaprMachineState *spapr)
+{
+ MachineState *ms = MACHINE(spapr);
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
+ int index = spapr_get_vcpu_id(cpu);
+ uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
+ 0xffffffff, 0xffffffff};
+ uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq()
+ : SPAPR_TIMEBASE_FREQ;
+ uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
+ uint32_t page_sizes_prop[64];
+ size_t page_sizes_prop_size;
+ unsigned int smp_threads = ms->smp.threads;
+ uint32_t vcpus_per_socket = smp_threads * ms->smp.cores;
+ uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
+ int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu));
+ SpaprDrc *drc;
+ int drc_index;
+ uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ];
+ int i;
+
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index);
+ if (drc) {
+ drc_index = spapr_drc_index(drc);
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index)));
+ }
+
+ _FDT((fdt_setprop_cell(fdt, offset, "reg", index)));
+ _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
+
+ _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size",
+ env->dcache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size",
+ env->dcache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size",
+ env->icache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size",
+ env->icache_line_size)));
+
+ if (pcc->l1_dcache_size) {
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
+ pcc->l1_dcache_size)));
+ } else {
+ warn_report("Unknown L1 dcache size for cpu");
+ }
+ if (pcc->l1_icache_size) {
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
+ pcc->l1_icache_size)));
+ } else {
+ warn_report("Unknown L1 icache size for cpu");
+ }
+
+ _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size)));
+ _FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
+ _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
+
+ if (env->spr_cb[SPR_PURR].oea_read) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1)));
+ }
+ if (env->spr_cb[SPR_SPURR].oea_read) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1)));
+ }
+
+ if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
+ segs, sizeof(segs))));
+ }
+
+ /* Advertise VSX (vector extensions) if available
+ * 1 == VMX / Altivec available
+ * 2 == VSX available
+ *
+ * Only CPUs for which we create core types in spapr_cpu_core.c
+ * are possible, and all of those have VMX */
+ if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2)));
+ } else {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1)));
+ }
+
+ /* Advertise DFP (Decimal Floating Point) if available
+ * 0 / no property == no DFP
+ * 1 == DFP available */
+ if (spapr_get_cap(spapr, SPAPR_CAP_DFP) != 0) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
+ }
+
+ page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop,
+ sizeof(page_sizes_prop));
+ if (page_sizes_prop_size) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
+ page_sizes_prop, page_sizes_prop_size)));
+ }
+
+ spapr_dt_pa_features(spapr, cpu, fdt, offset);
+
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
+ cs->cpu_index / vcpus_per_socket)));
+
+ _FDT((fdt_setprop(fdt, offset, "ibm,pft-size",
+ pft_size_prop, sizeof(pft_size_prop))));
+
+ if (ms->numa_state->num_nodes > 1) {
+ _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cpu));
+ }
+
+ _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt));
+
+ if (pcc->radix_page_info) {
+ for (i = 0; i < pcc->radix_page_info->count; i++) {
+ radix_AP_encodings[i] =
+ cpu_to_be32(pcc->radix_page_info->entries[i]);
}
+ _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings",
+ radix_AP_encodings,
+ pcc->radix_page_info->count *
+ sizeof(radix_AP_encodings[0]))));
}
- return spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas,
- "ibm,architecture-vec-5");
+
+ /*
+ * We set this property to let the guest know that it can use the large
+ * decrementer and its width in bits.
+ */
+ if (spapr_get_cap(spapr, SPAPR_CAP_LARGE_DECREMENTER) != SPAPR_CAP_OFF)
+ _FDT((fdt_setprop_u32(fdt, offset, "ibm,dec-bits",
+ pcc->lrg_decr_bits)));
+}
+
+static void spapr_dt_cpus(void *fdt, SpaprMachineState *spapr)
+{
+ CPUState **rev;
+ CPUState *cs;
+ int n_cpus;
+ int cpus_offset;
+ char *nodename;
+ int i;
+
+ cpus_offset = fdt_add_subnode(fdt, 0, "cpus");
+ _FDT(cpus_offset);
+ _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
+ _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
+
+ /*
+ * We walk the CPUs in reverse order to ensure that CPU DT nodes
+ * created by fdt_add_subnode() end up in the right order in FDT
+ * for the guest kernel the enumerate the CPUs correctly.
+ *
+ * The CPU list cannot be traversed in reverse order, so we need
+ * to do extra work.
+ */
+ n_cpus = 0;
+ rev = NULL;
+ CPU_FOREACH(cs) {
+ rev = g_renew(CPUState *, rev, n_cpus + 1);
+ rev[n_cpus++] = cs;
+ }
+
+ for (i = n_cpus - 1; i >= 0; i--) {
+ CPUState *cs = rev[i];
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ int index = spapr_get_vcpu_id(cpu);
+ DeviceClass *dc = DEVICE_GET_CLASS(cs);
+ int offset;
+
+ if (!spapr_is_thread0_in_vcore(spapr, cpu)) {
+ continue;
+ }
+
+ nodename = g_strdup_printf("%s@%x", dc->fw_name, index);
+ offset = fdt_add_subnode(fdt, cpus_offset, nodename);
+ g_free(nodename);
+ _FDT(offset);
+ spapr_dt_cpu(cs, fdt, offset, spapr);
+ }
+
+ g_free(rev);
+}
+
+static int spapr_dt_rng(void *fdt)
+{
+ int node;
+ int ret;
+
+ node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities");
+ if (node <= 0) {
+ return -1;
+ }
+ ret = fdt_setprop_string(fdt, node, "device_type",
+ "ibm,platform-facilities");
+ ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1);
+ ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0);
+
+ node = fdt_add_subnode(fdt, node, "ibm,random-v1");
+ if (node <= 0) {
+ return -1;
+ }
+ ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random");
+
+ return ret ? -1 : 0;
}
static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt)
@@ -967,6 +951,29 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt)
_FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains",
maxdomains, sizeof(maxdomains)));
+ /*
+ * FWNMI reserves RTAS_ERROR_LOG_MAX for the machine check error log,
+ * and 16 bytes per CPU for system reset error log plus an extra 8 bytes.
+ *
+ * The system reset requirements are driven by existing Linux and PowerVM
+ * implementation which (contrary to PAPR) saves r3 in the error log
+ * structure like machine check, so Linux expects to find the saved r3
+ * value at the address in r3 upon FWNMI-enabled sreset interrupt (and
+ * does not look at the error value).
+ *
+ * System reset interrupts are not subject to interlock like machine
+ * check, so this memory area could be corrupted if the sreset is
+ * interrupted by a machine check (or vice versa) if it was shared. To
+ * prevent this, system reset uses per-CPU areas for the sreset save
+ * area. A system reset that interrupts a system reset handler could
+ * still overwrite this area, but Linux doesn't try to recover in that
+ * case anyway.
+ *
+ * The extra 8 bytes is required because Linux's FWNMI error log check
+ * is off-by-one.
+ */
+ _FDT(fdt_setprop_cell(fdt, rtas, "rtas-size", RTAS_ERROR_LOG_MAX +
+ ms->smp.max_cpus * sizeof(uint64_t)*2 + sizeof(uint64_t)));
_FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max",
RTAS_ERROR_LOG_MAX));
_FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate",
@@ -1040,81 +1047,91 @@ static void spapr_dt_ov5_platform_support(SpaprMachineState *spapr, void *fdt,
val, sizeof(val)));
}
-static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt)
+static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset)
{
MachineState *machine = MACHINE(spapr);
SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
int chosen;
- const char *boot_device = machine->boot_order;
- char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus);
- size_t cb = 0;
- char *bootlist = get_boot_devices_list(&cb);
_FDT(chosen = fdt_add_subnode(fdt, 0, "chosen"));
- if (machine->kernel_cmdline && machine->kernel_cmdline[0]) {
- _FDT(fdt_setprop_string(fdt, chosen, "bootargs",
- machine->kernel_cmdline));
- }
- if (spapr->initrd_size) {
- _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start",
- spapr->initrd_base));
- _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end",
- spapr->initrd_base + spapr->initrd_size));
- }
+ if (reset) {
+ const char *boot_device = machine->boot_order;
+ char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus);
+ size_t cb = 0;
+ char *bootlist = get_boot_devices_list(&cb);
+
+ if (machine->kernel_cmdline && machine->kernel_cmdline[0]) {
+ _FDT(fdt_setprop_string(fdt, chosen, "bootargs",
+ machine->kernel_cmdline));
+ }
- if (spapr->kernel_size) {
- uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr),
- cpu_to_be64(spapr->kernel_size) };
+ if (spapr->initrd_size) {
+ _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start",
+ spapr->initrd_base));
+ _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end",
+ spapr->initrd_base + spapr->initrd_size));
+ }
+
+ if (spapr->kernel_size) {
+ uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr),
+ cpu_to_be64(spapr->kernel_size) };
- _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel",
+ _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel",
&kprop, sizeof(kprop)));
- if (spapr->kernel_le) {
- _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0));
+ if (spapr->kernel_le) {
+ _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0));
+ }
}
- }
- if (boot_menu) {
- _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu)));
- }
- _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width));
- _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height));
- _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth));
+ if (boot_menu) {
+ _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu)));
+ }
+ _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width));
+ _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height));
+ _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth));
- if (cb && bootlist) {
- int i;
+ if (cb && bootlist) {
+ int i;
- for (i = 0; i < cb; i++) {
- if (bootlist[i] == '\n') {
- bootlist[i] = ' ';
+ for (i = 0; i < cb; i++) {
+ if (bootlist[i] == '\n') {
+ bootlist[i] = ' ';
+ }
}
+ _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist));
}
- _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist));
- }
- if (boot_device && strlen(boot_device)) {
- _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device));
- }
+ if (boot_device && strlen(boot_device)) {
+ _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device));
+ }
+
+ if (!spapr->has_graphics && stdout_path) {
+ /*
+ * "linux,stdout-path" and "stdout" properties are
+ * deprecated by linux kernel. New platforms should only
+ * use the "stdout-path" property. Set the new property
+ * and continue using older property to remain compatible
+ * with the existing firmware.
+ */
+ _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path));
+ _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path));
+ }
- if (!spapr->has_graphics && stdout_path) {
/*
- * "linux,stdout-path" and "stdout" properties are deprecated by linux
- * kernel. New platforms should only use the "stdout-path" property. Set
- * the new property and continue using older property to remain
- * compatible with the existing firmware.
+ * We can deal with BAR reallocation just fine, advertise it
+ * to the guest
*/
- _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path));
- _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path));
- }
+ if (smc->linux_pci_probe) {
+ _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0));
+ }
- /* We can deal with BAR reallocation just fine, advertise it to the guest */
- if (smc->linux_pci_probe) {
- _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0));
- }
+ spapr_dt_ov5_platform_support(spapr, fdt, chosen);
- spapr_dt_ov5_platform_support(spapr, fdt, chosen);
+ g_free(stdout_path);
+ g_free(bootlist);
+ }
- g_free(stdout_path);
- g_free(bootlist);
+ _FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5"));
}
static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt)
@@ -1192,7 +1209,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
/* /interrupt controller */
spapr_irq_dt(spapr, spapr_max_server_number(spapr), fdt, PHANDLE_INTC);
- ret = spapr_populate_memory(spapr, fdt);
+ ret = spapr_dt_memory(spapr, fdt);
if (ret < 0) {
error_report("couldn't setup memory nodes in fdt");
exit(1);
@@ -1202,7 +1219,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
spapr_dt_vdevice(spapr->vio_bus, fdt);
if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) {
- ret = spapr_rng_populate_dt(fdt);
+ ret = spapr_dt_rng(fdt);
if (ret < 0) {
error_report("could not set up rng device in the fdt");
exit(1);
@@ -1217,8 +1234,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
}
}
- /* cpus */
- spapr_populate_cpus_dt_node(fdt, spapr);
+ spapr_dt_cpus(fdt, spapr);
if (smc->dr_lmb_enabled) {
_FDT(spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB));
@@ -1240,9 +1256,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
spapr_dt_rtas(spapr, fdt);
/* /chosen */
- if (reset) {
- spapr_dt_chosen(spapr, fdt);
- }
+ spapr_dt_chosen(spapr, fdt, reset);
/* /hypervisor */
if (kvm_enabled()) {
@@ -1261,13 +1275,6 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
}
}
- /* ibm,client-architecture-support updates */
- ret = spapr_dt_cas_updates(spapr, fdt, spapr->ov5_cas);
- if (ret < 0) {
- error_report("couldn't setup CAS properties fdt");
- exit(1);
- }
-
if (smc->dr_phb_enabled) {
ret = spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB);
if (ret < 0) {
@@ -1569,7 +1576,7 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift,
spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT);
}
-void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr)
+void spapr_setup_hpt(SpaprMachineState *spapr)
{
int hpt_shift;
@@ -1585,9 +1592,16 @@ void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr)
}
spapr_reallocate_hpt(spapr, hpt_shift, &error_fatal);
- if (spapr->vrma_adjust) {
- spapr->rma_size = kvmppc_rma_size(spapr_node0_size(MACHINE(spapr)),
- spapr->htab_shift);
+ if (kvm_enabled()) {
+ hwaddr vrma_limit = kvmppc_vrma_limit(spapr->htab_shift);
+
+ /* Check our RMA fits in the possible VRMA */
+ if (vrma_limit < spapr->rma_size) {
+ error_report("Unable to create %" HWADDR_PRIu
+ "MiB RMA (VRMA only allows %" HWADDR_PRIu "MiB",
+ spapr->rma_size / MiB, vrma_limit / MiB);
+ exit(EXIT_FAILURE);
+ }
}
}
@@ -1627,7 +1641,7 @@ static void spapr_machine_reset(MachineState *machine)
spapr->patb_entry = PATE1_GR;
spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT);
} else {
- spapr_setup_hpt_and_vrma(spapr);
+ spapr_setup_hpt(spapr);
}
qemu_devices_reset();
@@ -1691,16 +1705,17 @@ static void spapr_machine_reset(MachineState *machine)
spapr->fdt_blob = fdt;
/* Set up the entry state */
- spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, fdt_addr);
+ spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, 0, fdt_addr, 0);
first_ppc_cpu->env.gpr[5] = 0;
spapr->cas_reboot = false;
- spapr->mc_status = -1;
- spapr->guest_machine_check_addr = -1;
+ spapr->fwnmi_system_reset_addr = -1;
+ spapr->fwnmi_machine_check_addr = -1;
+ spapr->fwnmi_machine_check_interlock = -1;
/* Signal all vCPUs waiting on this condition */
- qemu_cond_broadcast(&spapr->mc_delivery_cond);
+ qemu_cond_broadcast(&spapr->fwnmi_machine_check_interlock_cond);
migrate_del_blocker(spapr->fwnmi_migration_blocker);
}
@@ -1989,7 +2004,7 @@ static bool spapr_fwnmi_needed(void *opaque)
{
SpaprMachineState *spapr = (SpaprMachineState *)opaque;
- return spapr->guest_machine_check_addr != -1;
+ return spapr->fwnmi_machine_check_addr != -1;
}
static int spapr_fwnmi_pre_save(void *opaque)
@@ -2000,7 +2015,7 @@ static int spapr_fwnmi_pre_save(void *opaque)
* Check if machine check handling is in progress and print a
* warning message.
*/
- if (spapr->mc_status != -1) {
+ if (spapr->fwnmi_machine_check_interlock != -1) {
warn_report("A machine check is being handled during migration. The"
"handler may run and log hardware error on the destination");
}
@@ -2008,15 +2023,16 @@ static int spapr_fwnmi_pre_save(void *opaque)
return 0;
}
-static const VMStateDescription vmstate_spapr_machine_check = {
- .name = "spapr_machine_check",
+static const VMStateDescription vmstate_spapr_fwnmi = {
+ .name = "spapr_fwnmi",
.version_id = 1,
.minimum_version_id = 1,
.needed = spapr_fwnmi_needed,
.pre_save = spapr_fwnmi_pre_save,
.fields = (VMStateField[]) {
- VMSTATE_UINT64(guest_machine_check_addr, SpaprMachineState),
- VMSTATE_INT32(mc_status, SpaprMachineState),
+ VMSTATE_UINT64(fwnmi_system_reset_addr, SpaprMachineState),
+ VMSTATE_UINT64(fwnmi_machine_check_addr, SpaprMachineState),
+ VMSTATE_INT32(fwnmi_machine_check_interlock, SpaprMachineState),
VMSTATE_END_OF_LIST()
},
};
@@ -2055,7 +2071,7 @@ static const VMStateDescription vmstate_spapr = {
&vmstate_spapr_cap_large_decr,
&vmstate_spapr_cap_ccf_assist,
&vmstate_spapr_cap_fwnmi,
- &vmstate_spapr_machine_check,
+ &vmstate_spapr_fwnmi,
NULL
}
};
@@ -2641,6 +2657,42 @@ static PCIHostState *spapr_create_default_phb(void)
return PCI_HOST_BRIDGE(dev);
}
+static hwaddr spapr_rma_size(SpaprMachineState *spapr, Error **errp)
+{
+ MachineState *machine = MACHINE(spapr);
+ SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+ hwaddr rma_size = machine->ram_size;
+ hwaddr node0_size = spapr_node0_size(machine);
+
+ /* RMA has to fit in the first NUMA node */
+ rma_size = MIN(rma_size, node0_size);
+
+ /*
+ * VRMA access is via a special 1TiB SLB mapping, so the RMA can
+ * never exceed that
+ */
+ rma_size = MIN(rma_size, 1 * TiB);
+
+ /*
+ * Clamp the RMA size based on machine type. This is for
+ * migration compatibility with older qemu versions, which limited
+ * the RMA size for complicated and mostly bad reasons.
+ */
+ if (smc->rma_limit) {
+ rma_size = MIN(rma_size, smc->rma_limit);
+ }
+
+ if (rma_size < MIN_RMA_SLOF) {
+ error_setg(errp,
+ "pSeries SLOF firmware requires >= %" HWADDR_PRIx
+ "ldMiB guest RMA (Real Mode Area memory)",
+ MIN_RMA_SLOF / MiB);
+ return 0;
+ }
+
+ return rma_size;
+}
+
/* pSeries LPAR / sPAPR hardware init */
static void spapr_machine_init(MachineState *machine)
{
@@ -2652,7 +2704,6 @@ static void spapr_machine_init(MachineState *machine)
PCIHostState *phb;
int i;
MemoryRegion *sysmem = get_system_memory();
- hwaddr node0_size = spapr_node0_size(machine);
long load_limit, fw_size;
char *filename;
Error *resize_hpt_err = NULL;
@@ -2692,34 +2743,7 @@ static void spapr_machine_init(MachineState *machine)
exit(1);
}
- spapr->rma_size = node0_size;
-
- /* With KVM, we don't actually know whether KVM supports an
- * unbounded RMA (PR KVM) or is limited by the hash table size
- * (HV KVM using VRMA), so we always assume the latter
- *
- * In that case, we also limit the initial allocations for RTAS
- * etc... to 256M since we have no way to know what the VRMA size
- * is going to be as it depends on the size of the hash table
- * which isn't determined yet.
- */
- if (kvm_enabled()) {
- spapr->vrma_adjust = 1;
- spapr->rma_size = MIN(spapr->rma_size, 0x10000000);
- }
-
- /* Actually we don't support unbounded RMA anymore since we added
- * proper emulation of HV mode. The max we can get is 16G which
- * also happens to be what we configure for PAPR mode so make sure
- * we don't do anything bigger than that
- */
- spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull);
-
- if (spapr->rma_size > node0_size) {
- error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")",
- spapr->rma_size);
- exit(1);
- }
+ spapr->rma_size = spapr_rma_size(spapr, &error_fatal);
/* Setup a load limit for the ramdisk leaving room for SLOF and FDT */
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
@@ -2869,7 +2893,7 @@ static void spapr_machine_init(MachineState *machine)
spapr_create_lmb_dr_connectors(spapr);
}
- if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_ON) {
+ if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_ON) {
/* Create the error string for live migration blocker */
error_setg(&spapr->fwnmi_migration_blocker,
"A machine check is being handled during migration. The handler"
@@ -2956,13 +2980,6 @@ static void spapr_machine_init(MachineState *machine)
}
}
- if (spapr->rma_size < (MIN_RMA_SLOF * MiB)) {
- error_report(
- "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)",
- MIN_RMA_SLOF);
- exit(1);
- }
-
if (kernel_filename) {
uint64_t lowaddr = 0;
@@ -3045,7 +3062,7 @@ static void spapr_machine_init(MachineState *machine)
kvmppc_spapr_enable_inkernel_multitce();
}
- qemu_cond_init(&spapr->mc_delivery_cond);
+ qemu_cond_init(&spapr->fwnmi_machine_check_interlock_cond);
}
static int spapr_kvm_type(MachineState *machine, const char *vm_type)
@@ -3367,8 +3384,28 @@ static void spapr_machine_finalizefn(Object *obj)
void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg)
{
+ SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+
cpu_synchronize_state(cs);
- ppc_cpu_do_system_reset(cs);
+ /* If FWNMI is inactive, addr will be -1, which will deliver to 0x100 */
+ if (spapr->fwnmi_system_reset_addr != -1) {
+ uint64_t rtas_addr, addr;
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+
+ /* get rtas addr from fdt */
+ rtas_addr = spapr_get_rtas_addr();
+ if (!rtas_addr) {
+ qemu_system_guest_panicked(NULL);
+ return;
+ }
+
+ addr = rtas_addr + RTAS_ERROR_LOG_MAX + cs->cpu_index * sizeof(uint64_t)*2;
+ stq_be_phys(&address_space_memory, addr, env->gpr[3]);
+ stq_be_phys(&address_space_memory, addr + sizeof(uint64_t), 0);
+ env->gpr[3] = addr;
+ }
+ ppc_cpu_do_system_reset(cs, spapr->fwnmi_system_reset_addr);
}
static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
@@ -3389,8 +3426,8 @@ int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE;
node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP,
&error_abort);
- *fdt_start_offset = spapr_populate_memory_node(fdt, node, addr,
- SPAPR_MEMORY_BLOCK_SIZE);
+ *fdt_start_offset = spapr_dt_memory_node(fdt, node, addr,
+ SPAPR_MEMORY_BLOCK_SIZE);
return 0;
}
@@ -3791,7 +3828,7 @@ int spapr_core_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
offset = fdt_add_subnode(fdt, 0, nodename);
g_free(nodename);
- spapr_populate_cpu_dt(cs, fdt, offset, spapr);
+ spapr_dt_cpu(cs, fdt, offset, spapr);
*fdt_start_offset = offset;
return 0;
@@ -4504,7 +4541,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF;
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON;
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON;
- smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_ON;
+ smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON;
spapr_caps_add_properties(smc, &error_abort);
smc->irq = &spapr_irq_dual;
smc->dr_phb_enabled = true;
@@ -4582,7 +4619,8 @@ static void spapr_machine_4_2_class_options(MachineClass *mc)
spapr_machine_5_0_class_options(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;
+ smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_OFF;
+ smc->rma_limit = 16 * GiB;
mc->nvdimm_supported = false;
}
diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c
index 8b27d3ac09..679ae7959f 100644
--- a/hw/ppc/spapr_caps.c
+++ b/hw/ppc/spapr_caps.c
@@ -509,17 +509,14 @@ static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val,
}
}
-static void cap_fwnmi_mce_apply(SpaprMachineState *spapr, uint8_t val,
+static void cap_fwnmi_apply(SpaprMachineState *spapr, uint8_t val,
Error **errp)
{
if (!val) {
return; /* Disabled by default */
}
- if (tcg_enabled()) {
- warn_report("Firmware Assisted Non-Maskable Interrupts(FWNMI) not "
- "supported in TCG");
- } else if (kvm_enabled()) {
+ if (kvm_enabled()) {
if (kvmppc_set_fwnmi() < 0) {
error_setg(errp, "Firmware Assisted Non-Maskable Interrupts(FWNMI) "
"not supported by KVM");
@@ -626,14 +623,14 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
.type = "bool",
.apply = cap_ccf_assist_apply,
},
- [SPAPR_CAP_FWNMI_MCE] = {
- .name = "fwnmi-mce",
- .description = "Handle fwnmi machine check exceptions",
- .index = SPAPR_CAP_FWNMI_MCE,
+ [SPAPR_CAP_FWNMI] = {
+ .name = "fwnmi",
+ .description = "Implements PAPR FWNMI option",
+ .index = SPAPR_CAP_FWNMI,
.get = spapr_cap_get_bool,
.set = spapr_cap_set_bool,
.type = "bool",
- .apply = cap_fwnmi_mce_apply,
+ .apply = cap_fwnmi_apply,
},
};
@@ -774,7 +771,7 @@ SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE);
SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV);
SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER);
SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST);
-SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI_MCE);
+SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI);
void spapr_caps_init(SpaprMachineState *spapr)
{
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index d09125d9af..ac1c109427 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -50,22 +50,14 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu)
* the settings below ensure proper operations with TCG in absence of
* a real hypervisor.
*
- * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for
- * real mode accesses, which thankfully defaults to 0 and isn't
- * accessible in guest mode.
- *
* Disable Power-saving mode Exit Cause exceptions for the CPU, so
* we don't get spurious wakups before an RTAS start-cpu call.
* For the same reason, set PSSCR_EC.
*/
- lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm);
+ lpcr &= ~(LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm);
lpcr |= LPCR_LPES0 | LPCR_LPES1;
env->spr[SPR_PSSCR] |= PSSCR_EC;
- /* Set RMLS to the max (ie, 16G) */
- lpcr &= ~LPCR_RMLS;
- lpcr |= 1ull << LPCR_RMLS_SHIFT;
-
ppc_store_lpcr(cpu, lpcr);
/* Set a full AMOR so guest can use the AMR as it sees fit */
@@ -84,13 +76,17 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu)
spapr_irq_cpu_intc_reset(spapr, cpu);
}
-void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3)
+void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip,
+ target_ulong r1, target_ulong r3,
+ target_ulong r4)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
CPUPPCState *env = &cpu->env;
env->nip = nip;
+ env->gpr[1] = r1;
env->gpr[3] = r3;
+ env->gpr[4] = r4;
kvmppc_set_reg_ppc_online(cpu, 1);
CPU(cpu)->halted = 0;
/* Enable Power-saving mode Exit Cause exceptions */
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 8b32b7eea5..323fcef4aa 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -786,28 +786,12 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered)
{
SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
CPUState *cs = CPU(cpu);
- uint64_t rtas_addr;
CPUPPCState *env = &cpu->env;
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
- target_ulong msr = 0;
+ uint64_t rtas_addr;
struct rtas_error_log log;
struct mc_extended_log *ext_elog;
uint32_t summary;
- /*
- * Properly set bits in MSR before we invoke the handler.
- * SRR0/1, DAR and DSISR are properly set by KVM
- */
- if (!(*pcc->interrupts_big_endian)(cpu)) {
- msr |= (1ULL << MSR_LE);
- }
-
- if (env->msr & (1ULL << MSR_SF)) {
- msr |= (1ULL << MSR_SF);
- }
-
- msr |= (1ULL << MSR_ME);
-
ext_elog = g_malloc0(sizeof(*ext_elog));
summary = spapr_mce_get_elog_type(cpu, recovered, ext_elog);
@@ -823,8 +807,7 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered)
/* get rtas addr from fdt */
rtas_addr = spapr_get_rtas_addr();
if (!rtas_addr) {
- /* Unable to fetch rtas_addr. Hence reset the guest */
- ppc_cpu_do_system_reset(cs);
+ qemu_system_guest_panicked(NULL);
g_free(ext_elog);
return;
}
@@ -836,12 +819,11 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered)
cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET +
sizeof(env->gpr[3]) + sizeof(log), ext_elog,
sizeof(*ext_elog));
+ g_free(ext_elog);
env->gpr[3] = rtas_addr + RTAS_ERROR_LOG_OFFSET;
- env->msr = msr;
- env->nip = spapr->guest_machine_check_addr;
- g_free(ext_elog);
+ ppc_cpu_do_fwnmi_machine_check(cs, spapr->fwnmi_machine_check_addr);
}
void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered)
@@ -851,7 +833,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered)
int ret;
Error *local_err = NULL;
- if (spapr->guest_machine_check_addr == -1) {
+ if (spapr->fwnmi_machine_check_addr == -1) {
/*
* This implies that we have hit a machine check either when the
* guest has not registered FWNMI (i.e., "ibm,nmi-register" not
@@ -863,19 +845,19 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered)
return;
}
- while (spapr->mc_status != -1) {
+ while (spapr->fwnmi_machine_check_interlock != -1) {
/*
* Check whether the same CPU got machine check error
* while still handling the mc error (i.e., before
* that CPU called "ibm,nmi-interlock")
*/
- if (spapr->mc_status == cpu->vcpu_id) {
+ if (spapr->fwnmi_machine_check_interlock == cpu->vcpu_id) {
qemu_system_guest_panicked(NULL);
return;
}
- qemu_cond_wait_iothread(&spapr->mc_delivery_cond);
+ qemu_cond_wait_iothread(&spapr->fwnmi_machine_check_interlock_cond);
/* Meanwhile if the system is reset, then just return */
- if (spapr->guest_machine_check_addr == -1) {
+ if (spapr->fwnmi_machine_check_addr == -1) {
return;
}
}
@@ -891,7 +873,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered)
warn_report("Received a fwnmi while migration was in progress");
}
- spapr->mc_status = cpu->vcpu_id;
+ spapr->fwnmi_machine_check_interlock = cpu->vcpu_id;
spapr_mce_dispatch_elog(cpu, recovered);
}
@@ -983,6 +965,19 @@ void spapr_clear_pending_events(SpaprMachineState *spapr)
}
}
+void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr)
+{
+ SpaprEventLogEntry *entry = NULL, *next_entry;
+
+ QTAILQ_FOREACH_SAFE(entry, &spapr->pending_events, next, next_entry) {
+ if (spapr_event_log_entry_type(entry) == RTAS_LOG_TYPE_HOTPLUG) {
+ QTAILQ_REMOVE(&spapr->pending_events, entry, next);
+ g_free(entry->extended_log);
+ g_free(entry);
+ }
+ }
+}
+
void spapr_events_init(SpaprMachineState *spapr)
{
int epow_irq = SPAPR_IRQ_EPOW;
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 934eb12d27..40c86e91eb 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1458,7 +1458,7 @@ static void spapr_check_setup_free_hpt(SpaprMachineState *spapr,
spapr_free_hpt(spapr);
} else if (!(patbe_new & PATE1_GR)) {
/* RADIX->HASH || NOTHING->HASH : Allocate HPT */
- spapr_setup_hpt_and_vrma(spapr);
+ spapr_setup_hpt(spapr);
}
return;
}
@@ -1640,7 +1640,7 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu,
return best_compat;
}
-static bool spapr_transient_dev_before_cas(void)
+static void spapr_handle_transient_dev_before_cas(SpaprMachineState *spapr)
{
Object *drc_container;
ObjectProperty *prop;
@@ -1658,10 +1658,11 @@ static bool spapr_transient_dev_before_cas(void)
prop->name, NULL));
if (spapr_drc_transient(drc)) {
- return true;
+ spapr_drc_reset(drc);
}
}
- return false;
+
+ spapr_clear_pending_hotplug_events(spapr);
}
static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
@@ -1834,9 +1835,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
spapr_irq_update_active_intc(spapr);
- if (spapr_transient_dev_before_cas()) {
- spapr->cas_reboot = true;
- }
+ spapr_handle_transient_dev_before_cas(spapr);
if (!spapr->cas_reboot) {
void *fdt;
@@ -1846,7 +1845,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
* (because the guest isn't going to use radix) then set it up here. */
if ((spapr->patb_entry & PATE1_GR) && !guest_radix) {
/* legacy hash or new hash: */
- spapr_setup_hpt_and_vrma(spapr);
+ spapr_setup_hpt(spapr);
}
if (fdt_bufsize < sizeof(hdr)) {
diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c
index 74eeb8bb74..25be8082d7 100644
--- a/hw/ppc/spapr_nvdimm.c
+++ b/hw/ppc/spapr_nvdimm.c
@@ -35,6 +35,7 @@ void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size,
{
char *uuidstr = NULL;
QemuUUID uuid;
+ int ret;
if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) {
error_setg(errp, "NVDIMM memory size excluding the label area"
@@ -43,8 +44,10 @@ void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size,
return;
}
- uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, NULL);
- qemu_uuid_parse(uuidstr, &uuid);
+ uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP,
+ &error_abort);
+ ret = qemu_uuid_parse(uuidstr, &uuid);
+ g_assert(!ret);
g_free(uuidstr);
if (qemu_uuid_is_null(&uuid)) {
diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c
index 0ff6d1aeae..dd003f1763 100644
--- a/hw/ppc/spapr_ovec.c
+++ b/hw/ppc/spapr_ovec.c
@@ -200,8 +200,8 @@ SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector)
return ov;
}
-int spapr_ovec_populate_dt(void *fdt, int fdt_offset,
- SpaprOptionVector *ov, const char *name)
+int spapr_dt_ovec(void *fdt, int fdt_offset,
+ SpaprOptionVector *ov, const char *name)
{
uint8_t vec[OV_MAXBYTES + 1];
uint16_t vec_len;
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 656fdd2216..9fb8c8632a 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -190,7 +190,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, SpaprMachineState *spapr,
*/
newcpu->env.tb_env->tb_offset = callcpu->env.tb_env->tb_offset;
- spapr_cpu_set_entry_state(newcpu, start, r3);
+ spapr_cpu_set_entry_state(newcpu, start, 0, r3, 0);
qemu_cpu_kick(CPU(newcpu));
@@ -414,8 +414,9 @@ static void rtas_ibm_nmi_register(PowerPCCPU *cpu,
uint32_t nret, target_ulong rets)
{
hwaddr rtas_addr;
+ target_ulong sreset_addr, mce_addr;
- if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) {
+ if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) {
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
return;
}
@@ -426,7 +427,19 @@ static void rtas_ibm_nmi_register(PowerPCCPU *cpu,
return;
}
- spapr->guest_machine_check_addr = rtas_ld(args, 1);
+ sreset_addr = rtas_ld(args, 0);
+ mce_addr = rtas_ld(args, 1);
+
+ /* PAPR requires these are in the first 32M of memory and within RMA */
+ if (sreset_addr >= 32 * MiB || sreset_addr >= spapr->rma_size ||
+ mce_addr >= 32 * MiB || mce_addr >= spapr->rma_size) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ spapr->fwnmi_system_reset_addr = sreset_addr;
+ spapr->fwnmi_machine_check_addr = mce_addr;
+
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
@@ -436,29 +449,39 @@ static void rtas_ibm_nmi_interlock(PowerPCCPU *cpu,
target_ulong args,
uint32_t nret, target_ulong rets)
{
- if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) {
+ if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) {
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
return;
}
- if (spapr->guest_machine_check_addr == -1) {
+ if (spapr->fwnmi_machine_check_addr == -1) {
/* NMI register not called */
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
- if (spapr->mc_status != cpu->vcpu_id) {
- /* The vCPU that hit the NMI should invoke "ibm,nmi-interlock" */
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ if (spapr->fwnmi_machine_check_interlock != cpu->vcpu_id) {
+ /*
+ * The vCPU that hit the NMI should invoke "ibm,nmi-interlock"
+ * This should be PARAM_ERROR, but Linux calls "ibm,nmi-interlock"
+ * for system reset interrupts, despite them not being interlocked.
+ * PowerVM silently ignores this and returns success here. Returning
+ * failure causes Linux to print the error "FWNMI: nmi-interlock
+ * failed: -3", although no other apparent ill effects, this is a
+ * regression for the user when enabling FWNMI. So for now, match
+ * PowerVM. When most Linux clients are fixed, this could be
+ * changed.
+ */
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
return;
}
/*
* vCPU issuing "ibm,nmi-interlock" is done with NMI handling,
- * hence unset mc_status.
+ * hence unset fwnmi_machine_check_interlock.
*/
- spapr->mc_status = -1;
- qemu_cond_signal(&spapr->mc_delivery_cond);
+ spapr->fwnmi_machine_check_interlock = -1;
+ qemu_cond_signal(&spapr->fwnmi_machine_check_interlock_cond);
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
migrate_del_blocker(spapr->fwnmi_migration_blocker);
}
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index 7d584e7732..923488beb2 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -55,6 +55,8 @@
#define VSCSI_MAX_SECTORS 4096
#define VSCSI_REQ_LIMIT 24
+/* Maximum size of a IU payload */
+#define SRP_MAX_IU_DATA_LEN (SRP_MAX_IU_LEN - sizeof(union srp_iu))
#define SRP_RSP_SENSE_DATA_LEN 18
#define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL
@@ -66,7 +68,7 @@ typedef union vscsi_crq {
typedef struct vscsi_req {
vscsi_crq crq;
- union viosrp_iu iu;
+ uint8_t viosrp_iu_buf[SRP_MAX_IU_LEN];
/* SCSI request tracking */
SCSIRequest *sreq;
@@ -97,6 +99,11 @@ typedef struct {
vscsi_req reqs[VSCSI_REQ_LIMIT];
} VSCSIState;
+static union viosrp_iu *req_iu(vscsi_req *req)
+{
+ return (union viosrp_iu *)req->viosrp_iu_buf;
+}
+
static struct vscsi_req *vscsi_get_req(VSCSIState *s)
{
vscsi_req *req;
@@ -121,7 +128,7 @@ static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag)
for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
req = &s->reqs[i];
- if (req->iu.srp.cmd.tag == srp_tag) {
+ if (req_iu(req)->srp.cmd.tag == srp_tag) {
return req;
}
}
@@ -176,9 +183,11 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
{
long rc, rc1;
+ assert(length <= SRP_MAX_IU_LEN);
+
/* First copy the SRP */
rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr,
- &req->iu, length);
+ &req->viosrp_iu_buf, length);
if (rc) {
fprintf(stderr, "vscsi_send_iu: DMA write failure !\n");
}
@@ -188,7 +197,7 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
req->crq.s.reserved = 0x00;
req->crq.s.timeout = cpu_to_be16(0x0000);
req->crq.s.IU_length = cpu_to_be16(length);
- req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */
+ req->crq.s.IU_data_ptr = req_iu(req)->srp.rsp.tag; /* right byte order */
if (rc == 0) {
req->crq.s.status = VIOSRP_OK;
@@ -224,7 +233,7 @@ static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req,
static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req,
uint8_t status, int32_t res_in, int32_t res_out)
{
- union viosrp_iu *iu = &req->iu;
+ union viosrp_iu *iu = req_iu(req);
uint64_t tag = iu->srp.rsp.tag;
int total_len = sizeof(iu->srp.rsp);
uint8_t sol_not = iu->srp.cmd.sol_not;
@@ -261,10 +270,12 @@ static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req,
if (status) {
iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2;
if (req->senselen) {
- req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
- req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen);
- memcpy(req->iu.srp.rsp.data, req->sense, req->senselen);
- total_len += req->senselen;
+ int sense_data_len = MIN(req->senselen, SRP_MAX_IU_DATA_LEN);
+
+ iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
+ iu->srp.rsp.sense_data_len = cpu_to_be32(sense_data_len);
+ memcpy(iu->srp.rsp.data, req->sense, sense_data_len);
+ total_len += sense_data_len;
}
} else {
iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1;
@@ -285,7 +296,7 @@ static int vscsi_fetch_desc(VSCSIState *s, struct vscsi_req *req,
unsigned n, unsigned buf_offset,
struct srp_direct_buf *ret)
{
- struct srp_cmd *cmd = &req->iu.srp.cmd;
+ struct srp_cmd *cmd = &req_iu(req)->srp.cmd;
switch (req->dma_fmt) {
case SRP_NO_DATA_DESC: {
@@ -473,7 +484,7 @@ static int data_out_desc_size(struct srp_cmd *cmd)
static int vscsi_preprocess_desc(vscsi_req *req)
{
- struct srp_cmd *cmd = &req->iu.srp.cmd;
+ struct srp_cmd *cmd = &req_iu(req)->srp.cmd;
req->cdb_offset = cmd->add_cdb_len & ~3;
@@ -597,7 +608,7 @@ static const VMStateDescription vmstate_spapr_vscsi_req = {
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_BUFFER(crq.raw, vscsi_req),
- VMSTATE_BUFFER(iu.srp.reserved, vscsi_req),
+ VMSTATE_BUFFER(viosrp_iu_buf, vscsi_req),
VMSTATE_UINT32(qtag, vscsi_req),
VMSTATE_BOOL(active, vscsi_req),
VMSTATE_UINT32(data_len, vscsi_req),
@@ -655,7 +666,7 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
{
- union viosrp_iu *iu = &req->iu;
+ union viosrp_iu *iu = req_iu(req);
struct srp_login_rsp *rsp = &iu->srp.login_rsp;
uint64_t tag = iu->srp.rsp.tag;
@@ -671,8 +682,8 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
*/
rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2);
rsp->tag = tag;
- rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu));
- rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu));
+ rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN);
+ rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN);
/* direct and indirect */
rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT);
@@ -681,7 +692,7 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req)
{
- uint8_t *cdb = req->iu.srp.cmd.cdb;
+ uint8_t *cdb = req_iu(req)->srp.cmd.cdb;
uint8_t resp_data[36];
int rc, len, alen;
@@ -770,7 +781,7 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req)
static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
{
- union srp_iu *srp = &req->iu.srp;
+ union srp_iu *srp = &req_iu(req)->srp;
SCSIDevice *sdev;
int n, lun;
@@ -821,17 +832,16 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
{
- union viosrp_iu *iu = &req->iu;
+ union viosrp_iu *iu = req_iu(req);
vscsi_req *tmpreq;
int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE;
SCSIDevice *d;
uint64_t tag = iu->srp.rsp.tag;
uint8_t sol_not = iu->srp.cmd.sol_not;
- fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
- iu->srp.tsk_mgmt.tsk_mgmt_func);
-
- d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun);
+ trace_spapr_vscsi_process_tsk_mgmt(iu->srp.tsk_mgmt.tsk_mgmt_func);
+ d = vscsi_device_find(&s->bus,
+ be64_to_cpu(req_iu(req)->srp.tsk_mgmt.lun), &lun);
if (!d) {
resp = SRP_TSK_MGMT_FIELDS_INVALID;
} else {
@@ -842,7 +852,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
break;
}
- tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag);
+ tmpreq = vscsi_find_req(s, req_iu(req)->srp.tsk_mgmt.task_tag);
if (tmpreq && tmpreq->sreq) {
assert(tmpreq->sreq->hba_private);
scsi_req_cancel(tmpreq->sreq);
@@ -867,7 +877,8 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
tmpreq = &s->reqs[i];
- if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) {
+ if (req_iu(tmpreq)->srp.cmd.lun
+ != req_iu(req)->srp.tsk_mgmt.lun) {
continue;
}
if (!tmpreq->active || !tmpreq->sreq) {
@@ -889,6 +900,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
}
/* Compose the response here as */
+ QEMU_BUILD_BUG_ON(SRP_MAX_IU_DATA_LEN < 4);
memset(iu, 0, sizeof(struct srp_rsp) + 4);
iu->srp.rsp.opcode = SRP_RSP;
iu->srp.rsp.req_lim_delta = cpu_to_be32(1);
@@ -911,7 +923,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
{
- union srp_iu *srp = &req->iu.srp;
+ union srp_iu *srp = &req_iu(req)->srp;
int done = 1;
uint8_t opcode = srp->rsp.opcode;
@@ -948,7 +960,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
struct mad_adapter_info_data info;
int rc;
- sinfo = &req->iu.mad.adapter_info;
+ sinfo = &req_iu(req)->mad.adapter_info;
#if 0 /* What for ? */
rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer),
@@ -984,7 +996,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req)
uint64_t buffer;
int rc;
- vcap = &req->iu.mad.capabilities;
+ vcap = &req_iu(req)->mad.capabilities;
req_len = len = be16_to_cpu(vcap->common.length);
buffer = be64_to_cpu(vcap->buffer);
if (len > sizeof(cap)) {
@@ -1029,7 +1041,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req)
static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req)
{
- union mad_iu *mad = &req->iu.mad;
+ union mad_iu *mad = &req_iu(req)->mad;
bool request_handled = false;
uint64_t retlen = 0;
@@ -1088,7 +1100,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
* in our 256 bytes IUs. If not we'll have to increase the size
* of the structure.
*/
- if (crq->s.IU_length > sizeof(union viosrp_iu)) {
+ if (crq->s.IU_length > SRP_MAX_IU_LEN) {
fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n",
crq->s.IU_length);
vscsi_put_req(req);
@@ -1096,7 +1108,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
}
/* XXX Handle failure differently ? */
- if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu,
+ if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->viosrp_iu_buf,
crq->s.IU_length)) {
fprintf(stderr, "vscsi_got_payload: DMA read failure !\n");
vscsi_put_req(req);
diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events
index b0820052f8..9a4a60ca63 100644
--- a/hw/scsi/trace-events
+++ b/hw/scsi/trace-events
@@ -227,6 +227,7 @@ spapr_vscsi_command_complete_status(uint32_t status) "Command complete err=%"PRI
spapr_vscsi_save_request(uint32_t qtag, unsigned desc, unsigned offset) "saving tag=%"PRIu32", current desc#%u, offset=0x%x"
spapr_vscsi_load_request(uint32_t qtag, unsigned desc, unsigned offset) "restoring tag=%"PRIu32", current desc#%u, offset=0x%x"
spapr_vscsi_process_login(void) "Got login, sending response !"
+spapr_vscsi_process_tsk_mgmt(uint8_t func) "tsk_mgmt_func 0x%02x"
spapr_vscsi_queue_cmd_no_drive(uint64_t lun) "Command for lun 0x%08" PRIx64 " with no drive"
spapr_vscsi_queue_cmd(uint32_t qtag, unsigned cdb, const char *cmd, int lun, int ret) "Queued command tag 0x%"PRIx32" CMD 0x%x=%s LUN %d ret: %d"
spapr_vscsi_do_crq(unsigned c0, unsigned c1) "crq: %02x %02x ..."
diff --git a/hw/scsi/viosrp.h b/hw/scsi/viosrp.h
index d8e365db1e..e5f9768e8f 100644
--- a/hw/scsi/viosrp.h
+++ b/hw/scsi/viosrp.h
@@ -34,6 +34,8 @@
#ifndef PPC_VIOSRP_H
#define PPC_VIOSRP_H
+#include "hw/scsi/srp.h"
+
#define SRP_VERSION "16.a"
#define SRP_MAX_IU_LEN 256
#define SRP_MAX_LOC_LEN 32
@@ -47,7 +49,6 @@ union srp_iu {
struct srp_tsk_mgmt tsk_mgmt;
struct srp_cmd cmd;
struct srp_rsp rsp;
- uint8_t reserved[SRP_MAX_IU_LEN];
};
enum viosrp_crq_formats {
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 09110961a5..42d64a0368 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -79,10 +79,10 @@ typedef enum {
#define SPAPR_CAP_LARGE_DECREMENTER 0x08
/* Count Cache Flush Assist HW Instruction */
#define SPAPR_CAP_CCF_ASSIST 0x09
-/* FWNMI machine check handling */
-#define SPAPR_CAP_FWNMI_MCE 0x0A
+/* Implements PAPR FWNMI option */
+#define SPAPR_CAP_FWNMI 0x0A
/* Num Caps */
-#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI_MCE + 1)
+#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI + 1)
/*
* Capability Values
@@ -126,6 +126,7 @@ struct SpaprMachineClass {
bool pre_4_1_migration; /* don't migrate hpt-max-page-size */
bool linux_pci_probe;
bool smp_threads_vsmt; /* set VSMT to smp_threads by default */
+ hwaddr rma_limit; /* clamp the RMA to this size */
void (*phb_placement)(SpaprMachineState *spapr, uint32_t index,
uint64_t *buid, hwaddr *pio,
@@ -156,7 +157,6 @@ struct SpaprMachineState {
SpaprPendingHpt *pending_hpt; /* in-progress resize */
hwaddr rma_size;
- int vrma_adjust;
uint32_t fdt_size;
uint32_t fdt_initial_size;
void *fdt_blob;
@@ -192,14 +192,22 @@ struct SpaprMachineState {
* occurs during the unplug process. */
QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs;
- /* State related to "ibm,nmi-register" and "ibm,nmi-interlock" calls */
- target_ulong guest_machine_check_addr;
- /*
- * mc_status is set to -1 if mc is not in progress, else is set to the CPU
- * handling the mc.
+ /* State related to FWNMI option */
+
+ /* System Reset and Machine Check Notification Routine addresses
+ * registered by "ibm,nmi-register" RTAS call.
+ */
+ target_ulong fwnmi_system_reset_addr;
+ target_ulong fwnmi_machine_check_addr;
+
+ /* Machine Check FWNMI synchronization, fwnmi_machine_check_interlock is
+ * set to -1 if a FWNMI machine check is not in progress, else is set to
+ * the CPU that was delivered the machine check, and is set back to -1
+ * when that CPU makes an "ibm,nmi-interlock" RTAS call. The cond is used
+ * to synchronize other CPUs.
*/
- int mc_status;
- QemuCond mc_delivery_cond;
+ int fwnmi_machine_check_interlock;
+ QemuCond fwnmi_machine_check_interlock_cond;
/*< public >*/
char *kvm_type;
@@ -736,6 +744,7 @@ void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr);
#define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000))
#define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff)
+#define RTAS_SIZE 2048
#define RTAS_ERROR_LOG_MAX 2048
/* Offset from rtas-base where error log is placed */
@@ -795,7 +804,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space);
void spapr_events_init(SpaprMachineState *sm);
void spapr_dt_events(SpaprMachineState *sm, void *fdt);
void close_htab_fd(SpaprMachineState *spapr);
-void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr);
+void spapr_setup_hpt(SpaprMachineState *spapr);
void spapr_free_hpt(SpaprMachineState *spapr);
SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn);
void spapr_tce_table_enable(SpaprTceTable *tcet,
@@ -824,6 +833,7 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize);
void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift,
Error **errp);
void spapr_clear_pending_events(SpaprMachineState *spapr);
+void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr);
int spapr_max_server_number(SpaprMachineState *spapr);
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
uint64_t pte0, uint64_t pte1);
diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h
index 1c4cc6559c..7aed8f555b 100644
--- a/include/hw/ppc/spapr_cpu_core.h
+++ b/include/hw/ppc/spapr_cpu_core.h
@@ -40,7 +40,9 @@ typedef struct SpaprCpuCoreClass {
} SpaprCpuCoreClass;
const char *spapr_get_cpu_core_type(const char *cpu_type);
-void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3);
+void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip,
+ target_ulong r1, target_ulong r3,
+ target_ulong r4);
typedef struct SpaprCpuState {
uint64_t vpa_addr;
diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h
index 2bed517a2b..d4dee9e06a 100644
--- a/include/hw/ppc/spapr_ovec.h
+++ b/include/hw/ppc/spapr_ovec.h
@@ -72,8 +72,8 @@ void spapr_ovec_set(SpaprOptionVector *ov, long bitnr);
void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr);
bool spapr_ovec_test(SpaprOptionVector *ov, long bitnr);
SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector);
-int spapr_ovec_populate_dt(void *fdt, int fdt_offset,
- SpaprOptionVector *ov, const char *name);
+int spapr_dt_ovec(void *fdt, int fdt_offset,
+ SpaprOptionVector *ov, const char *name);
/* migration */
extern const VMStateDescription vmstate_spapr_ovec;
diff --git a/pc-bios/README b/pc-bios/README
index d6d33d237f..f54c2743d0 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -14,7 +14,7 @@
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
https://github.com/aik/SLOF, and the image currently in qemu is
- built from git tag qemu-slof-20191217.
+ built from git tag qemu-slof-20200317.
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
legacy x86 software to communicate with an attached serial console as
diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin
index 78d8b26cbb..40499a1451 100644
--- a/pc-bios/slof.bin
+++ b/pc-bios/slof.bin
Binary files differ
diff --git a/roms/SLOF b/roms/SLOF
-Subproject 9546892a80d5a4c73deea6719de46372f007f4a
+Subproject ab6984f5a6d054e1f634dda855b32e535711197
diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h
index e499575dc8..15d6b54a7d 100644
--- a/target/ppc/cpu-qom.h
+++ b/target/ppc/cpu-qom.h
@@ -177,6 +177,7 @@ typedef struct PowerPCCPUClass {
uint64_t insns_flags;
uint64_t insns_flags2;
uint64_t msr_mask;
+ uint64_t lpcr_mask; /* Available bits in the LPCR */
uint64_t lpcr_pm; /* Power-saving mode Exit Cause Enable bits */
powerpc_mmu_t mmu_model;
powerpc_excp_t excp_model;
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index b283042515..f8c7d6f19c 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -24,8 +24,6 @@
#include "exec/cpu-defs.h"
#include "cpu-qom.h"
-/* #define PPC_EMULATE_32BITS_HYPV */
-
#define TCG_GUEST_DEFAULT_MO 0
#define TARGET_PAGE_BITS_64K 16
@@ -300,13 +298,12 @@ typedef struct ppc_v3_pate_t {
#define MSR_SF 63 /* Sixty-four-bit mode hflags */
#define MSR_TAG 62 /* Tag-active mode (POWERx ?) */
#define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */
-#define MSR_SHV 60 /* hypervisor state hflags */
+#define MSR_HV 60 /* hypervisor state hflags */
#define MSR_TS0 34 /* Transactional state, 2 bits (Book3s) */
#define MSR_TS1 33
#define MSR_TM 32 /* Transactional Memory Available (Book3s) */
#define MSR_CM 31 /* Computation mode for BookE hflags */
#define MSR_ICM 30 /* Interrupt computation mode for BookE */
-#define MSR_THV 29 /* hypervisor state for 32 bits PowerPC hflags */
#define MSR_GS 28 /* guest state for BookE */
#define MSR_UCLE 26 /* User-mode cache lock enable for BookE */
#define MSR_VR 25 /* altivec available x hflags */
@@ -401,10 +398,13 @@ typedef struct ppc_v3_pate_t {
#define msr_sf ((env->msr >> MSR_SF) & 1)
#define msr_isf ((env->msr >> MSR_ISF) & 1)
-#define msr_shv ((env->msr >> MSR_SHV) & 1)
+#if defined(TARGET_PPC64)
+#define msr_hv ((env->msr >> MSR_HV) & 1)
+#else
+#define msr_hv (0)
+#endif
#define msr_cm ((env->msr >> MSR_CM) & 1)
#define msr_icm ((env->msr >> MSR_ICM) & 1)
-#define msr_thv ((env->msr >> MSR_THV) & 1)
#define msr_gs ((env->msr >> MSR_GS) & 1)
#define msr_ucle ((env->msr >> MSR_UCLE) & 1)
#define msr_vr ((env->msr >> MSR_VR) & 1)
@@ -449,16 +449,9 @@ typedef struct ppc_v3_pate_t {
/* Hypervisor bit is more specific */
#if defined(TARGET_PPC64)
-#define MSR_HVB (1ULL << MSR_SHV)
-#define msr_hv msr_shv
-#else
-#if defined(PPC_EMULATE_32BITS_HYPV)
-#define MSR_HVB (1ULL << MSR_THV)
-#define msr_hv msr_thv
+#define MSR_HVB (1ULL << MSR_HV)
#else
#define MSR_HVB (0ULL)
-#define msr_hv (0)
-#endif
#endif
/* DSISR */
@@ -1051,10 +1044,6 @@ struct CPUPPCState {
uint32_t flags;
uint64_t insns_flags;
uint64_t insns_flags2;
-#if defined(TARGET_PPC64)
- ppc_slb_t vrma_slb;
- target_ulong rmls;
-#endif
int error_code;
uint32_t pending_interrupts;
@@ -1231,7 +1220,8 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
int cpuid, void *opaque);
#ifndef CONFIG_USER_ONLY
-void ppc_cpu_do_system_reset(CPUState *cs);
+void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector);
+void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector);
extern const VMStateDescription vmstate_ppc_cpu;
#endif
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 027f54c0ed..08bc885ca6 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -128,6 +128,37 @@ static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail)
return offset;
}
+static inline void powerpc_set_excp_state(PowerPCCPU *cpu,
+ target_ulong vector, target_ulong msr)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+
+ /*
+ * We don't use hreg_store_msr here as already have treated any
+ * special case that could occur. Just store MSR and update hflags
+ *
+ * Note: We *MUST* not use hreg_store_msr() as-is anyway because it
+ * will prevent setting of the HV bit which some exceptions might need
+ * to do.
+ */
+ env->msr = msr & env->msr_mask;
+ hreg_compute_hflags(env);
+ env->nip = vector;
+ /* Reset exception state */
+ cs->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+
+ /* Reset the reservation */
+ env->reserve_addr = -1;
+
+ /*
+ * Any interrupt is context synchronizing, check if TCG TLB needs
+ * a delayed flush on ppc64
+ */
+ check_tlb_flush(env, false);
+}
+
/*
* Note that this function should be greatly optimized when called
* with a constant excp, from ppc_hw_interrupt
@@ -768,29 +799,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
}
}
#endif
- /*
- * We don't use hreg_store_msr here as already have treated any
- * special case that could occur. Just store MSR and update hflags
- *
- * Note: We *MUST* not use hreg_store_msr() as-is anyway because it
- * will prevent setting of the HV bit which some exceptions might need
- * to do.
- */
- env->msr = new_msr & env->msr_mask;
- hreg_compute_hflags(env);
- env->nip = vector;
- /* Reset exception state */
- cs->exception_index = POWERPC_EXCP_NONE;
- env->error_code = 0;
- /* Reset the reservation */
- env->reserve_addr = -1;
-
- /*
- * Any interrupt is context synchronizing, check if TCG TLB needs
- * a delayed flush on ppc64
- */
- check_tlb_flush(env, false);
+ powerpc_set_excp_state(cpu, vector, new_msr);
}
void ppc_cpu_do_interrupt(CPUState *cs)
@@ -951,12 +961,35 @@ static void ppc_hw_interrupt(CPUPPCState *env)
}
}
-void ppc_cpu_do_system_reset(CPUState *cs)
+void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET);
+ if (vector != -1) {
+ env->nip = vector;
+ }
+}
+
+void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ target_ulong msr = 0;
+
+ /*
+ * Set MSR and NIP for the handler, SRR0/1, DAR and DSISR have already
+ * been set by KVM.
+ */
+ msr = (1ULL << MSR_ME);
+ msr |= env->msr & (1ULL << MSR_SF);
+ if (!(*pcc->interrupts_big_endian)(cpu)) {
+ msr |= (1ULL << MSR_LE);
+ }
+
+ powerpc_set_excp_state(cpu, vector, msr);
}
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 7f44b1aa1a..597f72be1b 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -2113,7 +2113,7 @@ void kvmppc_error_append_smt_possible_hint(Error *const *errp)
#ifdef TARGET_PPC64
-uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift)
+uint64_t kvmppc_vrma_limit(unsigned int hash_shift)
{
struct kvm_ppc_smmu_info info;
long rampagesize, best_page_shift;
@@ -2140,8 +2140,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift)
}
}
- return MIN(current_size,
- 1ULL << (best_page_shift + hash_shift - 7));
+ return 1ULL << (best_page_shift + hash_shift - 7);
}
#endif
diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h
index 9e4f2357cc..332fa0aa1c 100644
--- a/target/ppc/kvm_ppc.h
+++ b/target/ppc/kvm_ppc.h
@@ -47,7 +47,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift,
int *pfd, bool need_vfio);
int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size);
int kvmppc_reset_htab(int shift_hint);
-uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift);
+uint64_t kvmppc_vrma_limit(unsigned int hash_shift);
bool kvmppc_has_cap_spapr_vfio(void);
#endif /* !CONFIG_USER_ONLY */
bool kvmppc_has_cap_epr(void);
@@ -255,10 +255,9 @@ static inline int kvmppc_reset_htab(int shift_hint)
return 0;
}
-static inline uint64_t kvmppc_rma_size(uint64_t current_size,
- unsigned int hash_shift)
+static inline uint64_t kvmppc_vrma_limit(unsigned int hash_shift)
{
- return ram_size;
+ g_assert_not_reached();
}
static inline bool kvmppc_hpt_needs_host_contiguous_pages(void)
diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c
index da8966ccf5..34f6009b1e 100644
--- a/target/ppc/mmu-hash64.c
+++ b/target/ppc/mmu-hash64.c
@@ -18,6 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
+#include "qemu/units.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
@@ -668,6 +669,21 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu,
return 0;
}
+static bool ppc_hash64_use_vrma(CPUPPCState *env)
+{
+ switch (env->mmu_model) {
+ case POWERPC_MMU_3_00:
+ /*
+ * ISAv3.0 (POWER9) always uses VRMA, the VPM0 field and RMOR
+ * register no longer exist
+ */
+ return true;
+
+ default:
+ return !!(env->spr[SPR_LPCR] & LPCR_VPM0);
+ }
+}
+
static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code)
{
CPUPPCState *env = &POWERPC_CPU(cs)->env;
@@ -676,15 +692,7 @@ static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code)
if (msr_ir) {
vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
} else {
- switch (env->mmu_model) {
- case POWERPC_MMU_3_00:
- /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */
- vpm = true;
- break;
- default:
- vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
- break;
- }
+ vpm = ppc_hash64_use_vrma(env);
}
if (vpm && !msr_hv) {
cs->exception_index = POWERPC_EXCP_HISI;
@@ -702,15 +710,7 @@ static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr)
if (msr_dr) {
vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
} else {
- switch (env->mmu_model) {
- case POWERPC_MMU_3_00:
- /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */
- vpm = true;
- break;
- default:
- vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
- break;
- }
+ vpm = ppc_hash64_use_vrma(env);
}
if (vpm && !msr_hv) {
cs->exception_index = POWERPC_EXCP_HDSI;
@@ -758,11 +758,67 @@ static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1)
stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80);
}
+static target_ulong rmls_limit(PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+ /*
+ * In theory the meanings of RMLS values are implementation
+ * dependent. In practice, this seems to have been the set from
+ * POWER4+..POWER8, and RMLS is no longer supported in POWER9.
+ *
+ * Unsupported values mean the OS has shot itself in the
+ * foot. Return a 0-sized RMA in this case, which we expect
+ * to trigger an immediate DSI or ISI
+ */
+ static const target_ulong rma_sizes[16] = {
+ [0] = 256 * GiB,
+ [1] = 16 * GiB,
+ [2] = 1 * GiB,
+ [3] = 64 * MiB,
+ [4] = 256 * MiB,
+ [7] = 128 * MiB,
+ [8] = 32 * MiB,
+ };
+ target_ulong rmls = (env->spr[SPR_LPCR] & LPCR_RMLS) >> LPCR_RMLS_SHIFT;
+
+ return rma_sizes[rmls];
+}
+
+static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb)
+{
+ CPUPPCState *env = &cpu->env;
+ target_ulong lpcr = env->spr[SPR_LPCR];
+ uint32_t vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT;
+ target_ulong vsid = SLB_VSID_VRMA | ((vrmasd << 4) & SLB_VSID_LLP_MASK);
+ int i;
+
+ for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
+ const PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i];
+
+ if (!sps->page_shift) {
+ break;
+ }
+
+ if ((vsid & SLB_VSID_LLP_MASK) == sps->slb_enc) {
+ slb->esid = SLB_ESID_V;
+ slb->vsid = vsid;
+ slb->sps = sps;
+ return 0;
+ }
+ }
+
+ error_report("Bad page size encoding in LPCR[VRMASD]; LPCR=0x"
+ TARGET_FMT_lx"\n", lpcr);
+
+ return -1;
+}
+
int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
int rwx, int mmu_idx)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
+ ppc_slb_t vrma_slbe;
ppc_slb_t *slb;
unsigned apshift;
hwaddr ptex;
@@ -789,27 +845,32 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
*/
raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
- /* In HV mode, add HRMOR if top EA bit is clear */
- if (msr_hv || !env->has_hv_mode) {
+ if (cpu->vhyp) {
+ /*
+ * In virtual hypervisor mode, there's nothing to do:
+ * EA == GPA == qemu guest address
+ */
+ } else if (msr_hv || !env->has_hv_mode) {
+ /* In HV mode, add HRMOR if top EA bit is clear */
if (!(eaddr >> 63)) {
raddr |= env->spr[SPR_HRMOR];
}
- } else {
- /* Otherwise, check VPM for RMA vs VRMA */
- if (env->spr[SPR_LPCR] & LPCR_VPM0) {
- slb = &env->vrma_slb;
- if (slb->sps) {
- goto skip_slb_search;
- }
- /* Not much else to do here */
+ } else if (ppc_hash64_use_vrma(env)) {
+ /* Emulated VRMA mode */
+ slb = &vrma_slbe;
+ if (build_vrma_slbe(cpu, slb) != 0) {
+ /* Invalid VRMA setup, machine check */
cs->exception_index = POWERPC_EXCP_MCHECK;
env->error_code = 0;
return 1;
- } else if (raddr < env->rmls) {
- /* RMA. Check bounds in RMLS */
- raddr |= env->spr[SPR_RMOR];
- } else {
- /* The access failed, generate the approriate interrupt */
+ }
+
+ goto skip_slb_search;
+ } else {
+ target_ulong limit = rmls_limit(cpu);
+
+ /* Emulated old-style RMO mode, bounds check against RMLS */
+ if (raddr >= limit) {
if (rwx == 2) {
ppc_hash64_set_isi(cs, SRR1_PROTFAULT);
} else {
@@ -821,6 +882,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
}
return 1;
}
+
+ raddr |= env->spr[SPR_RMOR];
}
tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
@@ -943,6 +1006,7 @@ skip_slb_search:
hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
{
CPUPPCState *env = &cpu->env;
+ ppc_slb_t vrma_slbe;
ppc_slb_t *slb;
hwaddr ptex, raddr;
ppc_hash_pte64_t pte;
@@ -953,22 +1017,29 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
/* In real mode the top 4 effective address bits are ignored */
raddr = addr & 0x0FFFFFFFFFFFFFFFULL;
- /* In HV mode, add HRMOR if top EA bit is clear */
- if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) {
+ if (cpu->vhyp) {
+ /*
+ * In virtual hypervisor mode, there's nothing to do:
+ * EA == GPA == qemu guest address
+ */
+ return raddr;
+ } else if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) {
+ /* In HV mode, add HRMOR if top EA bit is clear */
return raddr | env->spr[SPR_HRMOR];
- }
+ } else if (ppc_hash64_use_vrma(env)) {
+ /* Emulated VRMA mode */
+ slb = &vrma_slbe;
+ if (build_vrma_slbe(cpu, slb) != 0) {
+ return -1;
+ }
+ } else {
+ target_ulong limit = rmls_limit(cpu);
- /* Otherwise, check VPM for RMA vs VRMA */
- if (env->spr[SPR_LPCR] & LPCR_VPM0) {
- slb = &env->vrma_slb;
- if (!slb->sps) {
+ /* Emulated old-style RMO mode, bounds check against RMLS */
+ if (raddr >= limit) {
return -1;
}
- } else if (raddr < env->rmls) {
- /* RMA. Check bounds in RMLS */
return raddr | env->spr[SPR_RMOR];
- } else {
- return -1;
}
} else {
slb = slb_lookup(cpu, addr);
@@ -997,168 +1068,12 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex,
cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH;
}
-static void ppc_hash64_update_rmls(PowerPCCPU *cpu)
-{
- CPUPPCState *env = &cpu->env;
- uint64_t lpcr = env->spr[SPR_LPCR];
-
- /*
- * This is the full 4 bits encoding of POWER8. Previous
- * CPUs only support a subset of these but the filtering
- * is done when writing LPCR
- */
- switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) {
- case 0x8: /* 32MB */
- env->rmls = 0x2000000ull;
- break;
- case 0x3: /* 64MB */
- env->rmls = 0x4000000ull;
- break;
- case 0x7: /* 128MB */
- env->rmls = 0x8000000ull;
- break;
- case 0x4: /* 256MB */
- env->rmls = 0x10000000ull;
- break;
- case 0x2: /* 1GB */
- env->rmls = 0x40000000ull;
- break;
- case 0x1: /* 16GB */
- env->rmls = 0x400000000ull;
- break;
- default:
- /* What to do here ??? */
- env->rmls = 0;
- }
-}
-
-static void ppc_hash64_update_vrma(PowerPCCPU *cpu)
-{
- CPUPPCState *env = &cpu->env;
- const PPCHash64SegmentPageSizes *sps = NULL;
- target_ulong esid, vsid, lpcr;
- ppc_slb_t *slb = &env->vrma_slb;
- uint32_t vrmasd;
- int i;
-
- /* First clear it */
- slb->esid = slb->vsid = 0;
- slb->sps = NULL;
-
- /* Is VRMA enabled ? */
- lpcr = env->spr[SPR_LPCR];
- if (!(lpcr & LPCR_VPM0)) {
- return;
- }
-
- /*
- * Make one up. Mostly ignore the ESID which will not be needed
- * for translation
- */
- vsid = SLB_VSID_VRMA;
- vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT;
- vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP);
- esid = SLB_ESID_V;
-
- for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
- const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i];
-
- if (!sps1->page_shift) {
- break;
- }
-
- if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) {
- sps = sps1;
- break;
- }
- }
-
- if (!sps) {
- error_report("Bad page size encoding esid 0x"TARGET_FMT_lx
- " vsid 0x"TARGET_FMT_lx, esid, vsid);
- return;
- }
-
- slb->vsid = vsid;
- slb->esid = esid;
- slb->sps = sps;
-}
-
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
{
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
CPUPPCState *env = &cpu->env;
- uint64_t lpcr = 0;
-
- /* Filter out bits */
- switch (env->mmu_model) {
- case POWERPC_MMU_64B: /* 970 */
- if (val & 0x40) {
- lpcr |= LPCR_LPES0;
- }
- if (val & 0x8000000000000000ull) {
- lpcr |= LPCR_LPES1;
- }
- if (val & 0x20) {
- lpcr |= (0x4ull << LPCR_RMLS_SHIFT);
- }
- if (val & 0x4000000000000000ull) {
- lpcr |= (0x2ull << LPCR_RMLS_SHIFT);
- }
- if (val & 0x2000000000000000ull) {
- lpcr |= (0x1ull << LPCR_RMLS_SHIFT);
- }
- env->spr[SPR_RMOR] = ((lpcr >> 41) & 0xffffull) << 26;
- /*
- * XXX We could also write LPID from HID4 here
- * but since we don't tag any translation on it
- * it doesn't actually matter
- *
- * XXX For proper emulation of 970 we also need
- * to dig HRMOR out of HID5
- */
- break;
- case POWERPC_MMU_2_03: /* P5p */
- lpcr = val & (LPCR_RMLS | LPCR_ILE |
- LPCR_LPES0 | LPCR_LPES1 |
- LPCR_RMI | LPCR_HDICE);
- break;
- case POWERPC_MMU_2_06: /* P7 */
- lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD |
- LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
- LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 |
- LPCR_MER | LPCR_TC |
- LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE);
- break;
- case POWERPC_MMU_2_07: /* P8 */
- lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV |
- LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
- LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 |
- LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 |
- LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE);
- break;
- case POWERPC_MMU_3_00: /* P9 */
- lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
- (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL |
- LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD |
- (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE |
- LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC |
- LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE);
- /*
- * If we have a virtual hypervisor, we need to bring back RMLS. It
- * doesn't exist on an actual P9 but that's all we know how to
- * configure with softmmu at the moment
- */
- if (cpu->vhyp) {
- lpcr |= (val & LPCR_RMLS);
- }
- break;
- default:
- ;
- }
- env->spr[SPR_LPCR] = lpcr;
- ppc_hash64_update_rmls(cpu);
- ppc_hash64_update_vrma(cpu);
+ env->spr[SPR_LPCR] = val & pcc->lpcr_mask;
}
void helper_store_lpcr(CPUPPCState *env, target_ulong val)
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 36fa27367c..127c82a24e 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -1938,15 +1938,17 @@ static void gen_rlwinm(DisasContext *ctx)
me += 32;
#endif
mask = MASK(mb, me);
- if (sh == 0) {
- tcg_gen_andi_tl(t_ra, t_rs, mask);
- } else if (mask <= 0xffffffffu) {
- TCGv_i32 t0 = tcg_temp_new_i32();
- tcg_gen_trunc_tl_i32(t0, t_rs);
- tcg_gen_rotli_i32(t0, t0, sh);
- tcg_gen_andi_i32(t0, t0, mask);
- tcg_gen_extu_i32_tl(t_ra, t0);
- tcg_temp_free_i32(t0);
+ if (mask <= 0xffffffffu) {
+ if (sh == 0) {
+ tcg_gen_andi_tl(t_ra, t_rs, mask);
+ } else {
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(t0, t_rs);
+ tcg_gen_rotli_i32(t0, t0, sh);
+ tcg_gen_andi_i32(t0, t0, mask);
+ tcg_gen_extu_i32_tl(t_ra, t0);
+ tcg_temp_free_i32(t0);
+ }
} else {
#if defined(TARGET_PPC64)
tcg_gen_deposit_i64(t_ra, t_rs, t_rs, 32, 32);
diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
index 53995f62ea..0ae145e18d 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -7895,25 +7895,21 @@ static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn)
{
gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);
}
-
-static void spr_write_970_hid4(DisasContext *ctx, int sprn, int gprn)
-{
-#if defined(TARGET_PPC64)
- spr_write_generic(ctx, sprn, gprn);
- gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);
-#endif
-}
-
#endif /* !defined(CONFIG_USER_ONLY) */
static void gen_spr_970_lpar(CPUPPCState *env)
{
#if !defined(CONFIG_USER_ONLY)
- /* Logical partitionning */
- /* PPC970: HID4 is effectively the LPCR */
+ /*
+ * PPC970: HID4 covers things later controlled by the LPCR and
+ * RMOR in later CPUs, but with a different encoding. We only
+ * support the 970 in "Apple mode" which has all hypervisor
+ * facilities disabled by strapping, so we can basically just
+ * ignore it
+ */
spr_register(env, SPR_970_HID4, "HID4",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_970_hid4,
+ &spr_read_generic, &spr_write_generic,
0x00000000);
#endif
}
@@ -8019,12 +8015,16 @@ static void gen_spr_book3s_ids(CPUPPCState *env)
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
- spr_register_hv(env, SPR_RMOR, "RMOR",
+ spr_register_hv(env, SPR_HRMOR, "HRMOR",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
- spr_register_hv(env, SPR_HRMOR, "HRMOR",
+}
+
+static void gen_spr_rmor(CPUPPCState *env)
+{
+ spr_register_hv(env, SPR_RMOR, "RMOR",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
@@ -8476,6 +8476,8 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
(1ull << MSR_DR) |
(1ull << MSR_PMM) |
(1ull << MSR_RI);
+ pcc->lpcr_mask = LPCR_RMLS | LPCR_ILE | LPCR_LPES0 | LPCR_LPES1 |
+ LPCR_RMI | LPCR_HDICE;
pcc->mmu_model = POWERPC_MMU_2_03;
#if defined(CONFIG_SOFTMMU)
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
@@ -8492,44 +8494,6 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
pcc->l1_icache_size = 0x10000;
}
-/*
- * The CPU used to have a "compat" property which set the
- * compatibility mode PVR. However, this was conceptually broken - it
- * only makes sense on the pseries machine type (otherwise the guest
- * owns the PCR and can control the compatibility mode itself). It's
- * been replaced with the 'max-cpu-compat' property on the pseries
- * machine type. For backwards compatibility, pseries specially
- * parses the -cpu parameter and converts old compat= parameters into
- * the appropriate machine parameters. This stub implementation of
- * the parameter catches any uses on explicitly created CPUs.
- */
-static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- QNull *null = NULL;
-
- if (!qtest_enabled()) {
- warn_report("CPU 'compat' property is deprecated and has no effect; "
- "use max-cpu-compat machine property instead");
- }
- visit_type_null(v, name, &null, NULL);
- qobject_unref(null);
-}
-
-static const PropertyInfo ppc_compat_deprecated_propinfo = {
- .name = "str",
- .description = "compatibility mode (deprecated)",
- .get = getset_compat_deprecated,
- .set = getset_compat_deprecated,
-};
-static Property powerpc_servercpu_properties[] = {
- {
- .name = "compat",
- .info = &ppc_compat_deprecated_propinfo,
- },
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void init_proc_POWER7(CPUPPCState *env)
{
/* Common Registers */
@@ -8539,6 +8503,7 @@ static void init_proc_POWER7(CPUPPCState *env)
/* POWER7 Specific Registers */
gen_spr_book3s_ids(env);
+ gen_spr_rmor(env);
gen_spr_amr(env);
gen_spr_book3s_purr(env);
gen_spr_power5p_common(env);
@@ -8611,7 +8576,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
dc->fw_name = "PowerPC,POWER7";
dc->desc = "POWER7";
- device_class_set_props(dc, powerpc_servercpu_properties);
pcc->pvr_match = ppc_pvr_match_power7;
pcc->pcr_mask = PCR_VEC_DIS | PCR_VSX_DIS | PCR_COMPAT_2_05;
pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
@@ -8652,6 +8616,12 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
(1ull << MSR_PMM) |
(1ull << MSR_RI) |
(1ull << MSR_LE);
+ pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD |
+ LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
+ LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 |
+ LPCR_MER | LPCR_TC |
+ LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE;
+ pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2;
pcc->mmu_model = POWERPC_MMU_2_06;
#if defined(CONFIG_SOFTMMU)
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
@@ -8668,7 +8638,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
- pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2;
}
static void init_proc_POWER8(CPUPPCState *env)
@@ -8680,6 +8649,7 @@ static void init_proc_POWER8(CPUPPCState *env)
/* POWER8 Specific Registers */
gen_spr_book3s_ids(env);
+ gen_spr_rmor(env);
gen_spr_amr(env);
gen_spr_iamr(env);
gen_spr_book3s_purr(env);
@@ -8776,7 +8746,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
dc->fw_name = "PowerPC,POWER8";
dc->desc = "POWER8";
- device_class_set_props(dc, powerpc_servercpu_properties);
pcc->pvr_match = ppc_pvr_match_power8;
pcc->pcr_mask = PCR_TM_DIS | PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
@@ -8804,7 +8773,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
PPC2_TM | PPC2_PM_ISA206;
pcc->msr_mask = (1ull << MSR_SF) |
- (1ull << MSR_SHV) |
+ (1ull << MSR_HV) |
(1ull << MSR_TM) |
(1ull << MSR_VR) |
(1ull << MSR_VSX) |
@@ -8823,6 +8792,13 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
(1ull << MSR_TS0) |
(1ull << MSR_TS1) |
(1ull << MSR_LE);
+ pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV |
+ LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
+ LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 |
+ LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 |
+ LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE;
+ pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 |
+ LPCR_P8_PECE3 | LPCR_P8_PECE4;
pcc->mmu_model = POWERPC_MMU_2_07;
#if defined(CONFIG_SOFTMMU)
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
@@ -8840,8 +8816,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
- pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 |
- LPCR_P8_PECE3 | LPCR_P8_PECE4;
}
#ifdef CONFIG_SOFTMMU
@@ -8988,7 +8962,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
dc->fw_name = "PowerPC,POWER9";
dc->desc = "POWER9";
- device_class_set_props(dc, powerpc_servercpu_properties);
pcc->pvr_match = ppc_pvr_match_power9;
pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07;
pcc->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 |
@@ -9017,7 +8990,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL;
pcc->msr_mask = (1ull << MSR_SF) |
- (1ull << MSR_SHV) |
+ (1ull << MSR_HV) |
(1ull << MSR_TM) |
(1ull << MSR_VR) |
(1ull << MSR_VSX) |
@@ -9034,6 +9007,14 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
(1ull << MSR_PMM) |
(1ull << MSR_RI) |
(1ull << MSR_LE);
+ pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
+ (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL |
+ LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD |
+ (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE |
+ LPCR_DEE | LPCR_OEE))
+ | LPCR_MER | LPCR_GTSE | LPCR_TC |
+ LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE;
+ pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE;
pcc->mmu_model = POWERPC_MMU_3_00;
#if defined(CONFIG_SOFTMMU)
pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault;
@@ -9053,7 +9034,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
- pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE;
}
#ifdef CONFIG_SOFTMMU
@@ -9198,7 +9178,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
dc->fw_name = "PowerPC,POWER10";
dc->desc = "POWER10";
- device_class_set_props(dc, powerpc_servercpu_properties);
pcc->pvr_match = ppc_pvr_match_power10;
pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07 |
PCR_COMPAT_3_00;
@@ -9228,7 +9207,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL;
pcc->msr_mask = (1ull << MSR_SF) |
- (1ull << MSR_SHV) |
+ (1ull << MSR_HV) |
(1ull << MSR_TM) |
(1ull << MSR_VR) |
(1ull << MSR_VSX) |
@@ -9245,6 +9224,14 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
(1ull << MSR_PMM) |
(1ull << MSR_RI) |
(1ull << MSR_LE);
+ pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
+ (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL |
+ LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD |
+ (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE |
+ LPCR_DEE | LPCR_OEE))
+ | LPCR_MER | LPCR_GTSE | LPCR_TC |
+ LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE;
+ pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE;
pcc->mmu_model = POWERPC_MMU_3_00;
#if defined(CONFIG_SOFTMMU)
pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault;
@@ -9263,7 +9250,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
- pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE;
}
#if !defined(CONFIG_USER_ONLY)
@@ -10486,6 +10472,8 @@ static void ppc_cpu_parse_featurestr(const char *type, char *features,
*s = '\0';
for (i = 0; inpieces[i]; i++) {
if (g_str_has_prefix(inpieces[i], "compat=")) {
+ warn_report_once("CPU 'compat' property is deprecated; "
+ "use max-cpu-compat machine property instead");
compat_str = inpieces[i];
continue;
}
diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h
index d9c4c22343..49bd72d20b 100644
--- a/tests/qtest/libqos/libqos-spapr.h
+++ b/tests/qtest/libqos/libqos-spapr.h
@@ -12,7 +12,6 @@ void qtest_spapr_shutdown(QOSState *qs);
"cap-cfpc=broken," \
"cap-sbbc=broken," \
"cap-ibs=broken," \
- "cap-ccf-assist=off," \
- "cap-fwnmi-mce=off"
+ "cap-ccf-assist=off,"
#endif