RE: [PATCH 07/11] E500 TLB emulation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: kvm-ppc-owner@xxxxxxxxxxxxxxx 
> [mailto:kvm-ppc-owner@xxxxxxxxxxxxxxx] On Behalf Of Hollis Blanchard
> Sent: Tuesday, December 16, 2008 9:34 AM
> To: Liu Yu-B13201
> Cc: kvm-ppc@xxxxxxxxxxxxxxx
> Subject: Re: [PATCH 07/11] E500 TLB emulation
> 
> On Thu, 2008-12-11 at 17:11 +0800, Liu Yu wrote:
> > Signed-off-by: Liu Yu <yu.liu@xxxxxxxxxxxxx>
> > ---
> >  arch/powerpc/kvm/e500_tlb.c |  748 
> +++++++++++++++++++++++++++++++++++++++++++
> >  arch/powerpc/kvm/e500_tlb.h |  177 ++++++++++
> >  2 files changed, 925 insertions(+), 0 deletions(-)
> >  create mode 100644 arch/powerpc/kvm/e500_tlb.c
> >  create mode 100644 arch/powerpc/kvm/e500_tlb.h
> > 
> > diff --git a/arch/powerpc/kvm/e500_tlb.c 
> b/arch/powerpc/kvm/e500_tlb.c
> > new file mode 100644
> > index 0000000..a5858e6
> > --- /dev/null
> > +++ b/arch/powerpc/kvm/e500_tlb.c
> > @@ -0,0 +1,748 @@
> > +/*
> > + * Copyright (C) 2008 Freescale Semiconductor, Inc. All 
> rights reserved.
> > + *
> > + * Author: Yu Liu, yu.liu@xxxxxxxxxxxxx, Aug, 2008
> > + *
> > + * Description:
> > + * This file is based on arch/powerpc/kvm/44x_tlb.c,
> > + * by Hollis Blanchard <hollisb@xxxxxxxxxx>.
> > + *
> > + * 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/types.h>
> > +#include <linux/string.h>
> > +#include <linux/kvm.h>
> > +#include <linux/kvm_host.h>
> > +#include <linux/highmem.h>
> > +#include <asm/kvm_ppc.h>
> > +#include <asm/kvm_e500.h>
> > +
> > +#include "e500_tlb.h"
> > +
> > +#define to_htlb1_esel(esel) (tlb1_entry_num - (esel) - 1)
> > +
> > +static unsigned int tlb1_entry_num;
> > +
> > +void kvmppc_dump_tlbs(struct kvm_vcpu *vcpu)
> > +{
> > +	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
> > +	struct tlbe *tlbe;
> > +	int i, tlbsel;
> > +
> > +	printk("| %8s | %8s | %8s | %8s | %8s |\n",
> > +			"nr", "mas1", "mas2", "mas3", "mas7");
> > +
> > +	for (tlbsel = 0; tlbsel < 2; tlbsel++) {
> > +		printk("Guest TLB%d:\n", tlbsel);
> > +		for (i = 0; i < 
> vcpu_e500->guest_tlb_size[tlbsel]; i++) {
> > +			tlbe = &vcpu_e500->guest_tlb[tlbsel][i];
> > +			if (tlbe->mas1 & MAS1_VALID)
> > +				printk(" G[%d][%3d] |  %08X | 
> %08X | %08X | %08X |\n",
> > +					tlbsel, i, tlbe->mas1, 
> tlbe->mas2,
> > +					tlbe->mas3, tlbe->mas7);
> > +		}
> > +	}
> > +
> > +	for (tlbsel = 0; tlbsel < 2; tlbsel++) {
> > +		printk("Shadow TLB%d:\n", tlbsel);
> > +		for (i = 0; i < 
> vcpu_e500->shadow_tlb_size[tlbsel]; i++) {
> > +			tlbe = &vcpu_e500->shadow_tlb[tlbsel][i];
> > +			if (tlbe->mas1 & MAS1_VALID)
> > +				printk(" S[%d][%3d] |  %08X | 
> %08X | %08X | %08X |\n",
> > +					tlbsel, i, tlbe->mas1, 
> tlbe->mas2,
> > +					tlbe->mas3, tlbe->mas7);
> > +		}
> > +	}
> > +}
> > +
> > +static inline unsigned int tlb0_get_next_victim(
> > +		struct kvmppc_vcpu_e500 *vcpu_e500)
> > +{
> > +	unsigned int victim;
> > +
> > +	victim = vcpu_e500->guest_tlb_nv[0]++;
> > +	if (unlikely(vcpu_e500->guest_tlb_nv[0] >= 
> KVM_E500_TLB0_WAY_NUM))
> > +		vcpu_e500->guest_tlb_nv[0] = 0;
> > +
> > +	return victim;
> > +}
> > +
> > +static inline unsigned int tlb1_max_shadow_size(void)
> > +{
> > +	return tlb1_entry_num - tlbcam_index;
> > +}
> > +
> > +static inline u32 e500_shadow_mas3_attrib(u32 mas3, int usermode)
> > +{
> > +	/* Mask off reserved bits. */
> > +	mas3 &= MAS3_ATTRIB_MASK;
> > +
> > +	if (!usermode) {
> > +		/* Guest is in supervisor mode,
> > +		 * so we need to translate guest
> > +		 * supervisor permissions into user permissions. */
> > +		mas3 &= ~E500_TLB_USER_PERM_MASK;
> > +		mas3 |= (mas3 & E500_TLB_SUPER_PERM_MASK) << 1;
> > +	}
> > +
> > +	return mas3 | E500_TLB_SUPER_PERM_MASK;
> > +}
> > +
> > +static inline u32 e500_shadow_mas2_attrib(u32 mas2, int usermode)
> > +{
> > +	return mas2 & MAS2_ATTRIB_MASK;
> > +}
> > +
> > +/*
> > + * writing shadow tlb entry to host TLB
> > + */
> > +static inline void __write_host_tlbe(struct tlbe *stlbe)
> > +{
> > +	mtspr(SPRN_MAS1, stlbe->mas1);
> > +	mtspr(SPRN_MAS2, stlbe->mas2);
> > +	mtspr(SPRN_MAS3, stlbe->mas3);
> > +	mtspr(SPRN_MAS7, stlbe->mas7);
> > +	__asm__ __volatile__ ("tlbwe\n" : : );
> > +}
> > +
> > +static inline void write_host_tlbe(struct kvmppc_vcpu_e500 
> *vcpu_e500,
> > +		int tlbsel, int esel)
> > +{
> > +	struct tlbe *stlbe = &vcpu_e500->shadow_tlb[tlbsel][esel];
> > +
> > +	local_irq_disable();
> > +	if (tlbsel == 0) {
> > +		__write_host_tlbe(stlbe);
> > +	} else {
> > +		unsigned register mas0;
> > +
> > +		mas0 = mfspr(SPRN_MAS0);
> > +
> > +		mtspr(SPRN_MAS0, MAS0_TLBSEL(1) | 
> MAS0_ESEL(to_htlb1_esel(esel)));
> > +		__write_host_tlbe(stlbe);
> > +
> > +		mtspr(SPRN_MAS0, mas0);
> > +	}
> > +	local_irq_enable();
> > +}
> > +
> > +void kvmppc_e500_tlb_load(struct kvm_vcpu *vcpu, int cpu)
> > +{
> > +	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
> > +	int i;
> > +	unsigned register mas0;
> > +	/* Mark every guest entry in the shadow TLB entry 
> modified, so that they
> > +	* will all be reloaded on the next vcpu run (instead of being
> > +	* demand-faulted). */
> 
> This comment does not apply to this code.
Fixed

> 
> > +	local_irq_disable();
> > +	mas0 = mfspr(SPRN_MAS0);
> > +	for (i = 0; i < vcpu_e500->shadow_tlb_size[1]; i++) {
> > +		struct tlbe *stlbe = &vcpu_e500->shadow_tlb[1][i];
> > +
> > +		if (get_tlb_v(stlbe)) {
> > +			mtspr(SPRN_MAS0, MAS0_TLBSEL(1)
> > +					| MAS0_ESEL(to_htlb1_esel(i)));
> > +			__write_host_tlbe(stlbe);
> > +		}
> > +	}
> > +	mtspr(SPRN_MAS0, mas0);
> > +	local_irq_enable();
> > +}
> 
> ...
> 
> > +
> > +/* Must be called with mmap_sem locked for writing. */
> 
> This comment is not correct.
Fixed.

> 
> > +static void e500_shadow_release(struct kvmppc_vcpu_e500 *vcpu_e500,
> > +		int tlbsel, int esel)
> > +{
> > +	struct tlbe *stlbe = &vcpu_e500->shadow_tlb[tlbsel][esel];
> > +	struct page *page = vcpu_e500->shadow_pages[tlbsel][esel];
> > +
> > +	if (page) {
> > +		vcpu_e500->shadow_pages[tlbsel][esel] = NULL;
> > +
> > +		if (get_tlb_v(stlbe)) {
> > +			if (tlbe_is_writable(stlbe))
> > +				kvm_release_page_dirty(page);
> > +			else
> > +				kvm_release_page_clean(page);
> > +		}
> > +	}
> > +}
> > +
> > +/* Must be called with mmap_sem locked for writing. */
> 
> This comment is not correct.
Fixed.

> 
> > +static void e500_tlbe_invalidate(struct kvmppc_vcpu_e500 
> *vcpu_e500,
> > +		int tlbsel, int esel)
> > +{
> > +	struct tlbe *stlbe = &vcpu_e500->shadow_tlb[tlbsel][esel];
> > +
> > +	e500_shadow_release(vcpu_e500, tlbsel, esel);
> > +	stlbe->mas1 = 0;
> > +	KVMTRACE_5D(STLB_INVAL, &vcpu_e500->vcpu, 
> index_of(tlbsel, esel),
> > +			stlbe->mas1, stlbe->mas2, stlbe->mas3, 
> stlbe->mas7,
> > +			handler);
> > +}
> 
> > +
> > +/* Must be called with mmap_sem locked for writing. */
> 
> Comment is not correct.
Fixed.

