/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright SUSE Linux Products GmbH 2010 * Copyright 2010-2011 Freescale Semiconductor, Inc. * * Authors: Alexander Graf <agraf@suse.de> */ #include <asm/ppc_asm.h> #include <asm/kvm_asm.h> #include <asm/reg.h> #include <asm/page.h> #include <asm/asm-offsets.h> #define KVM_MAGIC_PAGE (-4096) #ifdef CONFIG_64BIT #define LL64(reg, offs, reg2) ld reg, (offs)(reg2) #define STL64(reg, offs, reg2) std reg, (offs)(reg2) #else #define LL64(reg, offs, reg2) lwz reg, (offs + 4)(reg2) #define STL64(reg, offs, reg2) stw reg, (offs + 4)(reg2) #endif #define SCRATCH_SAVE \ /* Enable critical section. We are critical if \ shared->critical == r1 */ \ STL64(r1, KVM_MAGIC_PAGE + KVM_MAGIC_CRITICAL, 0); \ \ /* Save state */ \ PPC_STL r31, (KVM_MAGIC_PAGE + KVM_MAGIC_SCRATCH1)(0); \ PPC_STL r30, (KVM_MAGIC_PAGE + KVM_MAGIC_SCRATCH2)(0); \ mfcr r31; \ stw r31, (KVM_MAGIC_PAGE + KVM_MAGIC_SCRATCH3)(0); #define SCRATCH_RESTORE \ /* Restore state */ \ PPC_LL r31, (KVM_MAGIC_PAGE + KVM_MAGIC_SCRATCH1)(0); \ lwz r30, (KVM_MAGIC_PAGE + KVM_MAGIC_SCRATCH3)(0); \ mtcr r30; \ PPC_LL r30, (KVM_MAGIC_PAGE + KVM_MAGIC_SCRATCH2)(0); \ \ /* Disable critical section. We are critical if \ shared->critical == r1 and r2 is always != r1 */ \ STL64(r2, KVM_MAGIC_PAGE + KVM_MAGIC_CRITICAL, 0); .global kvm_template_start kvm_template_start: .global kvm_emulate_mtmsrd kvm_emulate_mtmsrd: SCRATCH_SAVE /* Put MSR & ~(MSR_EE|MSR_RI) in r31 */ LL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) lis r30, (~(MSR_EE | MSR_RI))@h ori r30, r30, (~(MSR_EE | MSR_RI))@l and r31, r31, r30 /* OR the register's (MSR_EE|MSR_RI) on MSR */ kvm_emulate_mtmsrd_reg: ori r30, r0, 0 andi. r30, r30, (MSR_EE|MSR_RI) or r31, r31, r30 /* Put MSR back into magic page */ STL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) /* Check if we have to fetch an interrupt */ lwz r31, (KVM_MAGIC_PAGE + KVM_MAGIC_INT)(0) cmpwi r31, 0 beq+ no_check /* Check if we may trigger an interrupt */ andi. r30, r30, MSR_EE beq no_check SCRATCH_RESTORE /* Nag hypervisor */ kvm_emulate_mtmsrd_orig_ins: tlbsync b kvm_emulate_mtmsrd_branch no_check: SCRATCH_RESTORE /* Go back to caller */ kvm_emulate_mtmsrd_branch: b . kvm_emulate_mtmsrd_end: .global kvm_emulate_mtmsrd_branch_offs kvm_emulate_mtmsrd_branch_offs: .long (kvm_emulate_mtmsrd_branch - kvm_emulate_mtmsrd) / 4 .global kvm_emulate_mtmsrd_reg_offs kvm_emulate_mtmsrd_reg_offs: .long (kvm_emulate_mtmsrd_reg - kvm_emulate_mtmsrd) / 4 .global kvm_emulate_mtmsrd_orig_ins_offs kvm_emulate_mtmsrd_orig_ins_offs: .long (kvm_emulate_mtmsrd_orig_ins - kvm_emulate_mtmsrd) / 4 .global kvm_emulate_mtmsrd_len kvm_emulate_mtmsrd_len: .long (kvm_emulate_mtmsrd_end - kvm_emulate_mtmsrd) / 4 #define MSR_SAFE_BITS (MSR_EE | MSR_RI) #define MSR_CRITICAL_BITS ~MSR_SAFE_BITS .global kvm_emulate_mtmsr kvm_emulate_mtmsr: SCRATCH_SAVE /* Fetch old MSR in r31 */ LL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) /* Find the changed bits between old and new MSR */ kvm_emulate_mtmsr_reg1: ori r30, r0, 0 xor r31, r30, r31 /* Check if we need to really do mtmsr */ LOAD_REG_IMMEDIATE(r30, MSR_CRITICAL_BITS) and. r31, r31, r30 /* No critical bits changed? Maybe we can stay in the guest. */ beq maybe_stay_in_guest do_mtmsr: SCRATCH_RESTORE /* Just fire off the mtmsr if it's critical */ kvm_emulate_mtmsr_orig_ins: mtmsr r0 b kvm_emulate_mtmsr_branch maybe_stay_in_guest: /* Get the target register in r30 */ kvm_emulate_mtmsr_reg2: ori r30, r0, 0 /* Put MSR into magic page because we don't call mtmsr */ STL64(r30, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) /* Check if we have to fetch an interrupt */ lwz r31, (KVM_MAGIC_PAGE + KVM_MAGIC_INT)(0) cmpwi r31, 0 beq+ no_mtmsr /* Check if we may trigger an interrupt */ andi. r31, r30, MSR_EE bne do_mtmsr no_mtmsr: SCRATCH_RESTORE /* Go back to caller */ kvm_emulate_mtmsr_branch: b . kvm_emulate_mtmsr_end: .global kvm_emulate_mtmsr_branch_offs kvm_emulate_mtmsr_branch_offs: .long (kvm_emulate_mtmsr_branch - kvm_emulate_mtmsr) / 4 .global kvm_emulate_mtmsr_reg1_offs kvm_emulate_mtmsr_reg1_offs: .long (kvm_emulate_mtmsr_reg1 - kvm_emulate_mtmsr) / 4 .global kvm_emulate_mtmsr_reg2_offs kvm_emulate_mtmsr_reg2_offs: .long (kvm_emulate_mtmsr_reg2 - kvm_emulate_mtmsr) / 4 .global kvm_emulate_mtmsr_orig_ins_offs kvm_emulate_mtmsr_orig_ins_offs: .long (kvm_emulate_mtmsr_orig_ins - kvm_emulate_mtmsr) / 4 .global kvm_emulate_mtmsr_len kvm_emulate_mtmsr_len: .long (kvm_emulate_mtmsr_end - kvm_emulate_mtmsr) / 4 /* also used for wrteei 1 */ .global kvm_emulate_wrtee kvm_emulate_wrtee: SCRATCH_SAVE /* Fetch old MSR in r31 */ LL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) /* Insert new MSR[EE] */ kvm_emulate_wrtee_reg: ori r30, r0, 0 rlwimi r31, r30, 0, MSR_EE /* * If MSR[EE] is now set, check for a pending interrupt. * We could skip this if MSR[EE] was already on, but that * should be rare, so don't bother. */ andi. r30, r30, MSR_EE /* Put MSR into magic page because we don't call wrtee */ STL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) beq no_wrtee /* Check if we have to fetch an interrupt */ lwz r30, (KVM_MAGIC_PAGE + KVM_MAGIC_INT)(0) cmpwi r30, 0 bne do_wrtee no_wrtee: SCRATCH_RESTORE /* Go back to caller */ kvm_emulate_wrtee_branch: b . do_wrtee: SCRATCH_RESTORE /* Just fire off the wrtee if it's critical */ kvm_emulate_wrtee_orig_ins: wrtee r0 b kvm_emulate_wrtee_branch kvm_emulate_wrtee_end: .global kvm_emulate_wrtee_branch_offs kvm_emulate_wrtee_branch_offs: .long (kvm_emulate_wrtee_branch - kvm_emulate_wrtee) / 4 .global kvm_emulate_wrtee_reg_offs kvm_emulate_wrtee_reg_offs: .long (kvm_emulate_wrtee_reg - kvm_emulate_wrtee) / 4 .global kvm_emulate_wrtee_orig_ins_offs kvm_emulate_wrtee_orig_ins_offs: .long (kvm_emulate_wrtee_orig_ins - kvm_emulate_wrtee) / 4 .global kvm_emulate_wrtee_len kvm_emulate_wrtee_len: .long (kvm_emulate_wrtee_end - kvm_emulate_wrtee) / 4 .global kvm_emulate_wrteei_0 kvm_emulate_wrteei_0: SCRATCH_SAVE /* Fetch old MSR in r31 */ LL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) /* Remove MSR_EE from old MSR */ rlwinm r31, r31, 0, ~MSR_EE /* Write new MSR value back */ STL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) SCRATCH_RESTORE /* Go back to caller */ kvm_emulate_wrteei_0_branch: b . kvm_emulate_wrteei_0_end: .global kvm_emulate_wrteei_0_branch_offs kvm_emulate_wrteei_0_branch_offs: .long (kvm_emulate_wrteei_0_branch - kvm_emulate_wrteei_0) / 4 .global kvm_emulate_wrteei_0_len kvm_emulate_wrteei_0_len: .long (kvm_emulate_wrteei_0_end - kvm_emulate_wrteei_0) / 4 .global kvm_emulate_mtsrin kvm_emulate_mtsrin: SCRATCH_SAVE LL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) andi. r31, r31, MSR_DR | MSR_IR beq kvm_emulate_mtsrin_reg1 SCRATCH_RESTORE kvm_emulate_mtsrin_orig_ins: nop b kvm_emulate_mtsrin_branch kvm_emulate_mtsrin_reg1: /* rX >> 26 */ rlwinm r30,r0,6,26,29 kvm_emulate_mtsrin_reg2: stw r0, (KVM_MAGIC_PAGE + KVM_MAGIC_SR)(r30) SCRATCH_RESTORE /* Go back to caller */ kvm_emulate_mtsrin_branch: b . kvm_emulate_mtsrin_end: .global kvm_emulate_mtsrin_branch_offs kvm_emulate_mtsrin_branch_offs: .long (kvm_emulate_mtsrin_branch - kvm_emulate_mtsrin) / 4 .global kvm_emulate_mtsrin_reg1_offs kvm_emulate_mtsrin_reg1_offs: .long (kvm_emulate_mtsrin_reg1 - kvm_emulate_mtsrin) / 4 .global kvm_emulate_mtsrin_reg2_offs kvm_emulate_mtsrin_reg2_offs: .long (kvm_emulate_mtsrin_reg2 - kvm_emulate_mtsrin) / 4 .global kvm_emulate_mtsrin_orig_ins_offs kvm_emulate_mtsrin_orig_ins_offs: .long (kvm_emulate_mtsrin_orig_ins - kvm_emulate_mtsrin) / 4 .global kvm_emulate_mtsrin_len kvm_emulate_mtsrin_len: .long (kvm_emulate_mtsrin_end - kvm_emulate_mtsrin) / 4 .global kvm_template_end kvm_template_end: