44x_emulate.c 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * 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 IBM Corp. 2008
 *
 * Authors: Hollis Blanchard <hollisb@us.ibm.com>
 */

#include <asm/kvm_ppc.h>
#include <asm/dcr.h>
#include <asm/dcr-regs.h>
#include <asm/disassemble.h>
24
#include <asm/kvm_44x.h>
25
#include "timing.h"
26 27 28 29

#include "booke.h"
#include "44x_tlb.h"

A
Alexander Graf 已提交
30
#define XOP_MFDCRX  259
31
#define XOP_MFDCR   323
A
Alexander Graf 已提交
32
#define XOP_MTDCRX  387
33 34 35 36 37
#define XOP_MTDCR   451
#define XOP_TLBSX   914
#define XOP_ICCCI   966
#define XOP_TLBWE   978

A
Alexander Graf 已提交
38 39 40 41 42 43 44 45 46 47 48
static int emulate_mtdcr(struct kvm_vcpu *vcpu, int rs, int dcrn)
{
	/* emulate some access in kernel */
	switch (dcrn) {
	case DCRN_CPR0_CONFIG_ADDR:
		vcpu->arch.cpr0_cfgaddr = kvmppc_get_gpr(vcpu, rs);
		return EMULATE_DONE;
	default:
		vcpu->run->dcr.dcrn = dcrn;
		vcpu->run->dcr.data = kvmppc_get_gpr(vcpu, rs);
		vcpu->run->dcr.is_write = 1;
A
Alexander Graf 已提交
49
		vcpu->arch.dcr_is_write = 1;
A
Alexander Graf 已提交
50 51 52 53 54 55
		vcpu->arch.dcr_needed = 1;
		kvmppc_account_exit(vcpu, DCR_EXITS);
		return EMULATE_DO_DCR;
	}
}

A
Alexander Graf 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
static int emulate_mfdcr(struct kvm_vcpu *vcpu, int rt, int dcrn)
{
	/* The guest may access CPR0 registers to determine the timebase
	 * frequency, and it must know the real host frequency because it
	 * can directly access the timebase registers.
	 *
	 * It would be possible to emulate those accesses in userspace,
	 * but userspace can really only figure out the end frequency.
	 * We could decompose that into the factors that compute it, but
	 * that's tricky math, and it's easier to just report the real
	 * CPR0 values.
	 */
	switch (dcrn) {
	case DCRN_CPR0_CONFIG_ADDR:
		kvmppc_set_gpr(vcpu, rt, vcpu->arch.cpr0_cfgaddr);
		break;
	case DCRN_CPR0_CONFIG_DATA:
		local_irq_disable();
		mtdcr(DCRN_CPR0_CONFIG_ADDR,
			  vcpu->arch.cpr0_cfgaddr);
		kvmppc_set_gpr(vcpu, rt,
			       mfdcr(DCRN_CPR0_CONFIG_DATA));
		local_irq_enable();
		break;
	default:
		vcpu->run->dcr.dcrn = dcrn;
		vcpu->run->dcr.data =  0;
		vcpu->run->dcr.is_write = 0;
A
Alexander Graf 已提交
84
		vcpu->arch.dcr_is_write = 0;
A
Alexander Graf 已提交
85 86 87 88 89 90 91 92 93
		vcpu->arch.io_gpr = rt;
		vcpu->arch.dcr_needed = 1;
		kvmppc_account_exit(vcpu, DCR_EXITS);
		return EMULATE_DO_DCR;
	}

	return EMULATE_DONE;
}

94 95
int kvmppc_core_emulate_op_44x(struct kvm_run *run, struct kvm_vcpu *vcpu,
			       unsigned int inst, int *advance)
96 97
{
	int emulated = EMULATE_DONE;
98 99 100 101 102 103 104
	int dcrn = get_dcrn(inst);
	int ra = get_ra(inst);
	int rb = get_rb(inst);
	int rc = get_rc(inst);
	int rs = get_rs(inst);
	int rt = get_rt(inst);
	int ws = get_ws(inst);
105 106 107 108 109 110

	switch (get_op(inst)) {
	case 31:
		switch (get_xop(inst)) {

		case XOP_MFDCR:
A
Alexander Graf 已提交
111 112
			emulated = emulate_mfdcr(vcpu, rt, dcrn);
			break;
113

A
Alexander Graf 已提交
114 115 116
		case XOP_MFDCRX:
			emulated = emulate_mfdcr(vcpu, rt,
					kvmppc_get_gpr(vcpu, ra));
117 118 119
			break;

		case XOP_MTDCR:
A
Alexander Graf 已提交
120 121
			emulated = emulate_mtdcr(vcpu, rs, dcrn);
			break;
122

A
Alexander Graf 已提交
123 124 125
		case XOP_MTDCRX:
			emulated = emulate_mtdcr(vcpu, rs,
					kvmppc_get_gpr(vcpu, ra));
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
			break;

		case XOP_TLBWE:
			emulated = kvmppc_44x_emul_tlbwe(vcpu, ra, rs, ws);
			break;

		case XOP_TLBSX:
			emulated = kvmppc_44x_emul_tlbsx(vcpu, rt, ra, rb, rc);
			break;

		case XOP_ICCCI:
			break;

		default:
			emulated = EMULATE_FAIL;
		}

		break;

	default:
		emulated = EMULATE_FAIL;
	}

149 150 151
	if (emulated == EMULATE_FAIL)
		emulated = kvmppc_booke_emulate_op(run, vcpu, inst, advance);

152 153 154
	return emulated;
}

155
int kvmppc_core_emulate_mtspr_44x(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
156
{
157 158
	int emulated = EMULATE_DONE;

159 160
	switch (sprn) {
	case SPRN_PID:
161
		kvmppc_set_pid(vcpu, spr_val); break;
162
	case SPRN_MMUCR:
163
		vcpu->arch.mmucr = spr_val; break;
164
	case SPRN_CCR0:
165
		vcpu->arch.ccr0 = spr_val; break;
166
	case SPRN_CCR1:
167
		vcpu->arch.ccr1 = spr_val; break;
168
	default:
169
		emulated = kvmppc_booke_emulate_mtspr(vcpu, sprn, spr_val);
170 171
	}

172
	return emulated;
173 174
}

175
int kvmppc_core_emulate_mfspr_44x(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val)
176
{
177 178
	int emulated = EMULATE_DONE;

179
	switch (sprn) {
180
	case SPRN_PID:
181
		*spr_val = vcpu->arch.pid; break;
182
	case SPRN_MMUCR:
183
		*spr_val = vcpu->arch.mmucr; break;
184
	case SPRN_CCR0:
185
		*spr_val = vcpu->arch.ccr0; break;
186
	case SPRN_CCR1:
187
		*spr_val = vcpu->arch.ccr1; break;
188
	default:
189
		emulated = kvmppc_booke_emulate_mfspr(vcpu, sprn, spr_val);
190 191
	}

192
	return emulated;
193 194
}