Changeset View
Changeset View
Standalone View
Standalone View
sys/arm64/vmm/hyp_macros.h
- This file was added.
| /* | |||||
| * Copyright (C) 2017 Alexandru Elisei <alexandru.elisei@gmail.com> | |||||
| * All rights reserved. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | |||||
| * modification, are permitted provided that the following conditions | |||||
| * are met: | |||||
| * 1. Redistributions of source code must retain the above copyright | |||||
| * notice, this list of conditions and the following disclaimer. | |||||
| * 2. Redistributions in binary form must reproduce the above copyright | |||||
| * notice, this list of conditions and the following disclaimer in the | |||||
| * documentation and/or other materials provided with the distribution. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
| * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
| * SUCH DAMAGE. | |||||
| */ | |||||
| #ifndef _VMM_HYP_MACROS_H_ | |||||
| #define _VMM_HYP_MACROS_H_ | |||||
| #define PUSH_SYS_REG_PAIR(reg0, reg1) \ | |||||
| mrs x1, reg0; \ | |||||
| mrs x2, reg1; \ | |||||
| stp x2, x1, [sp, #-16]!; | |||||
| #define PUSH_SYS_REG(reg) \ | |||||
| mrs x1, reg; \ | |||||
| str x1, [sp, #-16]!; | |||||
| /* | |||||
| * Push all the host registers before entering the guest. | |||||
| */ | |||||
| #define SAVE_HOST_REGS() \ | |||||
| /* Save the regular registers */ \ | |||||
| stp x0, x1, [sp, #-16]!; \ | |||||
| stp x2, x3, [sp, #-16]!; \ | |||||
| stp x4, x5, [sp, #-16]!; \ | |||||
| stp x6, x7, [sp, #-16]!; \ | |||||
| stp x8, x9, [sp, #-16]!; \ | |||||
| stp x10, x11, [sp, #-16]!; \ | |||||
| stp x12, x13, [sp, #-16]!; \ | |||||
| stp x14, x15, [sp, #-16]!; \ | |||||
| stp x16, x17, [sp, #-16]!; \ | |||||
| stp x18, x19, [sp, #-16]!; \ | |||||
| stp x20, x21, [sp, #-16]!; \ | |||||
| stp x22, x23, [sp, #-16]!; \ | |||||
| stp x24, x25, [sp, #-16]!; \ | |||||
| stp x26, x27, [sp, #-16]!; \ | |||||
| stp x28, x29, [sp, #-16]!; \ | |||||
| stp lr, fp, [sp, #-16]!; \ | |||||
| \ | |||||
| /* Push the system registers */ \ | |||||
| PUSH_SYS_REG_PAIR(SP_EL0, SP_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(ACTLR_EL1, AMAIR_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(ELR_EL1, PAR_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(MAIR_EL1, TCR_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(TPIDR_EL0, TPIDRRO_EL0); \ | |||||
| PUSH_SYS_REG_PAIR(TPIDR_EL1, TTBR0_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(TTBR1_EL1, VBAR_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(AFSR0_EL1, AFSR1_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(CONTEXTIDR_EL1, CPACR_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(ESR_EL1, FAR_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(SCTLR_EL1, SPSR_EL1); \ | |||||
| PUSH_SYS_REG_PAIR(ELR_EL2, HCR_EL2); \ | |||||
| PUSH_SYS_REG_PAIR(VPIDR_EL2, VMPIDR_EL2); \ | |||||
| PUSH_SYS_REG_PAIR(CPTR_EL2, SPSR_EL2); \ | |||||
| PUSH_SYS_REG_PAIR(ICH_HCR_EL2, ICH_VMCR_EL2); \ | |||||
| PUSH_SYS_REG_PAIR(CNTHCTL_EL2, CNTKCTL_EL1); \ | |||||
| PUSH_SYS_REG(CNTVOFF_EL2); | |||||
| #define SAVE_HOST_VFP_REGS() \ | |||||
| stp q0, q1, [sp, #-16 * 2]!; \ | |||||
| stp q2, q3, [sp, #-16 * 2]!; \ | |||||
| stp q4, q5, [sp, #-16 * 2]!; \ | |||||
| stp q6, q7, [sp, #-16 * 2]!; \ | |||||
| stp q8, q9, [sp, #-16 * 2]!; \ | |||||
| stp q10, q11, [sp, #-16 * 2]!; \ | |||||
| stp q12, q13, [sp, #-16 * 2]!; \ | |||||
| stp q14, q15, [sp, #-16 * 2]!; \ | |||||
| stp q16, q17, [sp, #-16 * 2]!; \ | |||||
| stp q18, q19, [sp, #-16 * 2]!; \ | |||||
| stp q20, q21, [sp, #-16 * 2]!; \ | |||||
| stp q22, q23, [sp, #-16 * 2]!; \ | |||||
| stp q24, q25, [sp, #-16 * 2]!; \ | |||||
| stp q26, q27, [sp, #-16 * 2]!; \ | |||||
| stp q28, q29, [sp, #-16 * 2]!; \ | |||||
| stp q30, q31, [sp, #-16 * 2]!; \ | |||||
| PUSH_SYS_REG_PAIR(FPCR, FPSR); | |||||
| #define POP_SYS_REG_PAIR(reg0, reg1) \ | |||||
| ldp x2, x1, [sp], #16; \ | |||||
| msr reg1, x2; \ | |||||
| msr reg0, x1; | |||||
| #define LOAD_HOST_VFP_REGS() \ | |||||
| POP_SYS_REG_PAIR(FPCR, FPSR); \ | |||||
| ldp q30, q31, [sp], #16 * 2; \ | |||||
| ldp q28, q29, [sp], #16 * 2; \ | |||||
| ldp q26, q27, [sp], #16 * 2; \ | |||||
| ldp q24, q25, [sp], #16 * 2; \ | |||||
| ldp q22, q23, [sp], #16 * 2; \ | |||||
| ldp q20, q21, [sp], #16 * 2; \ | |||||
| ldp q18, q19, [sp], #16 * 2; \ | |||||
| ldp q16, q17, [sp], #16 * 2; \ | |||||
| ldp q14, q15, [sp], #16 * 2; \ | |||||
| ldp q12, q13, [sp], #16 * 2; \ | |||||
| ldp q10, q11, [sp], #16 * 2; \ | |||||
| ldp q8, q9, [sp], #16 * 2; \ | |||||
| ldp q6, q7, [sp], #16 * 2; \ | |||||
| ldp q4, q5, [sp], #16 * 2; \ | |||||
| ldp q2, q3, [sp], #16 * 2; \ | |||||
| ldp q0, q1, [sp], #16 * 2; \ | |||||
| #define POP_SYS_REG(reg) \ | |||||
| ldr x1, [sp], #16; \ | |||||
| msr reg, x1; | |||||
| /* | |||||
| * Restore all the host registers before entering the host. | |||||
| */ | |||||
| #define LOAD_HOST_REGS() \ | |||||
| /* Pop the system registers first */ \ | |||||
| POP_SYS_REG(CNTVOFF_EL2); \ | |||||
| POP_SYS_REG_PAIR(CNTHCTL_EL2, CNTKCTL_EL1); \ | |||||
| POP_SYS_REG_PAIR(ICH_HCR_EL2, ICH_VMCR_EL2); \ | |||||
| POP_SYS_REG_PAIR(CPTR_EL2, SPSR_EL2); \ | |||||
| POP_SYS_REG_PAIR(VPIDR_EL2, VMPIDR_EL2); \ | |||||
| POP_SYS_REG_PAIR(ELR_EL2, HCR_EL2); \ | |||||
| POP_SYS_REG_PAIR(SCTLR_EL1, SPSR_EL1); \ | |||||
| POP_SYS_REG_PAIR(ESR_EL1, FAR_EL1); \ | |||||
| POP_SYS_REG_PAIR(CONTEXTIDR_EL1, CPACR_EL1); \ | |||||
| POP_SYS_REG_PAIR(AFSR0_EL1, AFSR1_EL1); \ | |||||
| POP_SYS_REG_PAIR(TTBR1_EL1, VBAR_EL1); \ | |||||
| POP_SYS_REG_PAIR(TPIDR_EL1, TTBR0_EL1); \ | |||||
| POP_SYS_REG_PAIR(TPIDR_EL0, TPIDRRO_EL0); \ | |||||
| POP_SYS_REG_PAIR(MAIR_EL1, TCR_EL1); \ | |||||
| POP_SYS_REG_PAIR(ELR_EL1, PAR_EL1); \ | |||||
| POP_SYS_REG_PAIR(ACTLR_EL1, AMAIR_EL1); \ | |||||
| POP_SYS_REG_PAIR(SP_EL0, SP_EL1); \ | |||||
| \ | |||||
| /* Pop the regular registers */ \ | |||||
| ldp lr, fp, [sp], #16; \ | |||||
| ldp x28, x29, [sp], #16; \ | |||||
| ldp x26, x27, [sp], #16; \ | |||||
| ldp x24, x25, [sp], #16; \ | |||||
| ldp x22, x23, [sp], #16; \ | |||||
| ldp x20, x21, [sp], #16; \ | |||||
| ldp x18, x19, [sp], #16; \ | |||||
| ldp x16, x17, [sp], #16; \ | |||||
| ldp x14, x15, [sp], #16; \ | |||||
| ldp x12, x13, [sp], #16; \ | |||||
| ldp x10, x11, [sp], #16; \ | |||||
| ldp x8, x9, [sp], #16; \ | |||||
| ldp x6, x7, [sp], #16; \ | |||||
| ldp x4, x5, [sp], #16; \ | |||||
| ldp x2, x3, [sp], #16; \ | |||||
| ldp x0, x1, [sp], #16; \ | |||||
| #define SAVE_ARRAY_REG64(reg, dest, remaining) \ | |||||
| cmp remaining, #0; \ | |||||
| beq 9f; \ | |||||
| mrs x7, reg; \ | |||||
| str x7, [dest]; \ | |||||
| add dest, dest, #8; \ | |||||
| sub remaining, remaining, #1; | |||||
| #define SAVE_LR_REGS() \ | |||||
| /* Load the number of ICH_LR_EL2 regs from memory */ \ | |||||
| mov x2, #HYPCTX_VGIC_ICH_LR_NUM; \ | |||||
| ldr x3, [x0, x2]; \ | |||||
| /* x1 holds the destination address */ \ | |||||
| mov x1, #HYPCTX_VGIC_ICH_LR_EL2; \ | |||||
| add x1, x0, x1; \ | |||||
| SAVE_ARRAY_REG64(ich_lr0_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr1_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr2_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr3_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr4_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr5_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr6_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr7_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr8_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr9_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr10_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr11_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr12_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr13_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr14_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG64(ich_lr15_el2, x1, x3); \ | |||||
| 9:; \ | |||||
| ; | |||||
| #define SAVE_ARRAY_REG32(reg, dest, remaining) \ | |||||
| cmp remaining, #0; \ | |||||
| beq 9f; \ | |||||
| mrs x7, reg; \ | |||||
| str w7, [dest]; \ | |||||
| add dest, dest, #4; \ | |||||
| sub remaining, remaining, #1; | |||||
| #define SAVE_AP0R_REGS() \ | |||||
| /* Load the number of ICH_AP0R_EL2 regs from memory */ \ | |||||
| mov x2, #HYPCTX_VGIC_ICH_AP0R_NUM; \ | |||||
| ldr x3, [x0, x2]; \ | |||||
| /* x1 holds the destination address */ \ | |||||
| mov x1, #HYPCTX_VGIC_ICH_AP0R_EL2; \ | |||||
| add x1, x0, x1; \ | |||||
| SAVE_ARRAY_REG32(ich_ap0r0_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG32(ich_ap0r1_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG32(ich_ap0r2_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG32(ich_ap0r3_el2, x1, x3); \ | |||||
| 9:; \ | |||||
| ; | |||||
| #define SAVE_AP1R_REGS() \ | |||||
| /* Load the number of ICH_AP1R_EL2 regs from memory */ \ | |||||
| mov x2, #HYPCTX_VGIC_ICH_AP1R_NUM; \ | |||||
| ldr x3, [x0, x2]; \ | |||||
| /* x1 holds the destination address */ \ | |||||
| mov x1, #HYPCTX_VGIC_ICH_AP1R_EL2; \ | |||||
| add x1, x0, x1; \ | |||||
| SAVE_ARRAY_REG32(ich_ap1r0_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG32(ich_ap1r1_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG32(ich_ap1r2_el2, x1, x3); \ | |||||
| SAVE_ARRAY_REG32(ich_ap1r3_el2, x1, x3); \ | |||||
| 9:; \ | |||||
| ; | |||||
| /* | |||||
| * The STR and LDR instructions take an offset between [-256, 255], but the | |||||
| * hypctx register offset can be larger than that. To get around this limitation | |||||
| * we use a temporary register to hold the offset. | |||||
| */ | |||||
| #define SAVE_SYS_REG64(prefix, reg) \ | |||||
| mrs x1, reg; \ | |||||
| mov x2, prefix ##_ ##reg; \ | |||||
| str x1, [x0, x2]; | |||||
| #define SAVE_SYS_REG32(prefix, reg) \ | |||||
| mrs x1, reg; \ | |||||
| mov x2, prefix ##_ ##reg; \ | |||||
| str w1, [x0, x2]; | |||||
| #define SAVE_REG(prefix, reg) \ | |||||
| mov x1, prefix ##_ ##reg; \ | |||||
| str reg, [x0, x1]; | |||||
| /* | |||||
| * The STP and LDP instructions takes an immediate in the range of [-512, 504] | |||||
| * when using the post-indexed addressing mode, but the hypctx register offset | |||||
| * can be larger than that. To get around this limitation we compute the address | |||||
| * by adding the hypctx base address with the struct member offset. | |||||
| * | |||||
| * Using STP/LDP to save/load register pairs to the corresponding struct hypctx | |||||
| * variables works because the registers are declared as an array and they are | |||||
| * stored in contiguous memory addresses. | |||||
| */ | |||||
| #define SAVE_REG_PAIR(prefix, reg0, reg1) \ | |||||
| mov x1, prefix ##_ ##reg0; \ | |||||
| add x1, x0, x1; \ | |||||
| stp reg0, reg1, [x1]; | |||||
| /* | |||||
| * We use x0 to load the hypctx address from TPIDR_EL2 and x1 and x2 as | |||||
| * temporary registers to compute the hypctx member addresses. To save the guest | |||||
| * values at first we push them on the stack, use these temporary registers to | |||||
| * save the rest of the registers and at the end we pop the values from the | |||||
| * stack and save them. | |||||
| */ | |||||
| #define SAVE_GUEST_X_REGS() \ | |||||
| /* Push x0 */ \ | |||||
| str x0, [sp, #-16]!; \ | |||||
| /* Restore hypctx address */ \ | |||||
| mrs x0, tpidr_el2; \ | |||||
| /* Push x1 and x2 */ \ | |||||
| stp x1, x2, [sp, #-16]!; \ | |||||
| \ | |||||
| /* Save the other registers */ \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X3, X4); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X5, X6); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X7, X8); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X9, X10); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X11, X12); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X13, X14); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X15, X16); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X17, X18); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X19, X20); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X21, X22); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X23, X24); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X25, X26); \ | |||||
| SAVE_REG_PAIR(HYPCTX_REGS, X27, X28); \ | |||||
| SAVE_REG(HYPCTX_REGS, X29); \ | |||||
| SAVE_REG(HYPCTX_REGS, LR); \ | |||||
| \ | |||||
| /* Pop and save x1 and x2 */ \ | |||||
| ldp x1, x2, [sp], #16; \ | |||||
| mov x3, #HYPCTX_REGS_X1; \ | |||||
| add x3, x0, x3; \ | |||||
| stp x1, x2, [x3]; \ | |||||
| /* Pop and save x0 */ \ | |||||
| ldr x1, [sp], #16; \ | |||||
| mov x2, #HYPCTX_REGS_X0; \ | |||||
| add x2, x2, x0; \ | |||||
| str x1, [x2]; | |||||
| /* | |||||
| * Save all the guest registers. Start by saving the regular registers first | |||||
| * because those will be used as temporary registers for accessing the hypctx | |||||
| * member addresses. | |||||
| * | |||||
| * Expecting: | |||||
| * TPIDR_EL2 - struct hypctx address | |||||
| * | |||||
| * After call: | |||||
| * x0 - struct hypctx address | |||||
| */ | |||||
| #define SAVE_GUEST_REGS() \ | |||||
| SAVE_GUEST_X_REGS(); \ | |||||
| \ | |||||
| SAVE_REG(HYPCTX, FP); \ | |||||
| \ | |||||
| SAVE_SYS_REG32(HYPCTX_VTIMER_CPU, CNTKCTL_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX_VTIMER_CPU, CNTV_CVAL_EL0); \ | |||||
| SAVE_SYS_REG32(HYPCTX_VTIMER_CPU, CNTV_CTL_EL0);\ | |||||
| \ | |||||
| /* \ | |||||
| * ICH_EISR_EL2, ICH_ELRSR_EL2 and ICH_MISR_EL2 are read-only and are \ | |||||
| * saved because they are modified by the hardware as part of the \ | |||||
| * interrupt virtualization process and we need to inspect them in \ | |||||
| * the VGIC driver. \ | |||||
| */ \ | |||||
| SAVE_SYS_REG32(HYPCTX_VGIC, ICH_EISR_EL2); \ | |||||
| SAVE_SYS_REG32(HYPCTX_VGIC, ICH_ELRSR_EL2); \ | |||||
| SAVE_SYS_REG32(HYPCTX_VGIC, ICH_MISR_EL2); \ | |||||
| SAVE_SYS_REG32(HYPCTX_VGIC, ICH_HCR_EL2); \ | |||||
| SAVE_SYS_REG32(HYPCTX_VGIC, ICH_VMCR_EL2); \ | |||||
| \ | |||||
| SAVE_LR_REGS(); \ | |||||
| SAVE_AP0R_REGS(); \ | |||||
| SAVE_AP1R_REGS(); \ | |||||
| \ | |||||
| /* Save the stack pointer. */ \ | |||||
| mrs x1, sp_el1; \ | |||||
| mov x2, #HYPCTX_REGS_SP; \ | |||||
| str x1, [x0, x2]; \ | |||||
| \ | |||||
| SAVE_SYS_REG64(HYPCTX, ACTLR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, AFSR0_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, AFSR1_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, AMAIR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, CONTEXTIDR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, CPACR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, ELR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, ESR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, FAR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, MAIR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, PAR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, SCTLR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, SP_EL0); \ | |||||
| SAVE_SYS_REG64(HYPCTX, TCR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, TPIDR_EL0); \ | |||||
| SAVE_SYS_REG64(HYPCTX, TPIDRRO_EL0); \ | |||||
| SAVE_SYS_REG64(HYPCTX, TPIDR_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, TTBR0_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, TTBR1_EL1); \ | |||||
| SAVE_SYS_REG64(HYPCTX, VBAR_EL1); \ | |||||
| \ | |||||
| SAVE_SYS_REG32(HYPCTX, SPSR_EL1); \ | |||||
| \ | |||||
| SAVE_SYS_REG64(HYPCTX, CPTR_EL2); \ | |||||
| SAVE_SYS_REG64(HYPCTX, ELR_EL2); \ | |||||
| SAVE_SYS_REG64(HYPCTX, HCR_EL2); \ | |||||
| SAVE_SYS_REG64(HYPCTX, VPIDR_EL2); \ | |||||
| SAVE_SYS_REG64(HYPCTX, VMPIDR_EL2); \ | |||||
| SAVE_SYS_REG32(HYPCTX, SPSR_EL2); | |||||
| #define SAVE_GUEST_VFP_REGS() \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q0, Q1); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q2, Q3); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q4, Q5); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q6, Q7); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q8, Q9); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q10, Q11); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q12, Q13); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q14, Q15); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q16, Q17); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q18, Q19); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q20, Q21); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q22, Q23); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q24, Q25); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q26, Q27); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q28, Q29); \ | |||||
| SAVE_REG_PAIR(HYPCTX_VFPSTATE, Q30, Q31); \ | |||||
| \ | |||||
| SAVE_SYS_REG32(HYPCTX_VFPSTATE, FPCR); \ | |||||
| SAVE_SYS_REG32(HYPCTX_VFPSTATE, FPSR); | |||||
| /* See SAVE_SYS_REG */ | |||||
| #define LOAD_SYS_REG64(prefix, reg) \ | |||||
| mov x1, prefix ##_ ##reg; \ | |||||
| ldr x2, [x0, x1]; \ | |||||
| msr reg, x2; | |||||
| #define LOAD_SYS_REG32(prefix, reg) \ | |||||
| mov x1, prefix ##_ ##reg; \ | |||||
| ldr w2, [x0, x1]; \ | |||||
| msr reg, x2; | |||||
| /* See SAVE_REG_PAIR */ | |||||
| #define LOAD_REG_PAIR(prefix, reg0, reg1) \ | |||||
| mov x1, prefix ##_ ##reg0; \ | |||||
| add x1, x0, x1; \ | |||||
| ldp reg0, reg1, [x1]; | |||||
| #define LOAD_GUEST_VFP_REGS() \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q0, Q1); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q2, Q3); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q4, Q5); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q6, Q7); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q8, Q9); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q10, Q11); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q12, Q13); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q14, Q15); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q16, Q17); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q18, Q19); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q20, Q21); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q22, Q23); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q24, Q25); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q26, Q27); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q28, Q29); \ | |||||
| LOAD_REG_PAIR(HYPCTX_VFPSTATE, Q30, Q31); \ | |||||
| \ | |||||
| LOAD_SYS_REG32(HYPCTX_VFPSTATE, FPCR); \ | |||||
| LOAD_SYS_REG32(HYPCTX_VFPSTATE, FPSR); | |||||
| #define LOAD_REG(prefix, reg) \ | |||||
| mov x1, prefix ##_ ##reg; \ | |||||
| ldr reg, [x0, x1]; | |||||
| /* | |||||
| * We use x1 as a temporary register to store the hypctx member offset and x0 | |||||
| * to hold the hypctx address. We load the guest x0 and x1 register values in | |||||
| * registers x2 and x3, push x2 and x3 on the stack and then we restore x0 and | |||||
| * x1. | |||||
| */ | |||||
| #define LOAD_GUEST_X_REGS() \ | |||||
| mov x1, #HYPCTX_REGS_X0; \ | |||||
| /* x1 now holds the address of hypctx reg x0 */ \ | |||||
| add x1, x1, x0; \ | |||||
| /* Make x2 = guest x0 and x3 = guest x1 */ \ | |||||
| ldp x2, x3, [x1]; \ | |||||
| stp x2, x3, [sp, #-16]!; \ | |||||
| \ | |||||
| /* Load the other registers */ \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X2, X3); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X4, X5); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X6, X7); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X8, X9); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X10, X11); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X12, X13); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X14, X15); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X16, X17); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X18, X19); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X20, X21); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X22, X23); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X24, X25); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X26, X27); \ | |||||
| LOAD_REG_PAIR(HYPCTX_REGS, X28, X29); \ | |||||
| LOAD_REG(HYPCTX_REGS, LR); \ | |||||
| \ | |||||
| /* Pop guest x0 and x1 from the stack */ \ | |||||
| ldp x0, x1, [sp], #16; \ | |||||
| #define LOAD_ARRAY_REG64(reg, src, remaining) \ | |||||
| cmp remaining, #0; \ | |||||
| beq 9f; \ | |||||
| ldr x2, [src]; \ | |||||
| msr reg, x2; \ | |||||
| add src, src, #8; \ | |||||
| sub remaining, remaining, #1; | |||||
| #define LOAD_LR_REGS(); \ | |||||
| /* Load the number of ICH_LR_EL2 regs from memory */ \ | |||||
| mov x2, #HYPCTX_VGIC_ICH_LR_NUM; \ | |||||
| ldr x3, [x0, x2]; \ | |||||
| mov x1, #HYPCTX_VGIC_ICH_LR_EL2; \ | |||||
| /* x1 holds the load address */ \ | |||||
| add x1, x0, x1; \ | |||||
| LOAD_ARRAY_REG64(ich_lr0_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr1_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr2_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr3_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr4_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr5_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr6_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr7_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr8_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr9_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr10_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr11_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr12_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr13_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr14_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG64(ich_lr15_el2, x1, x3); \ | |||||
| 9:; \ | |||||
| ; | |||||
| #define LOAD_ARRAY_REG32(reg, src, remaining) \ | |||||
| cmp remaining, #0; \ | |||||
| beq 9f; \ | |||||
| ldr w2, [src]; \ | |||||
| msr reg, x2; \ | |||||
| add src, src, #4; \ | |||||
| sub remaining, remaining, #1; | |||||
| #define LOAD_AP0R_REGS(); \ | |||||
| /* Load the number of ICH_AP0R_EL2 regs from memory */ \ | |||||
| mov x2, #HYPCTX_VGIC_ICH_AP0R_NUM; \ | |||||
| ldr x3, [x0, x2]; \ | |||||
| /* x1 holds the load address */ \ | |||||
| mov x1, #HYPCTX_VGIC_ICH_AP0R_EL2; \ | |||||
| add x1, x0, x1; \ | |||||
| LOAD_ARRAY_REG32(ich_ap0r0_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG32(ich_ap0r1_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG32(ich_ap0r2_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG32(ich_ap0r3_el2, x1, x3); \ | |||||
| 9:; \ | |||||
| ; | |||||
| #define LOAD_AP1R_REGS(); \ | |||||
| /* Load the number of ICH_AP1R_EL2 regs from memory */ \ | |||||
| mov x2, #HYPCTX_VGIC_ICH_AP1R_NUM; \ | |||||
| ldr x3, [x0, x2]; \ | |||||
| /* x1 holds the load address */ \ | |||||
| mov x1, #HYPCTX_VGIC_ICH_AP1R_EL2; \ | |||||
| add x1, x0, x1; \ | |||||
| LOAD_ARRAY_REG32(ich_ap1r0_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG32(ich_ap1r1_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG32(ich_ap1r2_el2, x1, x3); \ | |||||
| LOAD_ARRAY_REG32(ich_ap1r3_el2, x1, x3); \ | |||||
| 9:; \ | |||||
| ; | |||||
| #define KTOHYP_REG(reg) \ | |||||
| mov x7, HYP_KVA_MASK; \ | |||||
| and reg, reg, x7; \ | |||||
| mov x7, HYP_KVA_OFFSET; \ | |||||
| orr reg, reg, x7; | |||||
| /* Load a register from struct hyp *hyp member of hypctx. */ | |||||
| #define LOAD_HYP_REG(prefix, reg) \ | |||||
| /* Compute VA of hyp member in x1 */ \ | |||||
| mov x1, #HYPCTX_HYP; \ | |||||
| add x1, x1, x0; \ | |||||
| /* Get hyp address in x2 */ \ | |||||
| ldr x2, [x1]; \ | |||||
| /* Transform hyp kernel VA into an EL2 VA */ \ | |||||
| KTOHYP_REG(x2); \ | |||||
| /* Get register offset inside struct hyp */ \ | |||||
| mov x1, prefix ##_ ##reg; \ | |||||
| /* Compute regster address */ \ | |||||
| add x2, x2, x1; \ | |||||
| /* Load the register */ \ | |||||
| ldr x1, [x2]; \ | |||||
| msr reg, x1; \ | |||||
| /* | |||||
| * Restore all the guest registers to their original values. | |||||
| * | |||||
| * Expecting: | |||||
| * x0 - struct hypctx address | |||||
| * | |||||
| * After call: | |||||
| * tpidr_el2 - struct hypctx address | |||||
| */ | |||||
| #define LOAD_GUEST_REGS() \ | |||||
| LOAD_SYS_REG64(HYPCTX, ACTLR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, AFSR0_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, AFSR1_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, AMAIR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, CONTEXTIDR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, CPACR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, ELR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, ESR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, FAR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, MAIR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, PAR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, SCTLR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, SP_EL0); \ | |||||
| LOAD_SYS_REG64(HYPCTX, TCR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, TPIDR_EL0); \ | |||||
| LOAD_SYS_REG64(HYPCTX, TPIDRRO_EL0); \ | |||||
| LOAD_SYS_REG64(HYPCTX, TPIDR_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, TTBR0_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, TTBR1_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX, VBAR_EL1); \ | |||||
| LOAD_SYS_REG32(HYPCTX, SPSR_EL1); \ | |||||
| \ | |||||
| LOAD_SYS_REG64(HYPCTX, CPTR_EL2); \ | |||||
| LOAD_SYS_REG64(HYPCTX, ELR_EL2); \ | |||||
| LOAD_SYS_REG64(HYPCTX, HCR_EL2); \ | |||||
| LOAD_SYS_REG64(HYPCTX, VPIDR_EL2); \ | |||||
| LOAD_SYS_REG64(HYPCTX, VMPIDR_EL2); \ | |||||
| LOAD_SYS_REG32(HYPCTX, SPSR_EL2); \ | |||||
| \ | |||||
| LOAD_SYS_REG32(HYPCTX_VGIC, ICH_HCR_EL2); \ | |||||
| LOAD_SYS_REG32(HYPCTX_VGIC, ICH_VMCR_EL2); \ | |||||
| \ | |||||
| LOAD_SYS_REG32(HYPCTX_VTIMER_CPU, CNTKCTL_EL1); \ | |||||
| LOAD_SYS_REG64(HYPCTX_VTIMER_CPU, CNTV_CVAL_EL0); \ | |||||
| LOAD_SYS_REG32(HYPCTX_VTIMER_CPU, CNTV_CTL_EL0); \ | |||||
| \ | |||||
| LOAD_REG(HYPCTX, FP); \ | |||||
| \ | |||||
| LOAD_HYP_REG(HYP, VTTBR_EL2); \ | |||||
| LOAD_HYP_REG(HYP_VTIMER, CNTHCTL_EL2); \ | |||||
| LOAD_HYP_REG(HYP_VTIMER, CNTVOFF_EL2); \ | |||||
| \ | |||||
| LOAD_LR_REGS(); \ | |||||
| LOAD_AP0R_REGS(); \ | |||||
| LOAD_AP1R_REGS(); \ | |||||
| \ | |||||
| /* Load the guest EL1 stack pointer */ \ | |||||
| mov x1, #HYPCTX_REGS_SP; \ | |||||
| add x1, x1, x0; \ | |||||
| ldr x2, [x1]; \ | |||||
| msr sp_el1, x2; \ | |||||
| \ | |||||
| LOAD_GUEST_X_REGS(); \ | |||||
| /* | |||||
| * Save exit information | |||||
| * | |||||
| * Expecting: | |||||
| * x0 - struct hypctx address | |||||
| */ | |||||
| #define SAVE_EXIT_INFO() \ | |||||
| SAVE_SYS_REG64(HYPCTX_EXIT_INFO, ESR_EL2); \ | |||||
| SAVE_SYS_REG64(HYPCTX_EXIT_INFO, FAR_EL2); \ | |||||
| SAVE_SYS_REG64(HYPCTX_EXIT_INFO, HPFAR_EL2); \ | |||||
| #endif /* !_VMM_HYP_MACROS_H_ */ | |||||