> 
> > +static int e500_guest_tlbe_invalidate(struct 
> kvmppc_vcpu_e500 *vcpu_e500,
> > +		int tlbsel, int esel)
> > +{
> > +	struct tlbe *gtlbe = &vcpu_e500->guest_tlb[tlbsel][esel];
> > +
> > +	if (unlikely(get_tlb_iprot(gtlbe)))
> > +		return -1;
> > +
> > +	if (tlbsel == 1) {
> > +		e500_mmu_invalidate(vcpu_e500, get_tlb_eaddr(gtlbe),
> > +				get_tlb_end(gtlbe),
> > +				get_tlb_tid(gtlbe));
> > +	} else {
> > +		e500_tlbe_invalidate(vcpu_e500, tlbsel, esel);
> > +	}
> > +
> > +	gtlbe->mas1 = 0;
> > +
> > +	return 0;
> > +}
> > +int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb)
> > +{
> 
> ^^^ Please add a newline before this function name.
Fixed.

> 
> > +	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
> > +	unsigned int ia;
> > +	int esel, tlbsel;
> > +	gva_t ea;
> > +
> > +	ea = ((ra) ? vcpu->arch.gpr[ra] : 0) + vcpu->arch.gpr[rb];
> > +
> > +	ia = (ea >> 2) & 0x1;
> > +	tlbsel = (ea >> 3) & 0x3;
> 
> > +	if (ia) {
> > +		/* invalidate all entries */
> > +		for (esel = 0; esel < 
> vcpu_e500->guest_tlb_size[tlbsel]; esel++)
> > +			e500_guest_tlbe_invalidate(vcpu_e500, 
> tlbsel, esel);
> 
> This is actually a major issue, and it affects many places 
> where you use
> tlbsel.
> 
> To get tlbsel you mask various things with 0x3, but you've only
> allocated 2 guest TLBs! So if the guest sets MAS0[TLBSEL] = 2 or 3, or
> tlbivax with the right bits set, you will dereference beyond 
> the bounds
> of the guest_tlb* vcpu arrays.
> 
Hmm, good catch. Thanks.

> To fix this, instead of doing error-checking in all these 
> sites, I would
> just mask with 1 instead of 3.
> 

If the guest instruction appoint MAS0[TLBSEL] = 2 or 3,
I think the best way is to ignore it.
--
To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM Development]     [KVM ARM]     [KVM ia64]     [Linux Virtualization]     [Linux USB Devel]     [Linux Video]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux