⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tlb.c

📁 linux 内核源代码
💻 C
字号:
/* * AVR32 TLB operations * * Copyright (C) 2004-2006 Atmel Corporation * * 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/mm.h>#include <asm/mmu_context.h>#define _TLBEHI_I	0x100void show_dtlb_entry(unsigned int index){	unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;	unsigned long flags;	local_irq_save(flags);	mmucr_save = sysreg_read(MMUCR);	tlbehi_save = sysreg_read(TLBEHI);	mmucr = mmucr_save & 0x13;	mmucr |= index << 14;	sysreg_write(MMUCR, mmucr);	asm volatile("tlbr" : : : "memory");	cpu_sync_pipeline();	tlbehi = sysreg_read(TLBEHI);	tlbelo = sysreg_read(TLBELO);	printk("%2u: %c %c %02x   %05x %05x %o  %o  %c %c %c %c\n",	       index,	       (tlbehi & 0x200)?'1':'0',	       (tlbelo & 0x100)?'1':'0',	       (tlbehi & 0xff),	       (tlbehi >> 12), (tlbelo >> 12),	       (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,	       (tlbelo & 0x200)?'1':'0',	       (tlbelo & 0x080)?'1':'0',	       (tlbelo & 0x001)?'1':'0',	       (tlbelo & 0x002)?'1':'0');	sysreg_write(MMUCR, mmucr_save);	sysreg_write(TLBEHI, tlbehi_save);	cpu_sync_pipeline();	local_irq_restore(flags);}void dump_dtlb(void){	unsigned int i;	printk("ID  V G ASID VPN   PFN   AP SZ C B W D\n");	for (i = 0; i < 32; i++)		show_dtlb_entry(i);}static unsigned long last_mmucr;static inline void set_replacement_pointer(unsigned shift){	unsigned long mmucr, mmucr_save;	mmucr = mmucr_save = sysreg_read(MMUCR);	/* Does this mapping already exist? */	__asm__ __volatile__(		"	tlbs\n"		"	mfsr %0, %1"		: "=r"(mmucr)		: "i"(SYSREG_MMUCR));	if (mmucr & SYSREG_BIT(MMUCR_N)) {		/* Not found -- pick a not-recently-accessed entry */		unsigned long rp;		unsigned long tlbar = sysreg_read(TLBARLO);		rp = 32 - fls(tlbar);		if (rp == 32) {			rp = 0;			sysreg_write(TLBARLO, -1L);		}		mmucr &= 0x13;		mmucr |= (rp << shift);		sysreg_write(MMUCR, mmucr);	}	last_mmucr = mmucr;}static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid){	unsigned long vpn;	vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid;	sysreg_write(TLBEHI, vpn);	cpu_sync_pipeline();	set_replacement_pointer(14);	sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);	/* Let's go */	asm volatile("nop\n\ttlbw" : : : "memory");	cpu_sync_pipeline();}void update_mmu_cache(struct vm_area_struct *vma,		      unsigned long address, pte_t pte){	unsigned long flags;	/* ptrace may call this routine */	if (vma && current->active_mm != vma->vm_mm)		return;	local_irq_save(flags);	update_dtlb(address, pte, get_asid());	local_irq_restore(flags);}void __flush_tlb_page(unsigned long asid, unsigned long page){	unsigned long mmucr, tlbehi;	page |= asid;	sysreg_write(TLBEHI, page);	cpu_sync_pipeline();	asm volatile("tlbs");	mmucr = sysreg_read(MMUCR);	if (!(mmucr & SYSREG_BIT(MMUCR_N))) {		unsigned long tlbarlo;		unsigned long entry;		/* Clear the "valid" bit */		tlbehi = sysreg_read(TLBEHI);		tlbehi &= ~_TLBEHI_VALID;		sysreg_write(TLBEHI, tlbehi);		cpu_sync_pipeline();		/* mark the entry as "not accessed" */		entry = (mmucr >> 14) & 0x3f;		tlbarlo = sysreg_read(TLBARLO);		tlbarlo |= (0x80000000 >> entry);		sysreg_write(TLBARLO, tlbarlo);		/* update the entry with valid bit clear */		asm volatile("tlbw");		cpu_sync_pipeline();	}}void flush_tlb_page(struct vm_area_struct *vma, unsigned long page){	if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {		unsigned long flags, asid;		unsigned long saved_asid = MMU_NO_ASID;		asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;		page &= PAGE_MASK;		local_irq_save(flags);		if (vma->vm_mm != current->mm) {			saved_asid = get_asid();			set_asid(asid);		}		__flush_tlb_page(asid, page);		if (saved_asid != MMU_NO_ASID)			set_asid(saved_asid);		local_irq_restore(flags);	}}void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,		     unsigned long end){	struct mm_struct *mm = vma->vm_mm;	if (mm->context != NO_CONTEXT) {		unsigned long flags;		int size;		local_irq_save(flags);		size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;		if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */			mm->context = NO_CONTEXT;			if (mm == current->mm)				activate_context(mm);		} else {			unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK;			unsigned long saved_asid = MMU_NO_ASID;			start &= PAGE_MASK;			end += (PAGE_SIZE - 1);			end &= PAGE_MASK;			if (mm != current->mm) {				saved_asid = get_asid();				set_asid(asid);			}			while (start < end) {				__flush_tlb_page(asid, start);				start += PAGE_SIZE;			}			if (saved_asid != MMU_NO_ASID)				set_asid(saved_asid);		}		local_irq_restore(flags);	}}/* * TODO: If this is only called for addresses > TASK_SIZE, we can probably * skip the ASID stuff and just use the Global bit... */void flush_tlb_kernel_range(unsigned long start, unsigned long end){	unsigned long flags;	int size;	local_irq_save(flags);	size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;	if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */		flush_tlb_all();	} else {		unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK;		unsigned long saved_asid = get_asid();		start &= PAGE_MASK;		end += (PAGE_SIZE - 1);		end &= PAGE_MASK;		set_asid(asid);		while (start < end) {			__flush_tlb_page(asid, start);			start += PAGE_SIZE;		}		set_asid(saved_asid);	}	local_irq_restore(flags);}void flush_tlb_mm(struct mm_struct *mm){	/* Invalidate all TLB entries of this process by getting a new ASID */	if (mm->context != NO_CONTEXT) {		unsigned long flags;		local_irq_save(flags);		mm->context = NO_CONTEXT;		if (mm == current->mm)			activate_context(mm);		local_irq_restore(flags);	}}void flush_tlb_all(void){	unsigned long flags;	local_irq_save(flags);	sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));	local_irq_restore(flags);}#ifdef CONFIG_PROC_FS#include <linux/seq_file.h>#include <linux/proc_fs.h>#include <linux/init.h>static void *tlb_start(struct seq_file *tlb, loff_t *pos){	static unsigned long tlb_index;	if (*pos >= 32)		return NULL;	tlb_index = 0;	return &tlb_index;}static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos){	unsigned long *index = v;	if (*index >= 31)		return NULL;	++*pos;	++*index;	return index;}static void tlb_stop(struct seq_file *tlb, void *v){}static int tlb_show(struct seq_file *tlb, void *v){	unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;	unsigned long flags;	unsigned long *index = v;	if (*index == 0)		seq_puts(tlb, "ID  V G ASID VPN   PFN   AP SZ C B W D\n");	BUG_ON(*index >= 32);	local_irq_save(flags);	mmucr_save = sysreg_read(MMUCR);	tlbehi_save = sysreg_read(TLBEHI);	mmucr = mmucr_save & 0x13;	mmucr |= *index << 14;	sysreg_write(MMUCR, mmucr);	asm volatile("tlbr" : : : "memory");	cpu_sync_pipeline();	tlbehi = sysreg_read(TLBEHI);	tlbelo = sysreg_read(TLBELO);	sysreg_write(MMUCR, mmucr_save);	sysreg_write(TLBEHI, tlbehi_save);	cpu_sync_pipeline();	local_irq_restore(flags);	seq_printf(tlb, "%2lu: %c %c %02x   %05x %05x %o  %o  %c %c %c %c\n",	       *index,	       (tlbehi & 0x200)?'1':'0',	       (tlbelo & 0x100)?'1':'0',	       (tlbehi & 0xff),	       (tlbehi >> 12), (tlbelo >> 12),	       (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,	       (tlbelo & 0x200)?'1':'0',	       (tlbelo & 0x080)?'1':'0',	       (tlbelo & 0x001)?'1':'0',	       (tlbelo & 0x002)?'1':'0');	return 0;}static struct seq_operations tlb_ops = {	.start		= tlb_start,	.next		= tlb_next,	.stop		= tlb_stop,	.show		= tlb_show,};static int tlb_open(struct inode *inode, struct file *file){	return seq_open(file, &tlb_ops);}static const struct file_operations proc_tlb_operations = {	.open		= tlb_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= seq_release,};static int __init proctlb_init(void){	struct proc_dir_entry *entry;	entry = create_proc_entry("tlb", 0, NULL);	if (entry)		entry->proc_fops = &proc_tlb_operations;	return 0;}late_initcall(proctlb_init);#endif /* CONFIG_PROC_FS */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -