44x_emulate.c 4.5 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

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

#define XOP_MFDCR   323
#define XOP_MTDCR   451
#define XOP_TLBSX   914
#define XOP_ICCCI   966
#define XOP_TLBWE   978

int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                           unsigned int inst, int *advance)
{
	int emulated = EMULATE_DONE;
	int dcrn;
	int ra;
	int rb;
	int rc;
	int rs;
	int rt;
	int ws;

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

		case XOP_MFDCR:
			dcrn = get_dcrn(inst);
			rt = get_rt(inst);

			/* 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:
68
				kvmppc_set_gpr(vcpu, rt, vcpu->arch.cpr0_cfgaddr);
69 70 71 72 73
				break;
			case DCRN_CPR0_CONFIG_DATA:
				local_irq_disable();
				mtdcr(DCRN_CPR0_CONFIG_ADDR,
					  vcpu->arch.cpr0_cfgaddr);
74 75
				kvmppc_set_gpr(vcpu, rt,
					       mfdcr(DCRN_CPR0_CONFIG_DATA));
76 77 78 79 80 81 82 83
				local_irq_enable();
				break;
			default:
				run->dcr.dcrn = dcrn;
				run->dcr.data =  0;
				run->dcr.is_write = 0;
				vcpu->arch.io_gpr = rt;
				vcpu->arch.dcr_needed = 1;
84
				kvmppc_account_exit(vcpu, DCR_EXITS);
85 86 87 88 89 90 91 92 93 94 95 96
				emulated = EMULATE_DO_DCR;
			}

			break;

		case XOP_MTDCR:
			dcrn = get_dcrn(inst);
			rs = get_rs(inst);

			/* emulate some access in kernel */
			switch (dcrn) {
			case DCRN_CPR0_CONFIG_ADDR:
97
				vcpu->arch.cpr0_cfgaddr = kvmppc_get_gpr(vcpu, rs);
98 99 100
				break;
			default:
				run->dcr.dcrn = dcrn;
101
				run->dcr.data = kvmppc_get_gpr(vcpu, rs);
102 103
				run->dcr.is_write = 1;
				vcpu->arch.dcr_needed = 1;
104
				kvmppc_account_exit(vcpu, DCR_EXITS);
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
				emulated = EMULATE_DO_DCR;
			}

			break;

		case XOP_TLBWE:
			ra = get_ra(inst);
			rs = get_rs(inst);
			ws = get_ws(inst);
			emulated = kvmppc_44x_emul_tlbwe(vcpu, ra, rs, ws);
			break;

		case XOP_TLBSX:
			rt = get_rt(inst);
			ra = get_ra(inst);
			rb = get_rb(inst);
			rc = get_rc(inst);
			emulated = kvmppc_44x_emul_tlbsx(vcpu, rt, ra, rb, rc);
			break;

		case XOP_ICCCI:
			break;

		default:
			emulated = EMULATE_FAIL;
		}

		break;

	default:
		emulated = EMULATE_FAIL;
	}

138 139 140
	if (emulated == EMULATE_FAIL)
		emulated = kvmppc_booke_emulate_op(run, vcpu, inst, advance);

141 142 143 144 145
	return emulated;
}

int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
{
146 147
	int emulated = EMULATE_DONE;

148 149
	switch (sprn) {
	case SPRN_PID:
150
		kvmppc_set_pid(vcpu, kvmppc_get_gpr(vcpu, rs)); break;
151
	case SPRN_MMUCR:
152
		vcpu->arch.mmucr = kvmppc_get_gpr(vcpu, rs); break;
153
	case SPRN_CCR0:
154
		vcpu->arch.ccr0 = kvmppc_get_gpr(vcpu, rs); break;
155
	case SPRN_CCR1:
156
		vcpu->arch.ccr1 = kvmppc_get_gpr(vcpu, rs); break;
157
	default:
158
		emulated = kvmppc_booke_emulate_mtspr(vcpu, sprn, rs);
159 160
	}

161
	kvmppc_set_exit_type(vcpu, EMULATED_MTSPR_EXITS);
162
	return emulated;
163 164 165 166
}

int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
{
167 168
	int emulated = EMULATE_DONE;

169
	switch (sprn) {
170
	case SPRN_PID:
171
		kvmppc_set_gpr(vcpu, rt, vcpu->arch.pid); break;
172
	case SPRN_MMUCR:
173
		kvmppc_set_gpr(vcpu, rt, vcpu->arch.mmucr); break;
174
	case SPRN_CCR0:
175
		kvmppc_set_gpr(vcpu, rt, vcpu->arch.ccr0); break;
176
	case SPRN_CCR1:
177
		kvmppc_set_gpr(vcpu, rt, vcpu->arch.ccr1); break;
178
	default:
179
		emulated = kvmppc_booke_emulate_mfspr(vcpu, sprn, rt);
180 181
	}

182
	kvmppc_set_exit_type(vcpu, EMULATED_MFSPR_EXITS);
183
	return emulated;
184 185
}