76daa3
From 3486821cd193da11753933c645883f96c8b035de Mon Sep 17 00:00:00 2001
76daa3
From: David Gibson <dgibson@redhat.com>
76daa3
Date: Thu, 27 Apr 2017 02:15:55 +0200
76daa3
Subject: [PATCH 20/23] target/ppc: Implement H_REGISTER_PROCESS_TABLE H_CALL
76daa3
76daa3
RH-Author: David Gibson <dgibson@redhat.com>
76daa3
Message-id: <20170427021558.4884-5-dgibson@redhat.com>
76daa3
Patchwork-id: 74917
76daa3
O-Subject: [Pegas-1.0 qemu-kvm-rhev PATCH 4/7] target/ppc: Implement H_REGISTER_PROCESS_TABLE H_CALL
76daa3
Bugzilla: 1368786
76daa3
RH-Acked-by: Thomas Huth <thuth@redhat.com>
76daa3
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
76daa3
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
76daa3
From: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
76daa3
76daa3
The H_REGISTER_PROCESS_TABLE H_CALL is used by a guest to indicate to the
76daa3
hypervisor where in memory its process table is and how translation should
76daa3
be performed using this process table.
76daa3
76daa3
Provide the implementation of this H_CALL for a guest.
76daa3
76daa3
We first check for invalid flags, then parse the flags to determine the
76daa3
operation, and then check the other parameters for valid values based on
76daa3
the operation (register new table/deregister table/maintain registration).
76daa3
The process table is then stored in the appropriate location and registered
76daa3
with the hypervisor (if running under KVM), and the LPCR_[UPRT/GTSE] bits
76daa3
are updated as required.
76daa3
76daa3
Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
76daa3
Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
76daa3
[dwg: Correct missing prototype and uninitialized variable]
76daa3
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
76daa3
76daa3
(cherry picked from commit b4db54132ffeadafa9516cc553ba9548e42d42ad)
76daa3
76daa3
Siged-off-by: David Gibson <dgibson@redhat.com>
76daa3
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
---
76daa3
 hw/ppc/spapr.c         |  35 +++++++++------
76daa3
 hw/ppc/spapr_hcall.c   | 113 +++++++++++++++++++++++++++++++++++++++++++++++--
76daa3
 include/hw/ppc/spapr.h |   2 +
76daa3
 target/ppc/kvm.c       |  31 ++++++++++++++
76daa3
 target/ppc/kvm_ppc.h   |  10 +++++
76daa3
 5 files changed, 176 insertions(+), 15 deletions(-)
76daa3
76daa3
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
76daa3
index 6995c7a..8b9f877 100644
76daa3
--- a/hw/ppc/spapr.c
76daa3
+++ b/hw/ppc/spapr.c
76daa3
@@ -40,6 +40,7 @@
76daa3
 #include "kvm_ppc.h"
76daa3
 #include "migration/migration.h"
76daa3
 #include "mmu-hash64.h"
76daa3
+#include "mmu-book3s-v3.h"
76daa3
 #include "qom/cpu.h"
76daa3
 
76daa3
 #include "hw/boards.h"
76daa3
@@ -1114,7 +1115,7 @@ static int get_htab_fd(sPAPRMachineState *spapr)
76daa3
     return spapr->htab_fd;
76daa3
 }
76daa3
 
76daa3
-static void close_htab_fd(sPAPRMachineState *spapr)
76daa3
+void close_htab_fd(sPAPRMachineState *spapr)
76daa3
 {
76daa3
     if (spapr->htab_fd >= 0) {
76daa3
         close(spapr->htab_fd);
76daa3
@@ -1241,6 +1242,19 @@ static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift,
76daa3
     }
76daa3
 }
76daa3
 
76daa3
+void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr)
76daa3
+{
76daa3
+    spapr_reallocate_hpt(spapr,
76daa3
+                     spapr_hpt_shift_for_ramsize(MACHINE(spapr)->maxram_size),
76daa3
+                     &error_fatal);
76daa3
+    if (spapr->vrma_adjust) {
76daa3
+        spapr->rma_size = kvmppc_rma_size(spapr_node0_size(),
76daa3
+                                          spapr->htab_shift);
76daa3
+    }
76daa3
+    /* We're setting up a hash table, so that means we're not radix */
76daa3
+    spapr->patb_entry = 0;
76daa3
+}
76daa3
+
76daa3
 static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque)
