| /* |
| * 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: |