Commit bc8080cb authored by Hollis Blanchard's avatar Hollis Blanchard Committed by Avi Kivity
Browse files

KVM: ppc: E500 core-specific code



Signed-off-by: default avatarLiu Yu <yu.liu@freescale.com>
Signed-off-by: default avatarHollis Blanchard <hollisb@us.ibm.com>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent 17c885eb
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
 *
 * Author: Yu Liu, <yu.liu@freescale.com>
 *
 * Description:
 * This file is derived from arch/powerpc/include/asm/kvm_44x.h,
 * by Hollis Blanchard <hollisb@us.ibm.com>.
 *
 * 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.
 */

#ifndef __ASM_KVM_E500_H__
#define __ASM_KVM_E500_H__

#include <linux/kvm_host.h>

#define BOOKE_INTERRUPT_SIZE 36

#define E500_PID_NUM   3
#define E500_TLB_NUM   2

struct tlbe{
	u32 mas1;
	u32 mas2;
	u32 mas3;
	u32 mas7;
};

struct kvmppc_vcpu_e500 {
	/* Unmodified copy of the guest's TLB. */
	struct tlbe *guest_tlb[E500_TLB_NUM];
	/* TLB that's actually used when the guest is running. */
	struct tlbe *shadow_tlb[E500_TLB_NUM];
	/* Pages which are referenced in the shadow TLB. */
	struct page **shadow_pages[E500_TLB_NUM];

	unsigned int guest_tlb_size[E500_TLB_NUM];
	unsigned int shadow_tlb_size[E500_TLB_NUM];
	unsigned int guest_tlb_nv[E500_TLB_NUM];

	u32 host_pid[E500_PID_NUM];
	u32 pid[E500_PID_NUM];

	u32 mas0;
	u32 mas1;
	u32 mas2;
	u32 mas3;
	u32 mas4;
	u32 mas5;
	u32 mas6;
	u32 mas7;
	u32 l1csr1;
	u32 hid0;
	u32 hid1;

	struct kvm_vcpu vcpu;
};

static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu)
{
	return container_of(vcpu, struct kvmppc_vcpu_e500, vcpu);
}

#endif /* __ASM_KVM_E500_H__ */
+13 −0
Original line number Diff line number Diff line
@@ -43,6 +43,19 @@ config KVM_EXIT_TIMING

	  If unsure, say N.

config KVM_E500
	bool "KVM support for PowerPC E500 processors"
	depends on EXPERIMENTAL && E500
	select KVM
	---help---
	  Support running unmodified E500 guest kernels in virtual machines on
	  E500 host processors.

	  This module provides access to the hardware capabilities through
	  a character device node named /dev/kvm.

	  If unsure, say N.

config KVM_TRACE
	bool "KVM trace support"
	depends on KVM && MARKERS && SYSFS
+9 −0
Original line number Diff line number Diff line
@@ -22,3 +22,12 @@ kvm-440-objs := \
	44x_tlb.o \
	44x_emulate.o
obj-$(CONFIG_KVM_440) += kvm-440.o

kvm-e500-objs := \
	booke.o \
	booke_emulate.o \
	booke_interrupts.o \
	e500.o \
	e500_tlb.o \
	e500_emulate.o
obj-$(CONFIG_KVM_E500) += kvm-e500.o
+151 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
 *
 * Author: Yu Liu, <yu.liu@freescale.com>
 *
 * Description:
 * This file is derived from arch/powerpc/kvm/44x.c,
 * by Hollis Blanchard <hollisb@us.ibm.com>.
 *
 * 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.
 */

#include <linux/kvm_host.h>
#include <linux/err.h>

#include <asm/reg.h>
#include <asm/cputable.h>
#include <asm/tlbflush.h>
#include <asm/kvm_e500.h>
#include <asm/kvm_ppc.h>

#include "e500_tlb.h"

void kvmppc_core_load_host_debugstate(struct kvm_vcpu *vcpu)
{
}

void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu)
{
}

void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
	kvmppc_e500_tlb_load(vcpu, cpu);
}

void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
{
	kvmppc_e500_tlb_put(vcpu);
}

int kvmppc_core_check_processor_compat(void)
{
	int r;

	if (strcmp(cur_cpu_spec->cpu_name, "e500v2") == 0)
		r = 0;
	else
		r = -ENOTSUPP;

	return r;
}

int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
{
	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);

	kvmppc_e500_tlb_setup(vcpu_e500);

	/* Use the same core vertion as host's */
	vcpu->arch.pvr = mfspr(SPRN_PVR);

	return 0;
}

/* 'linear_address' is actually an encoding of AS|PID|EADDR . */
int kvmppc_core_vcpu_translate(struct kvm_vcpu *vcpu,
                               struct kvm_translation *tr)
{
	int index;
	gva_t eaddr;
	u8 pid;
	u8 as;

	eaddr = tr->linear_address;
	pid = (tr->linear_address >> 32) & 0xff;
	as = (tr->linear_address >> 40) & 0x1;

	index = kvmppc_e500_tlb_search(vcpu, eaddr, pid, as);
	if (index < 0) {
		tr->valid = 0;
		return 0;
	}

	tr->physical_address = kvmppc_mmu_xlate(vcpu, index, eaddr);
	/* XXX what does "writeable" and "usermode" even mean? */
	tr->valid = 1;

	return 0;
}

struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
{
	struct kvmppc_vcpu_e500 *vcpu_e500;
	struct kvm_vcpu *vcpu;
	int err;

	vcpu_e500 = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL);
	if (!vcpu_e500) {
		err = -ENOMEM;
		goto out;
	}

	vcpu = &vcpu_e500->vcpu;
	err = kvm_vcpu_init(vcpu, kvm, id);
	if (err)
		goto free_vcpu;

	err = kvmppc_e500_tlb_init(vcpu_e500);
	if (err)
		goto uninit_vcpu;

	return vcpu;

uninit_vcpu:
	kvm_vcpu_uninit(vcpu);
free_vcpu:
	kmem_cache_free(kvm_vcpu_cache, vcpu_e500);
out:
	return ERR_PTR(err);
}

void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
{
	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);

	kvmppc_e500_tlb_uninit(vcpu_e500);
	kvm_vcpu_uninit(vcpu);
	kmem_cache_free(kvm_vcpu_cache, vcpu_e500);
}

static int kvmppc_e500_init(void)
{
	int r;

	r = kvmppc_booke_init();
	if (r)
		return r;

	return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), THIS_MODULE);
}

static void kvmppc_e500_exit(void)
{
	kvmppc_booke_exit();
}

module_init(kvmppc_e500_init);
module_exit(kvmppc_e500_exit);
+169 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
 *
 * Author: Yu Liu, <yu.liu@freescale.com>
 *
 * Description:
 * This file is derived from arch/powerpc/kvm/44x_emulate.c,
 * by Hollis Blanchard <hollisb@us.ibm.com>.
 *
 * 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.
 */

#include <asm/kvm_ppc.h>
#include <asm/disassemble.h>
#include <asm/kvm_e500.h>

#include "booke.h"
#include "e500_tlb.h"

#define XOP_TLBIVAX 786
#define XOP_TLBSX   914
#define XOP_TLBRE   946
#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 ra;
	int rb;
	int rs;
	int rt;

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

		case XOP_TLBRE:
			emulated = kvmppc_e500_emul_tlbre(vcpu);
			break;

		case XOP_TLBWE:
			emulated = kvmppc_e500_emul_tlbwe(vcpu);
			break;

		case XOP_TLBSX:
			rb = get_rb(inst);
			emulated = kvmppc_e500_emul_tlbsx(vcpu,rb);
			break;

		case XOP_TLBIVAX:
			ra = get_ra(inst);
			rb = get_rb(inst);
			emulated = kvmppc_e500_emul_tlbivax(vcpu, ra, rb);
			break;

		default:
			emulated = EMULATE_FAIL;
		}

		break;

	default:
		emulated = EMULATE_FAIL;
	}

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

	return emulated;
}

int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
{
	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
	int emulated = EMULATE_DONE;

	switch (sprn) {
	case SPRN_PID:
		vcpu_e500->pid[0] = vcpu->arch.shadow_pid =
			vcpu->arch.pid = vcpu->arch.gpr[rs];
		break;
	case SPRN_PID1:
		vcpu_e500->pid[1] = vcpu->arch.gpr[rs]; break;
	case SPRN_PID2:
		vcpu_e500->pid[2] = vcpu->arch.gpr[rs]; break;
	case SPRN_MAS0:
		vcpu_e500->mas0 = vcpu->arch.gpr[rs]; break;
	case SPRN_MAS1:
		vcpu_e500->mas1 = vcpu->arch.gpr[rs]; break;
	case SPRN_MAS2:
		vcpu_e500->mas2 = vcpu->arch.gpr[rs]; break;
	case SPRN_MAS3:
		vcpu_e500->mas3 = vcpu->arch.gpr[rs]; break;
	case SPRN_MAS4:
		vcpu_e500->mas4 = vcpu->arch.gpr[rs]; break;
	case SPRN_MAS6:
		vcpu_e500->mas6 = vcpu->arch.gpr[rs]; break;
	case SPRN_MAS7:
		vcpu_e500->mas7 = vcpu->arch.gpr[rs]; break;
	case SPRN_L1CSR1:
		vcpu_e500->l1csr1 = vcpu->arch.gpr[rs]; break;
	case SPRN_HID0:
		vcpu_e500->hid0 = vcpu->arch.gpr[rs]; break;
	case SPRN_HID1:
		vcpu_e500->hid1 = vcpu->arch.gpr[rs]; break;

	default:
		emulated = kvmppc_booke_emulate_mtspr(vcpu, sprn, rs);
	}

	return emulated;
}

int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
{
	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
	int emulated = EMULATE_DONE;

	switch (sprn) {
	case SPRN_PID:
		vcpu->arch.gpr[rt] = vcpu_e500->pid[0]; break;
	case SPRN_PID1:
		vcpu->arch.gpr[rt] = vcpu_e500->pid[1]; break;
	case SPRN_PID2:
		vcpu->arch.gpr[rt] = vcpu_e500->pid[2]; break;
	case SPRN_MAS0:
		vcpu->arch.gpr[rt] = vcpu_e500->mas0; break;
	case SPRN_MAS1:
		vcpu->arch.gpr[rt] = vcpu_e500->mas1; break;
	case SPRN_MAS2:
		vcpu->arch.gpr[rt] = vcpu_e500->mas2; break;
	case SPRN_MAS3:
		vcpu->arch.gpr[rt] = vcpu_e500->mas3; break;
	case SPRN_MAS4:
		vcpu->arch.gpr[rt] = vcpu_e500->mas4; break;
	case SPRN_MAS6:
		vcpu->arch.gpr[rt] = vcpu_e500->mas6; break;
	case SPRN_MAS7:
		vcpu->arch.gpr[rt] = vcpu_e500->mas7; break;

	case SPRN_TLB0CFG:
		vcpu->arch.gpr[rt] = mfspr(SPRN_TLB0CFG);
		vcpu->arch.gpr[rt] &= ~0xfffUL;
		vcpu->arch.gpr[rt] |= vcpu_e500->guest_tlb_size[0];
		break;

	case SPRN_TLB1CFG:
		vcpu->arch.gpr[rt] = mfspr(SPRN_TLB1CFG);
		vcpu->arch.gpr[rt] &= ~0xfffUL;
		vcpu->arch.gpr[rt] |= vcpu_e500->guest_tlb_size[1];
		break;

	case SPRN_L1CSR1:
		vcpu->arch.gpr[rt] = vcpu_e500->l1csr1; break;
	case SPRN_HID0:
		vcpu->arch.gpr[rt] = vcpu_e500->hid0; break;
	case SPRN_HID1:
		vcpu->arch.gpr[rt] = vcpu_e500->hid1; break;

	default:
		emulated = kvmppc_booke_emulate_mfspr(vcpu, sprn, rt);
	}

	return emulated;
}
Loading