[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC 08/10] xen/arm64: Implement the hypercall handing fully in assembly



Currently when the guest is issuing an hypercall, all the registers are
saved in the stack and then later reload from the stack before calling
the hypercall dispatcher.

To avoid spending time loading from the stack the arguments, the
hypercall handler can be implemented in assembly.

This patch implements the assembly version of do_trap_hypercall for
AArch64.

Note that the C version of do_trap_hypercall will be dropped in a
follow-up patch.

Signed-off-by: Julien Grall <julien.grall@xxxxxxxxxx>
---
 xen/arch/arm/arm64/asm-offsets.c |  11 +++
 xen/arch/arm/arm64/entry.S       | 147 ++++++++++++++++++++++++++++++++++++++-
 xen/include/asm-arm/processor.h  |   3 +
 3 files changed, 159 insertions(+), 2 deletions(-)

diff --git a/xen/arch/arm/arm64/asm-offsets.c b/xen/arch/arm/arm64/asm-offsets.c
index a3ce816..6f0e8f1 100644
--- a/xen/arch/arm/arm64/asm-offsets.c
+++ b/xen/arch/arm/arm64/asm-offsets.c
@@ -23,6 +23,12 @@
 void __dummy__(void)
 {
    OFFSET(UREGS_X0, struct cpu_user_regs, x0);
+   OFFSET(UREGS_X1, struct cpu_user_regs, x1);
+   OFFSET(UREGS_X2, struct cpu_user_regs, x2);
+   OFFSET(UREGS_X3, struct cpu_user_regs, x3);
+   OFFSET(UREGS_X4, struct cpu_user_regs, x4);
+   OFFSET(UREGS_X12, struct cpu_user_regs, x12);
+   OFFSET(UREGS_X16, struct cpu_user_regs, x16);
    OFFSET(UREGS_LR, struct cpu_user_regs, lr);
 
    OFFSET(UREGS_SP, struct cpu_user_regs, sp);
@@ -50,6 +56,11 @@ void __dummy__(void)
 
    BLANK();
    OFFSET(INITINFO_stack, struct init_info, stack);
+
+#ifdef PERF_COUNTERS
+   BLANK();
+   DEFINE(ASM_PERFC_hypercalls, PERFC_hypercalls);
+#endif
 }
 
 /*
diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S
index 93c80ff..5092533 100644
--- a/xen/arch/arm/arm64/entry.S
+++ b/xen/arch/arm/arm64/entry.S
@@ -1,4 +1,5 @@
 #include <xen/config.h>
+#include <xen/errno.h>
 #include <asm/asm_defns.h>
 #include <asm/bug.h>
 #include <asm/regs.h>
@@ -174,8 +175,9 @@ hyp_irq:
 guest_sync:
         entry   hyp=0, compat=0
         msr     daifclr, #2
+        mov     x7, x0
         mov     x0, sp
-        bl      do_trap_hypervisor
+        bl      do_trap_guest_sync
         exit    hyp=0, compat=0
 
 guest_irq:
@@ -195,8 +197,9 @@ guest_error_invalid:
 guest_sync_compat:
         entry   hyp=0, compat=1
         msr     daifclr, #2
+        mov     x7, x0
         mov     x0, sp
-        bl      do_trap_hypervisor
+        bl      do_trap_guest_sync
         exit    hyp=0, compat=1
 
 guest_irq_compat:
@@ -277,6 +280,146 @@ ENTRY(hyp_traps_vector)
         ventry  guest_fiq_invalid_compat        // FIQ 32-bit EL0/EL1
         ventry  guest_error_invalid_compat      // Error 32-bit EL0/EL1
 
+
+/*
+ * x0/sp - cpu_user_regs
+ * x7 - x0 of the guest (possible arg1 of an hypercall)
+ * x12 - hypercall number for HVC32
+ * x16 - hypercall number for HVC64
+ *
+ * Callee-saved register used within the function for specific purpose:
+ *
+ * x19 - save x30 (lr)
+ * x20 - save the guest pc (only for debug build)
+ * x21 - save the hypercall number
+ * x22 - ESR_EL2.EC
+ *
+ * Note that x1 - x4 shouldn't be clobbered as they contain the
+ * arguments of the hypercall if the guest issued an hypercall.
+ */
+do_trap_guest_sync:
+        mrs     x5, ESR_EL2
+        lsr     x22, x5, #HSR_EC_SHIFT      /* x22 <- ESR.EL2.EC */
+        /* The trap may be an hypercall if EC == HVC32 || HVC64 */
+        cmp     x22, #HSR_EC_HVC32
+        beq     do_trap_hvc32               /* EC == HVC32 -> HVC call */
+        cmp     x22, #HSR_EC_HVC64
+        beq     do_trap_hvc                 /* EC == HVC64 -> HVC call */
+
+        b do_trap_hypervisor                /* Not an HVC call -> generic 
dispatch */
+
+do_trap_hvc32:
+        /*
+         * For HVC32, the hypercall number is in x12, load it in x16
+         * to have the HVC dispatcher common.
+         */
+        mov     x16, x12
+
+do_trap_hvc:
+        /*
+         * Only HVC call with ISS == XEN_HYPERCALL_TAG is handled in
+         * assembly, the rest is handled in C by the generic dispatch.
+         */
+        and     x6, x5, #HSR_ISS_MASK       /* x6 <- ESR_EL2.ISS */
+        cmp     x6, #XEN_HYPERCALL_TAG
+        bne     do_trap_hypervisor          /* Not an hypercall -> generic 
dispatch */
+
+do_trap_hypercall:
+        /* The guest issued an hypercall */
+
+        /* Check if the hypercall number is valid */
+        cmp     x16, #(NR_hypercalls-1)
+        bhi     invalid_hypercall           /* Hypercall number invalid -> 
bail out */
+
+        PERFC_INCR(hypercalls, x16)
+
+#ifndef NDEBUG
+        /* Save guest PC to detect hypercall continuation */
+        ldr     x20, [sp, #UREGS_PC]        /* x20 <- regs->pc */
+#endif
+
+        /*
+         * Call the hypercall dispatcher
+         * The arguments arg2 - arg5 are already in the correct
+         * registers (i.e x1 - x4).
+         */
+        mov     x19, lr                     /* save LR in x19 */
+        mov     x21, x16                    /* save the hypercall number in 
x21 */
+        mov     x0, x7                      /* x0 <- arg1 */
+        mov     x5, x16                     /* x5 <- Hypercall number */
+        bl      do_dispatch_hypercall
+        mov     lr, x19                     /* Restore LR from x19 */
+
+        str     x0, [sp, #UREGS_X0]         /* regs->x0 <- 
do_dispatch_hypercall(...) */
+
+#ifndef NDEBUG
+        /*
+         * Clobber arguments register only if pc is unchanged, otherwise
+         * this is a hypercall continuation.
+         */
+        ldr     x0, [sp, #UREGS_PC]         /* x0 <- regs->pc */
+        cmp     x0, x20                     /* Compare regs->pc (x0) with 
orig_pc (x20) */
+        bne     clobber_done                /* different => no need to clobber 
*/
+
+        ldr     x6, =0xdeadbeefdeadbeef     /* x6 <- poison for registers */
+
+        ldr     x5, =hypercall_args_table
+        ldr     x5, [x5, x21]               /* x5 <- number of args */
+        cmp     x5, #5; b   .Largs5
+        cmp     x5, #4; b   .Largs4
+        cmp     x5, #3; b   .Largs3
+        cmp     x5, #2; b   .Largs2
+        cmp     x5, #1; b   .Largs1
+        cmp     x5, #0; b   .Largs0         /* Only for hypercall not 
implemented */
+        BUG                                 /* Number of arguments invalid */
+.Largs5:
+        ldr     x6, [sp, #UREGS_X4]
+.Largs4:
+        ldr     x6, [sp, #UREGS_X3]
+.Largs3:
+        ldr     x6, [sp, #UREGS_X2]
+.Largs2:
+        ldr     x6, [sp, #UREGS_X1]
+.Largs1:
+        /* Don't clobber x0 -- it's the return value */
+        b       .Lclobber_nr
+.Largs0:
+        /*
+         * Can only happen when the hypercall is not implemented.
+         * i.e hypercall_table[*nr] == do_ni_hypercall
+         */
+        ldr     x6, =hypercall_table
+        ldr     x6, [x6, x21, lsl #3]       /* x10 := pointer to hypercall 
function */
+        ldr     x5, do_ni_hypercall
+        cmp     x5, x6
+        beq     .Lclobber_nr                /* Hypercall function == 
do_ni_hypercall => Good */
+        ASSERT_FAILED("hypercall_table[*nr] == do_ni_hypercall")
+
+.Lclobber_nr:
+        /*
+         * Clobber the hypercall number register. The register depends
+         * on HSR.EL2 (stored in x22):
+         *      HSR_EC_HVC32 -> x12
+         *      HSR_EC_HVC64 -> x16
+         */
+        cmp     x22, #HSR_EC_HVC32
+        beq     .Lclobber_hvc32
+.Lclobber_hvc64:
+        str     x6, [sp, #UREGS_X16]
+        b       clobber_done
+.Lclobber_hvc32:
+        str     x6, [sp, #UREGS_X12]
+
+clobber_done:
+#endif
+
+        ret
+
+invalid_hypercall:
+        mov     x0, #-ENOSYS
+        str     x0, [sp, #UREGS_X0]         /* regs->x0 <- -ENOSYS */
+        ret
+
 /*
  * struct vcpu *__context_switch(struct vcpu *prev, struct vcpu *next)
  *
diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h
index 7e6eb66..da02599 100644
--- a/xen/include/asm-arm/processor.h
+++ b/xen/include/asm-arm/processor.h
@@ -483,6 +483,9 @@ union hsr {
 };
 #endif
 
+#define HSR_EC_SHIFT    26
+#define HSR_ISS_MASK    0x00ffffff
+
 /* HSR.EC == HSR_CP{15,14,10}_32 */
 #define HSR_CP32_OP2_MASK (0x000e0000)
 #define HSR_CP32_OP2_SHIFT (17)
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.