diff mbox

[RFC,05/17] ppc: Clean up and QOMify hypercall emulation

Message ID 1477825928-10803-6-git-send-email-david@gibson.dropbear.id.au
State New
Headers show

Commit Message

David Gibson Oct. 30, 2016, 11:11 a.m. UTC
The pseries machine type is a bit unusual in that it runs a paravirtualized
guest.  The guest expects to interact with a hypervisor, and qemu
emulates the functions of that hypervisor directly, rather than executing
hypervisor code within the emulated system.

To implement this in TCG, we need to intercept hypercall instructions and
direct them to the machine's hypercall handlers, rather than attempting to
perform a privilege change within TCG.  This is controlled by a global
hook - cpu_ppc_hypercall.

This cleanup makes the handling a little cleaner and more extensible that
a single global variable.  Instead, each CPU to have hypercalls intercepted
has a pointer set to a QOM object implementing a new virtual hypervisor
interface.  A method in that interface is called by TCG when it sees a
hypercall instruction.  It's possible we may want to add other methods in
future.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 hw/ppc/spapr.c              |  8 +++++---
 hw/ppc/spapr_cpu_core.c     |  1 +
 target-ppc/cpu.h            | 26 ++++++++++++++++++++++++--
 target-ppc/excp_helper.c    | 11 ++++-------
 target-ppc/translate_init.c | 12 ++++++++++++
 5 files changed, 46 insertions(+), 12 deletions(-)

Comments

Alexey Kardashevskiy Nov. 3, 2016, 8:50 a.m. UTC | #1
On 30/10/16 22:11, David Gibson wrote:
> The pseries machine type is a bit unusual in that it runs a paravirtualized
> guest.  The guest expects to interact with a hypervisor, and qemu
> emulates the functions of that hypervisor directly, rather than executing
> hypervisor code within the emulated system.
> 
> To implement this in TCG, we need to intercept hypercall instructions and
> direct them to the machine's hypercall handlers, rather than attempting to
> perform a privilege change within TCG.  This is controlled by a global
> hook - cpu_ppc_hypercall.
> 
> This cleanup makes the handling a little cleaner and more extensible that
> a single global variable.  Instead, each CPU to have hypercalls intercepted
> has a pointer set to a QOM object implementing a new virtual hypervisor
> interface.  A method in that interface is called by TCG when it sees a
> hypercall instruction.  It's possible we may want to add other methods in
> future.


Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>

I did not realize it is a global hook :)