76daa3
 {
76daa3
     bool matched = false;
76daa3
@@ -1269,17 +1283,14 @@ static void ppc_spapr_reset(void)
76daa3
     /* Check for unknown sysbus devices */
76daa3
     foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
76daa3
 
76daa3
-    spapr->patb_entry = 0;
76daa3
-
76daa3
-    /* Allocate and/or reset the hash page table */
76daa3
-    spapr_reallocate_hpt(spapr,
76daa3
-                         spapr_hpt_shift_for_ramsize(machine->maxram_size),
76daa3
-                         &error_fatal);
76daa3
-
76daa3
-    /* Update the RMA size if necessary */
76daa3
-    if (spapr->vrma_adjust) {
76daa3
-        spapr->rma_size = kvmppc_rma_size(spapr_node0_size(),
76daa3
-                                          spapr->htab_shift);
76daa3
+    if (kvm_enabled() && kvmppc_has_cap_mmu_radix()) {
76daa3
+        /* If using KVM with radix mode available, VCPUs can be started
76daa3
+         * without a HPT because KVM will start them in radix mode.
76daa3
+         * Set the GR bit in PATB so that we know there is no HPT. */
76daa3
+        spapr->patb_entry = PATBE1_GR;
76daa3
+    } else {
76daa3
+        spapr->patb_entry = 0;
76daa3
+        spapr_setup_hpt_and_vrma(spapr);
76daa3
     }
76daa3
 
76daa3
     qemu_devices_reset();
76daa3
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
76daa3
index 7952129..a958fee 100644
76daa3
--- a/hw/ppc/spapr_hcall.c
76daa3
+++ b/hw/ppc/spapr_hcall.c
76daa3
@@ -12,6 +12,8 @@
76daa3
 #include "trace.h"
76daa3
 #include "kvm_ppc.h"
76daa3
 #include "hw/ppc/spapr_ovec.h"
76daa3
+#include "qemu/error-report.h"
76daa3
+#include "mmu-book3s-v3.h"
76daa3
 
76daa3
 struct SPRSyncState {
76daa3
     int spr;
76daa3
@@ -894,14 +896,119 @@ static target_ulong h_invalidate_pid(PowerPCCPU *cpu, sPAPRMachineState *spapr,
76daa3
     return H_FUNCTION;
76daa3
 }
76daa3
 
76daa3
+static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr,
76daa3
+                                       uint64_t patbe_old, uint64_t patbe_new)
76daa3
+{
76daa3
+    /*
76daa3
+     * We have 4 Options:
76daa3
+     * HASH->HASH || RADIX->RADIX || NOTHING->RADIX : Do Nothing
76daa3
+     * HASH->RADIX                                  : Free HPT
76daa3
+     * RADIX->HASH                                  : Allocate HPT
76daa3
+     * NOTHING->HASH                                : Allocate HPT
76daa3
+     * Note: NOTHING implies the case where we said the guest could choose
76daa3
+     *       later and so assumed radix and now it's called H_REG_PROC_TBL
76daa3
+     */
76daa3
+
76daa3
+    if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) {
76daa3
+        /* We assume RADIX, so this catches all the "Do Nothing" cases */
76daa3
+    } else if (!(patbe_old & PATBE1_GR)) {
76daa3
+        /* HASH->RADIX : Free HPT */
76daa3
+        g_free(spapr->htab);
76daa3
+        spapr->htab = NULL;
76daa3
+        spapr->htab_shift = 0;
76daa3
+        close_htab_fd(spapr);
76daa3
+    } else if (!(patbe_new & PATBE1_GR)) {
76daa3
+        /* RADIX->HASH || NOTHING->HASH : Allocate HPT */
76daa3
+        spapr_setup_hpt_and_vrma(spapr);
76daa3
+    }
76daa3
+    return;
76daa3
+}
76daa3
+
76daa3
+#define FLAGS_MASK              0x01FULL
76daa3
+#define FLAG_MODIFY             0x10
76daa3
+#define FLAG_REGISTER           0x08
76daa3
+#define FLAG_RADIX              0x04
76daa3
+#define FLAG_HASH_PROC_TBL      0x02
76daa3
+#define FLAG_GTSE               0x01
76daa3
+
76daa3
 static target_ulong h_register_process_table(PowerPCCPU *cpu,
76daa3
                                              sPAPRMachineState *spapr,
76daa3
                                              target_ulong opcode,
76daa3
                                              target_ulong *args)
76daa3
 {
76daa3
-    qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n",
76daa3
-                  opcode, " (H_REGISTER_PROC_TBL)");
76daa3
-    return H_FUNCTION;
76daa3
+    CPUPPCState *env = &cpu->env;
76daa3
+    target_ulong flags = args[0];
76daa3
+    target_ulong proc_tbl = args[1];
76daa3
+    target_ulong page_size = args[2];
76daa3
+    target_ulong table_size = args[3];
76daa3
+    uint64_t cproc;
76daa3
+
76daa3
+    if (flags & ~FLAGS_MASK) { /* Check no reserved bits are set */
76daa3
+        return H_PARAMETER;
76daa3
+    }
76daa3
+    if (flags & FLAG_MODIFY) {
76daa3
+        if (flags & FLAG_REGISTER) {
76daa3
+            if (flags & FLAG_RADIX) { /* Register new RADIX process table */
76daa3
+                if (proc_tbl & 0xfff || proc_tbl >> 60) {
76daa3
+                    return H_P2;
76daa3
+                } else if (page_size) {
76daa3
+                    return H_P3;
76daa3
+                } else if (table_size > 24) {
76daa3
+                    return H_P4;
76daa3
+                }
76daa3
+                cproc = PATBE1_GR | proc_tbl | table_size;
76daa3
+            } else { /* Register new HPT process table */
76daa3
+                if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */
76daa3
+                    /* TODO - Not Supported */
76daa3
+                    /* Technically caused by flag bits => H_PARAMETER */
76daa3
+                    return H_PARAMETER;
76daa3
+                } else { /* Hash with SLB */
76daa3
+                    if (proc_tbl >> 38) {
76daa3
+                        return H_P2;
76daa3
+                    } else if (page_size & ~0x7) {
76daa3
+                        return H_P3;
76daa3
+                    } else if (table_size > 24) {
76daa3
+                        return H_P4;
76daa3
+                    }
76daa3
+                }
76daa3
+                cproc = (proc_tbl << 25) | page_size << 5 | table_size;
76daa3
+            }
76daa3
+
76daa3
+        } else { /* Deregister current process table */
76daa3
+            /* Set to benign value: (current GR) | 0. This allows
76daa3
+             * deregistration in KVM to succeed even if the radix bit in flags
76daa3
+             * doesn't match the radix bit in the old PATB. */
76daa3
+            cproc = spapr->patb_entry & PATBE1_GR;
76daa3
+        }
76daa3
+    } else { /* Maintain current registration */
76daa3
+        if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) {
76daa3
+            /* Technically caused by flag bits => H_PARAMETER */
76daa3
+            return H_PARAMETER; /* Existing Process Table Mismatch */
76daa3
+        }
76daa3
+        cproc = spapr->patb_entry;
76daa3
+    }
76daa3
+
76daa3
+    /* Check if we need to setup OR free the hpt */
76daa3
+    spapr_check_setup_free_hpt(spapr, spapr->patb_entry, cproc);
76daa3
+
76daa3
+    spapr->patb_entry = cproc; /* Save new process table */
76daa3
+    if ((flags & FLAG_RADIX) || (flags & FLAG_HASH_PROC_TBL)) {
76daa3
+        /* Use Process TBL */
76daa3
+        env->spr[SPR_LPCR] |= LPCR_UPRT;
76daa3
+    } else {
76daa3
+        env->spr[SPR_LPCR] &= ~LPCR_UPRT;
76daa3
+    }
76daa3
+    if (flags & FLAG_GTSE) { /* Partition Uses Guest Translation Shootdwn */
76daa3
+        env->spr[SPR_LPCR] |= LPCR_GTSE;
76daa3
+    } else {
76daa3
+        env->spr[SPR_LPCR] &= ~LPCR_GTSE;
76daa3
+    }
76daa3
+
76daa3
+    if (kvm_enabled()) {
76daa3
+        return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX,
76daa3
+                                       flags & FLAG_GTSE, cproc);
76daa3
+    }
76daa3
+    return H_SUCCESS;
76daa3
 }
76daa3
 
76daa3
 #define H_SIGNAL_SYS_RESET_ALL         -1
76daa3
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
76daa3
index 9db6bbe..247c969 100644
76daa3
--- a/include/hw/ppc/spapr.h
76daa3
+++ b/include/hw/ppc/spapr.h
76daa3
@@ -596,6 +596,8 @@ void spapr_dt_events(sPAPRMachineState *sm, void *fdt);
76daa3
 int spapr_h_cas_compose_response(sPAPRMachineState *sm,
76daa3
                                  target_ulong addr, target_ulong size,
76daa3
                                  sPAPROptionVector *ov5_updates);
76daa3
+void close_htab_fd(sPAPRMachineState *spapr);
76daa3
+void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr);
76daa3
 sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn);
76daa3
 void spapr_tce_table_enable(sPAPRTCETable *tcet,
76daa3
                             uint32_t page_shift, uint64_t bus_offset,
76daa3
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
76daa3
index b733578..43afa4e 100644
76daa3
--- a/target/ppc/kvm.c
76daa3
+++ b/target/ppc/kvm.c
76daa3
@@ -361,6 +361,37 @@ struct ppc_radix_page_info *kvm_get_radix_page_info(void)
76daa3
     return radix_page_info;
76daa3
 }
76daa3
 
