171 lines
4.4 KiB
ArmAsm
171 lines
4.4 KiB
ArmAsm
|
/*
|
||
|
* 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 2011 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
|
||
|
*
|
||
|
* Derived from book3s_interrupts.S, which is:
|
||
|
* Copyright SUSE Linux Products GmbH 2009
|
||
|
*
|
||
|
* 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>
|
||
|
#include <asm/exception-64s.h>
|
||
|
#include <asm/ppc-opcode.h>
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* *
|
||
|
* Guest entry / exit code that is in kernel module memory (vmalloc) *
|
||
|
* *
|
||
|
****************************************************************************/
|
||
|
|
||
|
/* Registers:
|
||
|
* r4: vcpu pointer
|
||
|
*/
|
||
|
_GLOBAL(__kvmppc_vcore_entry)
|
||
|
|
||
|
/* Write correct stack frame */
|
||
|
mflr r0
|
||
|
std r0,PPC_LR_STKOFF(r1)
|
||
|
|
||
|
/* Save host state to the stack */
|
||
|
stdu r1, -SWITCH_FRAME_SIZE(r1)
|
||
|
|
||
|
/* Save non-volatile registers (r14 - r31) and CR */
|
||
|
SAVE_NVGPRS(r1)
|
||
|
mfcr r3
|
||
|
std r3, _CCR(r1)
|
||
|
|
||
|
/* Save host DSCR */
|
||
|
BEGIN_FTR_SECTION
|
||
|
mfspr r3, SPRN_DSCR
|
||
|
std r3, HSTATE_DSCR(r13)
|
||
|
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
|
||
|
|
||
|
/* Save host DABR */
|
||
|
mfspr r3, SPRN_DABR
|
||
|
std r3, HSTATE_DABR(r13)
|
||
|
|
||
|
/* Hard-disable interrupts */
|
||
|
mfmsr r10
|
||
|
std r10, HSTATE_HOST_MSR(r13)
|
||
|
rldicl r10,r10,48,1
|
||
|
rotldi r10,r10,16
|
||
|
mtmsrd r10,1
|
||
|
|
||
|
/* Save host PMU registers and load guest PMU registers */
|
||
|
/* R4 is live here (vcpu pointer) but not r3 or r5 */
|
||
|
li r3, 1
|
||
|
sldi r3, r3, 31 /* MMCR0_FC (freeze counters) bit */
|
||
|
mfspr r7, SPRN_MMCR0 /* save MMCR0 */
|
||
|
mtspr SPRN_MMCR0, r3 /* freeze all counters, disable interrupts */
|
||
|
isync
|
||
|
ld r3, PACALPPACAPTR(r13) /* is the host using the PMU? */
|
||
|
lbz r5, LPPACA_PMCINUSE(r3)
|
||
|
cmpwi r5, 0
|
||
|
beq 31f /* skip if not */
|
||
|
mfspr r5, SPRN_MMCR1
|
||
|
mfspr r6, SPRN_MMCRA
|
||
|
std r7, HSTATE_MMCR(r13)
|
||
|
std r5, HSTATE_MMCR + 8(r13)
|
||
|
std r6, HSTATE_MMCR + 16(r13)
|
||
|
mfspr r3, SPRN_PMC1
|
||
|
mfspr r5, SPRN_PMC2
|
||
|
mfspr r6, SPRN_PMC3
|
||
|
mfspr r7, SPRN_PMC4
|
||
|
mfspr r8, SPRN_PMC5
|
||
|
mfspr r9, SPRN_PMC6
|
||
|
BEGIN_FTR_SECTION
|
||
|
mfspr r10, SPRN_PMC7
|
||
|
mfspr r11, SPRN_PMC8
|
||
|
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
|
||
|
stw r3, HSTATE_PMC(r13)
|
||
|
stw r5, HSTATE_PMC + 4(r13)
|
||
|
stw r6, HSTATE_PMC + 8(r13)
|
||
|
stw r7, HSTATE_PMC + 12(r13)
|
||
|
stw r8, HSTATE_PMC + 16(r13)
|
||
|
stw r9, HSTATE_PMC + 20(r13)
|
||
|
BEGIN_FTR_SECTION
|
||
|
stw r10, HSTATE_PMC + 24(r13)
|
||
|
stw r11, HSTATE_PMC + 28(r13)
|
||
|
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
|
||
|
31:
|
||
|
|
||
|
/*
|
||
|
* Put whatever is in the decrementer into the
|
||
|
* hypervisor decrementer.
|
||
|
*/
|
||
|
mfspr r8,SPRN_DEC
|
||
|
mftb r7
|
||
|
mtspr SPRN_HDEC,r8
|
||
|
extsw r8,r8
|
||
|
add r8,r8,r7
|
||
|
std r8,HSTATE_DECEXP(r13)
|
||
|
|
||
|
/*
|
||
|
* On PPC970, if the guest vcpu has an external interrupt pending,
|
||
|
* send ourselves an IPI so as to interrupt the guest once it
|
||
|
* enables interrupts. (It must have interrupts disabled,
|
||
|
* otherwise we would already have delivered the interrupt.)
|
||
|
*/
|
||
|
BEGIN_FTR_SECTION
|
||
|
ld r0, VCPU_PENDING_EXC(r4)
|
||
|
li r7, (1 << BOOK3S_IRQPRIO_EXTERNAL)
|
||
|
oris r7, r7, (1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h
|
||
|
and. r0, r0, r7
|
||
|
beq 32f
|
||
|
mr r31, r4
|
||
|
lhz r3, PACAPACAINDEX(r13)
|
||
|
bl smp_send_reschedule
|
||
|
nop
|
||
|
mr r4, r31
|
||
|
32:
|
||
|
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
|
||
|
|
||
|
/* Jump to partition switch code */
|
||
|
bl .kvmppc_hv_entry_trampoline
|
||
|
nop
|
||
|
|
||
|
/*
|
||
|
* We return here in virtual mode after the guest exits
|
||
|
* with something that we can't handle in real mode.
|
||
|
* Interrupts are enabled again at this point.
|
||
|
*/
|
||
|
|
||
|
.global kvmppc_handler_highmem
|
||
|
kvmppc_handler_highmem:
|
||
|
|
||
|
/*
|
||
|
* Register usage at this point:
|
||
|
*
|
||
|
* R1 = host R1
|
||
|
* R2 = host R2
|
||
|
* R12 = exit handler id
|
||
|
* R13 = PACA
|
||
|
*/
|
||
|
|
||
|
/* Restore non-volatile host registers (r14 - r31) and CR */
|
||
|
REST_NVGPRS(r1)
|
||
|
ld r4, _CCR(r1)
|
||
|
mtcr r4
|
||
|
|
||
|
addi r1, r1, SWITCH_FRAME_SIZE
|
||
|
ld r0, PPC_LR_STKOFF(r1)
|
||
|
mtlr r0
|
||
|
blr
|