> 
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> ---
>  hw/ppc/spapr.c              |  8 +++++---
>  hw/ppc/spapr_cpu_core.c     |  1 +
>  target-ppc/cpu.h            | 26 ++++++++++++++++++++++++--
>  target-ppc/excp_helper.c    | 11 ++++-------
>  target-ppc/translate_init.c | 12 ++++++++++++
>  5 files changed, 46 insertions(+), 12 deletions(-)
> 
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index 3551439..b7762ee 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -1006,7 +1006,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
>      return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
>  }
>  
> -static void emulate_spapr_hypercall(PowerPCCPU *cpu)
> +static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
> +                                    PowerPCCPU *cpu)
>  {
>      CPUPPCState *env = &cpu->env;
>  
> @@ -1778,8 +1779,6 @@ static void ppc_spapr_init(MachineState *machine)
>  
>      QLIST_INIT(&spapr->phbs);
>  
> -    cpu_ppc_hypercall = emulate_spapr_hypercall;
> -
>      /* Allocate RMA if necessary */
>      rma_alloc_size = kvmppc_alloc_rma(&rma);
>  
> @@ -2610,6 +2609,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
>      FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc);
>      NMIClass *nc = NMI_CLASS(oc);
>      HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
> +    PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc);
>  
>      mc->desc = "pSeries Logical Partition (PAPR compliant)";
>  
> @@ -2641,6 +2641,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
>      fwc->get_dev_path = spapr_get_fw_dev_path;
>      nc->nmi_monitor_handler = spapr_nmi;
>      smc->phb_placement = spapr_phb_placement;
> +    vhc->hypercall = emulate_spapr_hypercall;
>  }
>  
>  static const TypeInfo spapr_machine_info = {
> @@ -2656,6 +2657,7 @@ static const TypeInfo spapr_machine_info = {
>          { TYPE_FW_PATH_PROVIDER },
>          { TYPE_NMI },
>          { TYPE_HOTPLUG_HANDLER },
> +        { TYPE_PPC_VIRTUAL_HYPERVISOR },
>          { }
>      },
>  };
> diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
> index 1357293..ee5cd14 100644
> --- a/hw/ppc/spapr_cpu_core.c
> +++ b/hw/ppc/spapr_cpu_core.c
> @@ -57,6 +57,7 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
>      cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
>  
>      /* Enable PAPR mode in TCG or KVM */
> +    cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));
>      cpu_ppc_set_papr(cpu);
>  
>      if (cpu->max_compat) {
> diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
> index 1c90adb..a655105 100644
> --- a/target-ppc/cpu.h
> +++ b/target-ppc/cpu.h
> @@ -1148,6 +1148,9 @@ do {                                            \
>      env->wdt_period[3] = (d_);                  \
>   } while (0)
>  
> +typedef struct PPCVirtualHypervisor PPCVirtualHypervisor;
> +typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass;
> +
>  /**
>   * PowerPCCPU:
>   * @env: #CPUPPCState
> @@ -1166,6 +1169,7 @@ struct PowerPCCPU {
>      int cpu_dt_id;
>      uint32_t max_compat;
>      uint32_t cpu_version;
> +    PPCVirtualHypervisor *vhyp;
>  };
>  
>  static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
> @@ -1180,6 +1184,25 @@ static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
>  PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
>  PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr);
>  
> +struct PPCVirtualHypervisor {
> +    Object parent;
> +};
> +
> +struct PPCVirtualHypervisorClass {
> +    InterfaceClass parent;
> +    void (*hypercall)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu);
> +};
> +
> +#define TYPE_PPC_VIRTUAL_HYPERVISOR "ppc-virtual-hypervisor"
> +#define PPC_VIRTUAL_HYPERVISOR(obj)                 \
> +    OBJECT_CHECK(PPCVirtualHypervisor, (obj), TYPE_PPC_VIRTUAL_HYPERVISOR)
> +#define PPC_VIRTUAL_HYPERVISOR_CLASS(klass)         \
> +    OBJECT_CLASS_CHECK(PPCVirtualHypervisorClass, (klass), \
> +                       TYPE_PPC_VIRTUAL_HYPERVISOR)
> +#define PPC_VIRTUAL_HYPERVISOR_GET_CLASS(obj) \
> +    OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \
> +                     TYPE_PPC_VIRTUAL_HYPERVISOR)
> +
>  void ppc_cpu_do_interrupt(CPUState *cpu);
>  bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
>  void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
> @@ -1252,6 +1275,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val);
>  void store_booke_tsr (CPUPPCState *env, target_ulong val);
>  void ppc_tlb_invalidate_all (CPUPPCState *env);
>  void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
> +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
>  void cpu_ppc_set_papr(PowerPCCPU *cpu);
>  #endif
>  #endif
> @@ -2421,8 +2445,6 @@ static inline bool lsw_reg_in_range(int start, int nregs, int rx)
>             (start + nregs > 32 && (rx >= start || rx < start + nregs - 32));
>  }
>  
> -extern void (*cpu_ppc_hypercall)(PowerPCCPU *);
> -
>  void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env);
>  
>  /**
> diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c
> index 808760b..a077939 100644
> --- a/target-ppc/excp_helper.c
> +++ b/target-ppc/excp_helper.c
> @@ -35,11 +35,6 @@
>  #endif
>  
>  /*****************************************************************************/
> -/* PowerPC Hypercall emulation */
> -
> -void (*cpu_ppc_hypercall)(PowerPCCPU *);
> -
> -/*****************************************************************************/
>  /* Exception processing */
>  #if defined(CONFIG_USER_ONLY)
>  void ppc_cpu_do_interrupt(CPUState *cs)
> @@ -318,8 +313,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>          env->nip += 4;
>  
>          /* "PAPR mode" built-in hypercall emulation */
> -        if ((lev == 1) && cpu_ppc_hypercall) {
> -            cpu_ppc_hypercall(cpu);
> +        if ((lev == 1) && cpu->vhyp) {
> +            PPCVirtualHypervisorClass *vhc =
> +                PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
> +            vhc->hypercall(cpu->vhyp, cpu);
>              return;
>          }
>          if (lev == 1) {
> diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
> index 208fa1e..21899a4 100644
> --- a/target-ppc/translate_init.c
> +++ b/target-ppc/translate_init.c
> @@ -8857,6 +8857,11 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
>  
>  #if !defined(CONFIG_USER_ONLY)
>  
> +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp)
> +{
> +    cpu->vhyp = vhyp;
> +}
> +
>  void cpu_ppc_set_papr(PowerPCCPU *cpu)
>  {
>      CPUPPCState *env = &cpu->env;
> @@ -10587,9 +10592,16 @@ static const TypeInfo ppc_cpu_type_info = {
>      .class_init = ppc_cpu_class_init,
>  };
>  
> +static const TypeInfo ppc_vhyp_type_info = {
> +    .name = TYPE_PPC_VIRTUAL_HYPERVISOR,
> +    .parent = TYPE_INTERFACE,
> +    .class_size = sizeof(PPCVirtualHypervisorClass),
> +};
> +
>  static void ppc_cpu_register_types(void)
>  {
>      type_register_static(&ppc_cpu_type_info);
> +    type_register_static(&ppc_vhyp_type_info);
>  }
>  
>  type_init(ppc_cpu_register_types)
>
diff mbox

Patch

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 3551439..b7762ee 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1006,7 +1006,8 @@  static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
     return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
 }
 
-static void emulate_spapr_hypercall(PowerPCCPU *cpu)
+static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
+                                    PowerPCCPU *cpu)
 {
     CPUPPCState *env = &cpu->env;
 
@@ -1778,8 +1779,6 @@  static void ppc_spapr_init(MachineState *machine)
 
     QLIST_INIT(&spapr->phbs);
 
-    cpu_ppc_hypercall = emulate_spapr_hypercall;
-
     /* Allocate RMA if necessary */
     rma_alloc_size = kvmppc_alloc_rma(&rma);
 
@@ -2610,6 +2609,7 @@  static void spapr_machine_class_init(ObjectClass *oc, void *data)
     FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc);
     NMIClass *nc = NMI_CLASS(oc);
     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+    PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc);
 
     mc->desc = "pSeries Logical Partition (PAPR compliant)";
 