76daa3
+target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
76daa3
+                                     bool radix, bool gtse,
76daa3
+                                     uint64_t proc_tbl)
76daa3
+{
76daa3
+    CPUState *cs = CPU(cpu);
76daa3
+    int ret;
76daa3
+    uint64_t flags = 0;
76daa3
+    struct kvm_ppc_mmuv3_cfg cfg = {
76daa3
+        .process_table = proc_tbl,
76daa3
+    };
76daa3
+
76daa3
+    if (radix) {
76daa3
+        flags |= KVM_PPC_MMUV3_RADIX;
76daa3
+    }
76daa3
+    if (gtse) {
76daa3
+        flags |= KVM_PPC_MMUV3_GTSE;
76daa3
+    }
76daa3
+    cfg.flags = flags;
76daa3
+    ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_CONFIGURE_V3_MMU, &cfg;;
76daa3
+    switch (ret) {
76daa3
+    case 0:
76daa3
+        return H_SUCCESS;
76daa3
+    case -EINVAL:
76daa3
+        return H_PARAMETER;
76daa3
+    case -ENODEV:
76daa3
+        return H_NOT_AVAILABLE;
76daa3
+    default:
76daa3
+        return H_HARDWARE;
76daa3
+    }
76daa3
+}
76daa3
+
76daa3
 static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
76daa3
 {
76daa3
     if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) {
76daa3
diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h
76daa3
index 64189a4..4b2fd9a 100644
76daa3
--- a/target/ppc/kvm_ppc.h
76daa3
+++ b/target/ppc/kvm_ppc.h
76daa3
@@ -33,6 +33,9 @@ int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits);
76daa3
 int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits);
76daa3
 int kvmppc_set_tcr(PowerPCCPU *cpu);
76daa3
 int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu);
76daa3
+target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
76daa3
+                                     bool radix, bool gtse,
76daa3
+                                     uint64_t proc_tbl);
76daa3
 #ifndef CONFIG_USER_ONLY
76daa3
 off_t kvmppc_alloc_rma(void **rma);
76daa3
 bool kvmppc_spapr_use_multitce(void);
76daa3
@@ -159,6 +162,13 @@ static inline int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu)
76daa3
     return -1;
76daa3
 }
76daa3
 
76daa3
+static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
76daa3
+                                     bool radix, bool gtse,
76daa3
+                                     uint64_t proc_tbl)
76daa3
+{
76daa3
+    return 0;
76daa3
+}
76daa3
+
76daa3
 #ifndef CONFIG_USER_ONLY
76daa3
 static inline off_t kvmppc_alloc_rma(void **rma)
76daa3
 {
76daa3
-- 
76daa3
1.8.3.1
76daa3