@@ -2641,6 +2641,7 @@  static void spapr_machine_class_init(ObjectClass *oc, void *data)
     fwc->get_dev_path = spapr_get_fw_dev_path;
     nc->nmi_monitor_handler = spapr_nmi;
     smc->phb_placement = spapr_phb_placement;
+    vhc->hypercall = emulate_spapr_hypercall;
 }
 
 static const TypeInfo spapr_machine_info = {
@@ -2656,6 +2657,7 @@  static const TypeInfo spapr_machine_info = {
         { TYPE_FW_PATH_PROVIDER },
         { TYPE_NMI },
         { TYPE_HOTPLUG_HANDLER },
+        { TYPE_PPC_VIRTUAL_HYPERVISOR },
         { }
     },
 };
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index 1357293..ee5cd14 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -57,6 +57,7 @@  static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
     cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
 
     /* Enable PAPR mode in TCG or KVM */
+    cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));
     cpu_ppc_set_papr(cpu);
 
     if (cpu->max_compat) {
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 1c90adb..a655105 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1148,6 +1148,9 @@  do {                                            \
     env->wdt_period[3] = (d_);                  \
  } while (0)
 
+typedef struct PPCVirtualHypervisor PPCVirtualHypervisor;
+typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass;
+
 /**
  * PowerPCCPU:
  * @env: #CPUPPCState
@@ -1166,6 +1169,7 @@  struct PowerPCCPU {
     int cpu_dt_id;
     uint32_t max_compat;
     uint32_t cpu_version;
+    PPCVirtualHypervisor *vhyp;
 };
 
 static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
@@ -1180,6 +1184,25 @@  static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
 PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
 PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr);
 
+struct PPCVirtualHypervisor {
+    Object parent;
+};
+
+struct PPCVirtualHypervisorClass {
+    InterfaceClass parent;
+    void (*hypercall)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu);
+};
+
+#define TYPE_PPC_VIRTUAL_HYPERVISOR "ppc-virtual-hypervisor"
+#define PPC_VIRTUAL_HYPERVISOR(obj)                 \
+    OBJECT_CHECK(PPCVirtualHypervisor, (obj), TYPE_PPC_VIRTUAL_HYPERVISOR)
+#define PPC_VIRTUAL_HYPERVISOR_CLASS(klass)         \
+    OBJECT_CLASS_CHECK(PPCVirtualHypervisorClass, (klass), \
+                       TYPE_PPC_VIRTUAL_HYPERVISOR)
+#define PPC_VIRTUAL_HYPERVISOR_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \
+                     TYPE_PPC_VIRTUAL_HYPERVISOR)
+
 void ppc_cpu_do_interrupt(CPUState *cpu);
 bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
@@ -1252,6 +1275,7 @@  void store_booke_tcr (CPUPPCState *env, target_ulong val);
 void store_booke_tsr (CPUPPCState *env, target_ulong val);
 void ppc_tlb_invalidate_all (CPUPPCState *env);
 void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
+void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
 void cpu_ppc_set_papr(PowerPCCPU *cpu);
 #endif
 #endif
@@ -2421,8 +2445,6 @@  static inline bool lsw_reg_in_range(int start, int nregs, int rx)
            (start + nregs > 32 && (rx >= start || rx < start + nregs - 32));
 }
 
-extern void (*cpu_ppc_hypercall)(PowerPCCPU *);
-
 void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env);
 
 /**
diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c
index 808760b..a077939 100644
--- a/target-ppc/excp_helper.c
+++ b/target-ppc/excp_helper.c
@@ -35,11 +35,6 @@ 
 #endif
 
 /*****************************************************************************/
-/* PowerPC Hypercall emulation */
-
-void (*cpu_ppc_hypercall)(PowerPCCPU *);
-
-/*****************************************************************************/
 /* Exception processing */
 #if defined(CONFIG_USER_ONLY)
 void ppc_cpu_do_interrupt(CPUState *cs)
@@ -318,8 +313,10 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         env->nip += 4;
 
         /* "PAPR mode" built-in hypercall emulation */
-        if ((lev == 1) && cpu_ppc_hypercall) {
-            cpu_ppc_hypercall(cpu);
+        if ((lev == 1) && cpu->vhyp) {
+            PPCVirtualHypervisorClass *vhc =
+                PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+            vhc->hypercall(cpu->vhyp, cpu);
             return;
         }
         if (lev == 1) {
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 208fa1e..21899a4 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8857,6 +8857,11 @@  POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
 
 #if !defined(CONFIG_USER_ONLY)
 
+void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp)
+{
+    cpu->vhyp = vhyp;
+}
+
 void cpu_ppc_set_papr(PowerPCCPU *cpu)
 {
     CPUPPCState *env = &cpu->env;
@@ -10587,9 +10592,16 @@  static const TypeInfo ppc_cpu_type_info = {
     .class_init = ppc_cpu_class_init,
 };
 
+static const TypeInfo ppc_vhyp_type_info = {
+    .name = TYPE_PPC_VIRTUAL_HYPERVISOR,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(PPCVirtualHypervisorClass),
+};
+
 static void ppc_cpu_register_types(void)
 {
     type_register_static(&ppc_cpu_type_info);
+    type_register_static(&ppc_vhyp_type_info);
 }
 
 type_init(ppc_cpu_register_types)