forked from mirrors/linux
		
	[PATCH] xtensa: Architecture support for Tensilica Xtensa Part 5
The attached patches provides part 5 of an architecture implementation for the Tensilica Xtensa CPU series. Signed-off-by: Chris Zankel <chris@zankel.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
		
							parent
							
								
									249ac17e96
								
							
						
					
					
						commit
						3f65ce4d14
					
				
					 6 changed files with 1800 additions and 0 deletions
				
			
		
							
								
								
									
										13
									
								
								arch/xtensa/mm/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								arch/xtensa/mm/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Makefile for the Linux/Xtensa-specific parts of the memory manager.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Note! Dependencies are done automagically by 'make dep', which also
 | 
				
			||||||
 | 
					# removes any old dependencies. DON'T put your own dependencies here
 | 
				
			||||||
 | 
					# unless it's something special (ie not a .c file).
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Note 2! The CFLAGS definition is now in the main makefile...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					obj-y	 := init.o fault.o tlb.o misc.o
 | 
				
			||||||
 | 
					obj-m	 :=
 | 
				
			||||||
 | 
					obj-n	 :=
 | 
				
			||||||
 | 
					obj-	 :=
 | 
				
			||||||
							
								
								
									
										241
									
								
								arch/xtensa/mm/fault.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								arch/xtensa/mm/fault.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,241 @@
 | 
				
			||||||
 | 
					// TODO VM_EXEC flag work-around, cache aliasing
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * arch/xtensa/mm/fault.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is subject to the terms and conditions of the GNU General Public
 | 
				
			||||||
 | 
					 * License.  See the file "COPYING" in the main directory of this archive
 | 
				
			||||||
 | 
					 * for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2001 - 2005 Tensilica Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Chris Zankel <chris@zankel.net>
 | 
				
			||||||
 | 
					 * Joe Taylor	<joe@tensilica.com, joetylr@yahoo.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/mm.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <asm/mmu_context.h>
 | 
				
			||||||
 | 
					#include <asm/cacheflush.h>
 | 
				
			||||||
 | 
					#include <asm/hardirq.h>
 | 
				
			||||||
 | 
					#include <asm/uaccess.h>
 | 
				
			||||||
 | 
					#include <asm/system.h>
 | 
				
			||||||
 | 
					#include <asm/pgalloc.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned long asid_cache = ASID_FIRST_VERSION;
 | 
				
			||||||
 | 
					void bad_page_fault(struct pt_regs*, unsigned long, int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This routine handles page faults.  It determines the address,
 | 
				
			||||||
 | 
					 * and the problem, and then passes it off to one of the appropriate
 | 
				
			||||||
 | 
					 * routines.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: does not handle Miss and MultiHit.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void do_page_fault(struct pt_regs *regs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct vm_area_struct * vma;
 | 
				
			||||||
 | 
						struct mm_struct *mm = current->mm;
 | 
				
			||||||
 | 
						unsigned int exccause = regs->exccause;
 | 
				
			||||||
 | 
						unsigned int address = regs->excvaddr;
 | 
				
			||||||
 | 
						siginfo_t info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int is_write, is_exec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						info.si_code = SEGV_MAPERR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We fault-in kernel-space virtual memory on-demand. The
 | 
				
			||||||
 | 
						 * 'reference' page table is init_mm.pgd.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (address >= TASK_SIZE && !user_mode(regs))
 | 
				
			||||||
 | 
							goto vmalloc_fault;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If we're in an interrupt or have no user
 | 
				
			||||||
 | 
						 * context, we must not take the fault..
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (in_atomic() || !mm) {
 | 
				
			||||||
 | 
							bad_page_fault(regs, address, SIGSEGV);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						is_write = (exccause == XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE) ? 1 : 0;
 | 
				
			||||||
 | 
						is_exec =  (exccause == XCHAL_EXCCAUSE_ITLB_PRIVILEGE ||
 | 
				
			||||||
 | 
							    exccause == XCHAL_EXCCAUSE_ITLB_MISS ||
 | 
				
			||||||
 | 
							    exccause == XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE) ? 1 : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						printk("[%s:%d:%08x:%d:%08x:%s%s]\n", current->comm, current->pid,
 | 
				
			||||||
 | 
						       address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						down_read(&mm->mmap_sem);
 | 
				
			||||||
 | 
						vma = find_vma(mm, address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!vma)
 | 
				
			||||||
 | 
							goto bad_area;
 | 
				
			||||||
 | 
						if (vma->vm_start <= address)
 | 
				
			||||||
 | 
							goto good_area;
 | 
				
			||||||
 | 
						if (!(vma->vm_flags & VM_GROWSDOWN))
 | 
				
			||||||
 | 
							goto bad_area;
 | 
				
			||||||
 | 
						if (expand_stack(vma, address))
 | 
				
			||||||
 | 
							goto bad_area;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Ok, we have a good vm_area for this memory access, so
 | 
				
			||||||
 | 
						 * we can handle it..
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					good_area:
 | 
				
			||||||
 | 
						info.si_code = SEGV_ACCERR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_write) {
 | 
				
			||||||
 | 
							if (!(vma->vm_flags & VM_WRITE))
 | 
				
			||||||
 | 
								goto bad_area;
 | 
				
			||||||
 | 
						} else if (is_exec) {
 | 
				
			||||||
 | 
							if (!(vma->vm_flags & VM_EXEC))
 | 
				
			||||||
 | 
								goto bad_area;
 | 
				
			||||||
 | 
						} else	/* Allow read even from write-only pages. */
 | 
				
			||||||
 | 
							if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
 | 
				
			||||||
 | 
								goto bad_area;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If for any reason at all we couldn't handle the fault,
 | 
				
			||||||
 | 
						 * make sure we exit gracefully rather than endlessly redo
 | 
				
			||||||
 | 
						 * the fault.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					survive:
 | 
				
			||||||
 | 
						switch (handle_mm_fault(mm, vma, address, is_write)) {
 | 
				
			||||||
 | 
						case VM_FAULT_MINOR:
 | 
				
			||||||
 | 
							current->min_flt++;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case VM_FAULT_MAJOR:
 | 
				
			||||||
 | 
							current->maj_flt++;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case VM_FAULT_SIGBUS:
 | 
				
			||||||
 | 
							goto do_sigbus;
 | 
				
			||||||
 | 
						case VM_FAULT_OOM:
 | 
				
			||||||
 | 
							goto out_of_memory;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						up_read(&mm->mmap_sem);
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Something tried to access memory that isn't in our memory map..
 | 
				
			||||||
 | 
						 * Fix it, but check if it's kernel or user first..
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					bad_area:
 | 
				
			||||||
 | 
						up_read(&mm->mmap_sem);
 | 
				
			||||||
 | 
						if (user_mode(regs)) {
 | 
				
			||||||
 | 
							current->thread.bad_vaddr = address;
 | 
				
			||||||
 | 
							current->thread.error_code = is_write;
 | 
				
			||||||
 | 
							info.si_signo = SIGSEGV;
 | 
				
			||||||
 | 
							info.si_errno = 0;
 | 
				
			||||||
 | 
							/* info.si_code has been set above */
 | 
				
			||||||
 | 
							info.si_addr = (void *) address;
 | 
				
			||||||
 | 
							force_sig_info(SIGSEGV, &info, current);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bad_page_fault(regs, address, SIGSEGV);
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We ran out of memory, or some other thing happened to us that made
 | 
				
			||||||
 | 
						 * us unable to handle the page fault gracefully.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					out_of_memory:
 | 
				
			||||||
 | 
						up_read(&mm->mmap_sem);
 | 
				
			||||||
 | 
						if (current->pid == 1) {
 | 
				
			||||||
 | 
							yield();
 | 
				
			||||||
 | 
							down_read(&mm->mmap_sem);
 | 
				
			||||||
 | 
							goto survive;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printk("VM: killing process %s\n", current->comm);
 | 
				
			||||||
 | 
						if (user_mode(regs))
 | 
				
			||||||
 | 
							do_exit(SIGKILL);
 | 
				
			||||||
 | 
						bad_page_fault(regs, address, SIGKILL);
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					do_sigbus:
 | 
				
			||||||
 | 
						up_read(&mm->mmap_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Send a sigbus, regardless of whether we were in kernel
 | 
				
			||||||
 | 
						 * or user mode.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						current->thread.bad_vaddr = address;
 | 
				
			||||||
 | 
						info.si_code = SIGBUS;
 | 
				
			||||||
 | 
						info.si_errno = 0;
 | 
				
			||||||
 | 
						info.si_code = BUS_ADRERR;
 | 
				
			||||||
 | 
						info.si_addr = (void *) address;
 | 
				
			||||||
 | 
						force_sig_info(SIGBUS, &info, current);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Kernel mode? Handle exceptions or die */
 | 
				
			||||||
 | 
						if (!user_mode(regs))
 | 
				
			||||||
 | 
							bad_page_fault(regs, address, SIGBUS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vmalloc_fault:
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Synchronize this task's top level page-table
 | 
				
			||||||
 | 
							 * with the 'reference' page table.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							struct mm_struct *act_mm = current->active_mm;
 | 
				
			||||||
 | 
							int index = pgd_index(address);
 | 
				
			||||||
 | 
							pgd_t *pgd, *pgd_k;
 | 
				
			||||||
 | 
							pmd_t *pmd, *pmd_k;
 | 
				
			||||||
 | 
							pte_t *pte_k;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (act_mm == NULL)
 | 
				
			||||||
 | 
								goto bad_page_fault;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pgd = act_mm->pgd + index;
 | 
				
			||||||
 | 
							pgd_k = init_mm.pgd + index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!pgd_present(*pgd_k))
 | 
				
			||||||
 | 
								goto bad_page_fault;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pgd_val(*pgd) = pgd_val(*pgd_k);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pmd = pmd_offset(pgd, address);
 | 
				
			||||||
 | 
							pmd_k = pmd_offset(pgd_k, address);
 | 
				
			||||||
 | 
							if (!pmd_present(*pmd) || !pmd_present(*pmd_k))
 | 
				
			||||||
 | 
								goto bad_page_fault;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pmd_val(*pmd) = pmd_val(*pmd_k);
 | 
				
			||||||
 | 
							pte_k = pte_offset_kernel(pmd_k, address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!pte_present(*pte_k))
 | 
				
			||||||
 | 
								goto bad_page_fault;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					bad_page_fault:
 | 
				
			||||||
 | 
						bad_page_fault(regs, address, SIGKILL);
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						extern void die(const char*, struct pt_regs*, long);
 | 
				
			||||||
 | 
						const struct exception_table_entry *entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Are we prepared to handle this kernel fault?  */
 | 
				
			||||||
 | 
						if ((entry = search_exception_tables(regs->pc)) != NULL) {
 | 
				
			||||||
 | 
					#if 1
 | 
				
			||||||
 | 
							printk(KERN_DEBUG "%s: Exception at pc=%#010lx (%lx)\n",
 | 
				
			||||||
 | 
									current->comm, regs->pc, entry->fixup);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							current->thread.bad_uaddr = address;
 | 
				
			||||||
 | 
							regs->pc = entry->fixup;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Oops. The kernel tried to access some bad page. We'll have to
 | 
				
			||||||
 | 
						 * terminate things with extreme prejudice.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
 | 
				
			||||||
 | 
						       "address %08lx\n pc = %08lx, ra = %08lx\n",
 | 
				
			||||||
 | 
						       address, regs->pc, regs->areg[0]);
 | 
				
			||||||
 | 
						die("Oops", regs, sig);
 | 
				
			||||||
 | 
						do_exit(sig);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										551
									
								
								arch/xtensa/mm/init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										551
									
								
								arch/xtensa/mm/init.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,551 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * arch/xtensa/mm/init.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Derived from MIPS, PPC.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is subject to the terms and conditions of the GNU General Public
 | 
				
			||||||
 | 
					 * License.  See the file "COPYING" in the main directory of this archive
 | 
				
			||||||
 | 
					 * for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2001 - 2005 Tensilica Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Chris Zankel	<chris@zankel.net>
 | 
				
			||||||
 | 
					 * Joe Taylor	<joe@tensilica.com, joetylr@yahoo.com>
 | 
				
			||||||
 | 
					 * Marc Gauthier
 | 
				
			||||||
 | 
					 * Kevin Chea
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/config.h>
 | 
				
			||||||
 | 
					#include <linux/init.h>
 | 
				
			||||||
 | 
					#include <linux/signal.h>
 | 
				
			||||||
 | 
					#include <linux/sched.h>
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/errno.h>
 | 
				
			||||||
 | 
					#include <linux/string.h>
 | 
				
			||||||
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					#include <linux/ptrace.h>
 | 
				
			||||||
 | 
					#include <linux/bootmem.h>
 | 
				
			||||||
 | 
					#include <linux/swap.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/pgtable.h>
 | 
				
			||||||
 | 
					#include <asm/bootparam.h>
 | 
				
			||||||
 | 
					#include <asm/mmu_context.h>
 | 
				
			||||||
 | 
					#include <asm/tlb.h>
 | 
				
			||||||
 | 
					#include <asm/tlbflush.h>
 | 
				
			||||||
 | 
					#include <asm/page.h>
 | 
				
			||||||
 | 
					#include <asm/pgalloc.h>
 | 
				
			||||||
 | 
					#include <asm/pgtable.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEBUG 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 | 
				
			||||||
 | 
					//static DEFINE_SPINLOCK(tlb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This flag is used to indicate that the page was mapped and modified in
 | 
				
			||||||
 | 
					 * kernel space, so the cache is probably dirty at that address.
 | 
				
			||||||
 | 
					 * If cache aliasing is enabled and the page color mismatches, update_mmu_cache
 | 
				
			||||||
 | 
					 * synchronizes the caches if this bit is set.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PG_cache_clean PG_arch_1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* References to section boundaries */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern char _ftext, _etext, _fdata, _edata, _rodata_end;
 | 
				
			||||||
 | 
					extern char __init_begin, __init_end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * mem_reserve(start, end, must_exist)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Reserve some memory from the memory pool.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Parameters:
 | 
				
			||||||
 | 
					 *  start	Start of region,
 | 
				
			||||||
 | 
					 *  end		End of region,
 | 
				
			||||||
 | 
					 *  must_exist	Must exist in memory pool.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns:
 | 
				
			||||||
 | 
					 *  0 (memory area couldn't be mapped)
 | 
				
			||||||
 | 
					 * -1 (success)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int __init mem_reserve(unsigned long start, unsigned long end, int must_exist)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (start == end)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start = start & PAGE_MASK;
 | 
				
			||||||
 | 
						end = PAGE_ALIGN(end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < sysmem.nr_banks; i++)
 | 
				
			||||||
 | 
							if (start < sysmem.bank[i].end
 | 
				
			||||||
 | 
							    && end >= sysmem.bank[i].start)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (i == sysmem.nr_banks) {
 | 
				
			||||||
 | 
							if (must_exist)
 | 
				
			||||||
 | 
								printk (KERN_WARNING "mem_reserve: [0x%0lx, 0x%0lx) "
 | 
				
			||||||
 | 
									"not in any region!\n", start, end);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (start > sysmem.bank[i].start) {
 | 
				
			||||||
 | 
							if (end < sysmem.bank[i].end) {
 | 
				
			||||||
 | 
								/* split entry */
 | 
				
			||||||
 | 
								if (sysmem.nr_banks >= SYSMEM_BANKS_MAX)
 | 
				
			||||||
 | 
									panic("meminfo overflow\n");
 | 
				
			||||||
 | 
								sysmem.bank[sysmem.nr_banks].start = end;
 | 
				
			||||||
 | 
								sysmem.bank[sysmem.nr_banks].end = sysmem.bank[i].end;
 | 
				
			||||||
 | 
								sysmem.nr_banks++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sysmem.bank[i].end = start;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (end < sysmem.bank[i].end)
 | 
				
			||||||
 | 
								sysmem.bank[i].start = end;
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								/* remove entry */
 | 
				
			||||||
 | 
								sysmem.nr_banks--;
 | 
				
			||||||
 | 
								sysmem.bank[i].start = sysmem.bank[sysmem.nr_banks].start;
 | 
				
			||||||
 | 
								sysmem.bank[i].end   = sysmem.bank[sysmem.nr_banks].end;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Initialize the bootmem system and give it all the memory we have available.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __init bootmem_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long pfn;
 | 
				
			||||||
 | 
						unsigned long bootmap_start, bootmap_size;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						max_low_pfn = max_pfn = 0;
 | 
				
			||||||
 | 
						min_low_pfn = ~0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i=0; i < sysmem.nr_banks; i++) {
 | 
				
			||||||
 | 
							pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT;
 | 
				
			||||||
 | 
							if (pfn < min_low_pfn)
 | 
				
			||||||
 | 
								min_low_pfn = pfn;
 | 
				
			||||||
 | 
							pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT;
 | 
				
			||||||
 | 
							if (pfn > max_pfn)
 | 
				
			||||||
 | 
								max_pfn = pfn;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (min_low_pfn > max_pfn)
 | 
				
			||||||
 | 
							panic("No memory found!\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						max_low_pfn = max_pfn < MAX_LOW_MEMORY >> PAGE_SHIFT ?
 | 
				
			||||||
 | 
							max_pfn : MAX_LOW_MEMORY >> PAGE_SHIFT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find an area to use for the bootmem bitmap. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bootmap_size = bootmem_bootmap_pages(max_low_pfn) << PAGE_SHIFT;
 | 
				
			||||||
 | 
						bootmap_start = ~0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i=0; i<sysmem.nr_banks; i++)
 | 
				
			||||||
 | 
							if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) {
 | 
				
			||||||
 | 
								bootmap_start = sysmem.bank[i].start;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bootmap_start == ~0UL)
 | 
				
			||||||
 | 
							panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Reserve the bootmem bitmap area */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1);
 | 
				
			||||||
 | 
						bootmap_size = init_bootmem_node(NODE_DATA(0), min_low_pfn,
 | 
				
			||||||
 | 
										 bootmap_start >> PAGE_SHIFT,
 | 
				
			||||||
 | 
										 max_low_pfn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Add all remaining memory pieces into the bootmem map */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i=0; i<sysmem.nr_banks; i++)
 | 
				
			||||||
 | 
							free_bootmem(sysmem.bank[i].start,
 | 
				
			||||||
 | 
								     sysmem.bank[i].end - sysmem.bank[i].start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __init paging_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long zones_size[MAX_NR_ZONES];
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* All pages are DMA-able, so we put them all in the DMA zone. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						zones_size[ZONE_DMA] = max_low_pfn;
 | 
				
			||||||
 | 
						for (i = 1; i < MAX_NR_ZONES; i++)
 | 
				
			||||||
 | 
							zones_size[i] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_HIGHMEM
 | 
				
			||||||
 | 
						zones_size[ZONE_HIGHMEM] = max_pfn - max_low_pfn;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Initialize the kernel's page tables. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(swapper_pg_dir, 0, PAGE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free_area_init(zones_size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Flush the mmu and reset associated register to default values.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __init init_mmu (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Writing zeros to the <t>TLBCFG special registers ensure
 | 
				
			||||||
 | 
						 * that valid values exist in the register.  For existing
 | 
				
			||||||
 | 
						 * PGSZID<w> fields, zero selects the first element of the
 | 
				
			||||||
 | 
						 * page-size array.  For nonexistant PGSZID<w> fields, zero is
 | 
				
			||||||
 | 
						 * the best value to write.  Also, when changing PGSZID<w>
 | 
				
			||||||
 | 
						 * fields, the corresponding TLB must be flushed.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						set_itlbcfg_register (0);
 | 
				
			||||||
 | 
						set_dtlbcfg_register (0);
 | 
				
			||||||
 | 
						flush_tlb_all ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set rasid register to a known value. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_rasid_register (ASID_ALL_RESERVED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set PTEVADDR special register to the start of the page
 | 
				
			||||||
 | 
						 * table, which is in kernel mappable space (ie. not
 | 
				
			||||||
 | 
						 * statically mapped).  This register's value is undefined on
 | 
				
			||||||
 | 
						 * reset.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						set_ptevaddr_register (PGTABLE_START);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Initialize memory pages.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __init mem_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long codesize, reservedpages, datasize, initsize;
 | 
				
			||||||
 | 
						unsigned long highmemsize, tmp, ram;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						max_mapnr = num_physpages = max_low_pfn;
 | 
				
			||||||
 | 
						high_memory = (void *) __va(max_mapnr << PAGE_SHIFT);
 | 
				
			||||||
 | 
						highmemsize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if CONFIG_HIGHMEM
 | 
				
			||||||
 | 
					#error HIGHGMEM not implemented in init.c
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						totalram_pages += free_all_bootmem();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reservedpages = ram = 0;
 | 
				
			||||||
 | 
						for (tmp = 0; tmp < max_low_pfn; tmp++) {
 | 
				
			||||||
 | 
							ram++;
 | 
				
			||||||
 | 
							if (PageReserved(mem_map+tmp))
 | 
				
			||||||
 | 
								reservedpages++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						codesize =  (unsigned long) &_etext - (unsigned long) &_ftext;
 | 
				
			||||||
 | 
						datasize =  (unsigned long) &_edata - (unsigned long) &_fdata;
 | 
				
			||||||
 | 
						initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, "
 | 
				
			||||||
 | 
						       "%ldk data, %ldk init %ldk highmem)\n",
 | 
				
			||||||
 | 
						       (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
 | 
				
			||||||
 | 
						       ram << (PAGE_SHIFT-10),
 | 
				
			||||||
 | 
						       codesize >> 10,
 | 
				
			||||||
 | 
						       reservedpages << (PAGE_SHIFT-10),
 | 
				
			||||||
 | 
						       datasize >> 10,
 | 
				
			||||||
 | 
						       initsize >> 10,
 | 
				
			||||||
 | 
						       highmemsize >> 10);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					free_reserved_mem(void *start, void *end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (; start < end; start += PAGE_SIZE) {
 | 
				
			||||||
 | 
							ClearPageReserved(virt_to_page(start));
 | 
				
			||||||
 | 
							set_page_count(virt_to_page(start), 1);
 | 
				
			||||||
 | 
							free_page((unsigned long)start);
 | 
				
			||||||
 | 
							totalram_pages++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_BLK_DEV_INITRD
 | 
				
			||||||
 | 
					extern int initrd_is_mapped;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void free_initrd_mem(unsigned long start, unsigned long end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (initrd_is_mapped) {
 | 
				
			||||||
 | 
							free_reserved_mem((void*)start, (void*)end);
 | 
				
			||||||
 | 
							printk ("Freeing initrd memory: %ldk freed\n",(end-start)>>10);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void free_initmem(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						free_reserved_mem(&__init_begin, &__init_end);
 | 
				
			||||||
 | 
						printk("Freeing unused kernel memory: %dk freed\n",
 | 
				
			||||||
 | 
						       (&__init_end - &__init_begin) >> 10);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void show_mem(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, free = 0, total = 0, reserved = 0;
 | 
				
			||||||
 | 
						int shared = 0, cached = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printk("Mem-info:\n");
 | 
				
			||||||
 | 
						show_free_areas();
 | 
				
			||||||
 | 
						printk("Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
 | 
				
			||||||
 | 
						i = max_mapnr;
 | 
				
			||||||
 | 
						while (i-- > 0) {
 | 
				
			||||||
 | 
							total++;
 | 
				
			||||||
 | 
							if (PageReserved(mem_map+i))
 | 
				
			||||||
 | 
								reserved++;
 | 
				
			||||||
 | 
							else if (PageSwapCache(mem_map+i))
 | 
				
			||||||
 | 
								cached++;
 | 
				
			||||||
 | 
							else if (!page_count(mem_map + i))
 | 
				
			||||||
 | 
								free++;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								shared += page_count(mem_map + i) - 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printk("%d pages of RAM\n", total);
 | 
				
			||||||
 | 
						printk("%d reserved pages\n", reserved);
 | 
				
			||||||
 | 
						printk("%d pages shared\n", shared);
 | 
				
			||||||
 | 
						printk("%d pages swap cached\n",cached);
 | 
				
			||||||
 | 
						printk("%d free pages\n", free);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ------------------------------------------------------------------------- */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (DCACHE_WAY_SIZE > PAGE_SIZE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * With cache aliasing, the page color of the page in kernel space and user
 | 
				
			||||||
 | 
					 * space might mismatch. We temporarily map the page to a different virtual
 | 
				
			||||||
 | 
					 * address with the same color and clear the page there.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void clear_user_page(void *kaddr, unsigned long vaddr, struct page* page)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  	/*  There shouldn't be any entries for this page. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__flush_invalidate_dcache_page_phys(__pa(page_address(page)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!PAGE_COLOR_EQ(vaddr, kaddr)) {
 | 
				
			||||||
 | 
							unsigned long v, p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Temporarily map page to DTLB_WAY_DCACHE_ALIAS0. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							spin_lock(&tlb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							p = (unsigned long)pte_val((mk_pte(page,PAGE_KERNEL)));
 | 
				
			||||||
 | 
							kaddr = (void*)PAGE_COLOR_MAP0(vaddr);
 | 
				
			||||||
 | 
							v = (unsigned long)kaddr | DTLB_WAY_DCACHE_ALIAS0;
 | 
				
			||||||
 | 
							__asm__ __volatile__("wdtlb %0,%1; dsync" : :"a" (p), "a" (v));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							clear_page(kaddr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							spin_unlock(&tlb_lock);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							clear_page(kaddr);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We need to make sure that i$ and d$ are coherent. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clear_bit(PG_cache_clean, &page->flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * With cache aliasing, we have to make sure that the page color of the page
 | 
				
			||||||
 | 
					 * in kernel space matches that of the virtual user address before we read
 | 
				
			||||||
 | 
					 * the page. If the page color differ, we create a temporary DTLB entry with
 | 
				
			||||||
 | 
					 * the corrent page color and use this 'temporary' address as the source.
 | 
				
			||||||
 | 
					 * We then use the same approach as in clear_user_page and copy the data
 | 
				
			||||||
 | 
					 * to the kernel space and clear the PG_cache_clean bit to synchronize caches
 | 
				
			||||||
 | 
					 * later.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note:
 | 
				
			||||||
 | 
					 * Instead of using another 'way' for the temporary DTLB entry, we could
 | 
				
			||||||
 | 
					 * probably use the same entry that points to the kernel address (after
 | 
				
			||||||
 | 
					 * saving the original value and restoring it when we are done).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void copy_user_page(void* to, void* from, unsigned long vaddr,
 | 
				
			||||||
 | 
					    		    struct page* to_page)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* There shouldn't be any entries for the new page. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__flush_invalidate_dcache_page_phys(__pa(page_address(to_page)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock(&tlb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!PAGE_COLOR_EQ(vaddr, from)) {
 | 
				
			||||||
 | 
							unsigned long v, p, t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							__asm__ __volatile__ ("pdtlb %1,%2; rdtlb1 %0,%1"
 | 
				
			||||||
 | 
									      : "=a"(p), "=a"(t) : "a"(from));
 | 
				
			||||||
 | 
							from = (void*)PAGE_COLOR_MAP0(vaddr);
 | 
				
			||||||
 | 
							v = (unsigned long)from | DTLB_WAY_DCACHE_ALIAS0;
 | 
				
			||||||
 | 
							__asm__ __volatile__ ("wdtlb %0,%1; dsync" ::"a" (p), "a" (v));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!PAGE_COLOR_EQ(vaddr, to)) {
 | 
				
			||||||
 | 
							unsigned long v, p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							p = (unsigned long)pte_val((mk_pte(to_page,PAGE_KERNEL)));
 | 
				
			||||||
 | 
							to = (void*)PAGE_COLOR_MAP1(vaddr);
 | 
				
			||||||
 | 
							v = (unsigned long)to | DTLB_WAY_DCACHE_ALIAS1;
 | 
				
			||||||
 | 
							__asm__ __volatile__ ("wdtlb %0,%1; dsync" ::"a" (p), "a" (v));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						copy_page(to, from);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_unlock(&tlb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We need to make sure that i$ and d$ are coherent. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clear_bit(PG_cache_clean, &to_page->flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Any time the kernel writes to a user page cache page, or it is about to
 | 
				
			||||||
 | 
					 * read from a page cache page this routine is called.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note:
 | 
				
			||||||
 | 
					 * The kernel currently only provides one architecture bit in the page
 | 
				
			||||||
 | 
					 * flags that we use for I$/D$ coherency. Maybe, in future, we can
 | 
				
			||||||
 | 
					 * use a sepearte bit for deferred dcache aliasing:
 | 
				
			||||||
 | 
					 * If the page is not mapped yet, we only need to set a flag,
 | 
				
			||||||
 | 
					 * if mapped, we need to invalidate the page.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					// FIXME: we probably need this for WB caches not only for Page Coloring..
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flush_dcache_page(struct page *page)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long addr = __pa(page_address(page));
 | 
				
			||||||
 | 
						struct address_space *mapping = page_mapping(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__flush_invalidate_dcache_page_phys(addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!test_bit(PG_cache_clean, &page->flags))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If this page hasn't been mapped, yet, handle I$/D$ coherency later.*/
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						if (mapping && !mapping_mapped(mapping))
 | 
				
			||||||
 | 
							clear_bit(PG_cache_clean, &page->flags);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							__invalidate_icache_page_phys(addr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flush_cache_range(struct vm_area_struct* vma, unsigned long s,
 | 
				
			||||||
 | 
							       unsigned long e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__flush_invalidate_cache_all();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flush_cache_page(struct vm_area_struct* vma, unsigned long address,
 | 
				
			||||||
 | 
					    		      unsigned long pfn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct page *page = pfn_to_page(pfn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Remove any entry for the old mapping. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (current->active_mm == vma->vm_mm) {
 | 
				
			||||||
 | 
							unsigned long addr = __pa(page_address(page));
 | 
				
			||||||
 | 
							__flush_invalidate_dcache_page_phys(addr);
 | 
				
			||||||
 | 
							if ((vma->vm_flags & VM_EXEC) != 0)
 | 
				
			||||||
 | 
								__invalidate_icache_page_phys(addr);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif	/* (DCACHE_WAY_SIZE > PAGE_SIZE) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pte_t* pte_alloc_one_kernel (struct mm_struct* mm, unsigned long addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pte_t* pte = (pte_t*)__get_free_pages(GFP_KERNEL|__GFP_REPEAT, 0);
 | 
				
			||||||
 | 
						if (likely(pte)) {
 | 
				
			||||||
 | 
						       	pte_t* ptep = (pte_t*)(pte_val(*pte) + PAGE_OFFSET);
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
							for (i = 0; i < 1024; i++, ptep++)
 | 
				
			||||||
 | 
								pte_clear(mm, addr, ptep);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return pte;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct page* pte_alloc_one(struct mm_struct *mm, unsigned long addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct page *page;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						page = alloc_pages(GFP_KERNEL | __GFP_REPEAT, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (likely(page)) {
 | 
				
			||||||
 | 
							pte_t* ptep = kmap_atomic(page, KM_USER0);
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < 1024; i++, ptep++)
 | 
				
			||||||
 | 
								pte_clear(mm, addr, ptep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							kunmap_atomic(ptep, KM_USER0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return page;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Handle D$/I$ coherency.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note:
 | 
				
			||||||
 | 
					 * We only have one architecture bit for the page flags, so we cannot handle
 | 
				
			||||||
 | 
					 * cache aliasing, yet.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t pte)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long pfn = pte_pfn(pte);
 | 
				
			||||||
 | 
						struct page *page;
 | 
				
			||||||
 | 
						unsigned long vaddr = addr & PAGE_MASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pfn_valid(pfn))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						page = pfn_to_page(pfn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						invalidate_itlb_mapping(addr);
 | 
				
			||||||
 | 
						invalidate_dtlb_mapping(addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We have a new mapping. Use it. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						write_dtlb_entry(pte, dtlb_probe(addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If the processor can execute from this page, synchronize D$/I$. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((vma->vm_flags & VM_EXEC) != 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							write_itlb_entry(pte, itlb_probe(addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Synchronize caches, if not clean. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!test_and_set_bit(PG_cache_clean, &page->flags)) {
 | 
				
			||||||
 | 
								__flush_dcache_page(vaddr);
 | 
				
			||||||
 | 
								__invalidate_icache_page(vaddr);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										374
									
								
								arch/xtensa/mm/misc.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								arch/xtensa/mm/misc.S
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,374 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * arch/xtensa/mm/misc.S
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Miscellaneous assembly functions.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is subject to the terms and conditions of the GNU General Public
 | 
				
			||||||
 | 
					 * License.  See the file "COPYING" in the main directory of this archive
 | 
				
			||||||
 | 
					 * for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2001 - 2005 Tensilica Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Chris Zankel	<chris@zankel.net>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Note: we might want to implement some of the loops as zero-overhead-loops,
 | 
				
			||||||
 | 
					 *	 where applicable and if supported by the processor.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/linkage.h>
 | 
				
			||||||
 | 
					#include <asm/page.h>
 | 
				
			||||||
 | 
					#include <asm/pgtable.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <xtensa/cacheasm.h>
 | 
				
			||||||
 | 
					#include <xtensa/cacheattrasm.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* clear_page (page) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(clear_page)
 | 
				
			||||||
 | 
						entry	a1, 16
 | 
				
			||||||
 | 
						addi	a4, a2, PAGE_SIZE
 | 
				
			||||||
 | 
						movi	a3, 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	s32i	a3, a2, 0
 | 
				
			||||||
 | 
						s32i	a3, a2, 4
 | 
				
			||||||
 | 
						s32i	a3, a2, 8
 | 
				
			||||||
 | 
						s32i	a3, a2, 12
 | 
				
			||||||
 | 
						s32i	a3, a2, 16
 | 
				
			||||||
 | 
						s32i	a3, a2, 20
 | 
				
			||||||
 | 
						s32i	a3, a2, 24
 | 
				
			||||||
 | 
						s32i	a3, a2, 28
 | 
				
			||||||
 | 
						addi	a2, a2, 32
 | 
				
			||||||
 | 
						blt	a2, a4, 1b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * copy_page (void *to, void *from)
 | 
				
			||||||
 | 
					 *                  a2        a3
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(copy_page)
 | 
				
			||||||
 | 
						entry	a1, 16
 | 
				
			||||||
 | 
						addi	a4, a2, PAGE_SIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	l32i	a5, a3, 0
 | 
				
			||||||
 | 
						l32i	a6, a3, 4
 | 
				
			||||||
 | 
						l32i	a7, a3, 8
 | 
				
			||||||
 | 
						s32i	a5, a2, 0
 | 
				
			||||||
 | 
						s32i	a6, a2, 4
 | 
				
			||||||
 | 
						s32i	a7, a2, 8
 | 
				
			||||||
 | 
						l32i	a5, a3, 12
 | 
				
			||||||
 | 
						l32i	a6, a3, 16
 | 
				
			||||||
 | 
						l32i	a7, a3, 20
 | 
				
			||||||
 | 
						s32i	a5, a2, 12
 | 
				
			||||||
 | 
						s32i	a6, a2, 16
 | 
				
			||||||
 | 
						s32i	a7, a2, 20
 | 
				
			||||||
 | 
						l32i	a5, a3, 24
 | 
				
			||||||
 | 
						l32i	a6, a3, 28
 | 
				
			||||||
 | 
						s32i	a5, a2, 24
 | 
				
			||||||
 | 
						s32i	a6, a2, 28
 | 
				
			||||||
 | 
						addi	a2, a2, 32
 | 
				
			||||||
 | 
						addi	a3, a3, 32
 | 
				
			||||||
 | 
						blt	a2, a4, 1b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __flush_invalidate_cache_all(void)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__flush_invalidate_cache_all)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						dcache_writeback_inv_all a2, a3
 | 
				
			||||||
 | 
						icache_invalidate_all a2, a3
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __invalidate_icache_all(void)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_icache_all)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						icache_invalidate_all a2, a3
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __flush_invalidate_dcache_all(void)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__flush_invalidate_dcache_all)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						dcache_writeback_inv_all a2, a3
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __flush_invalidate_cache_range(ulong start, ulong size)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__flush_invalidate_cache_range)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						mov	a4, a2
 | 
				
			||||||
 | 
						mov	a5, a3
 | 
				
			||||||
 | 
						dcache_writeback_inv_region a4, a5, a6
 | 
				
			||||||
 | 
						icache_invalidate_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __invalidate_icache_page(ulong start)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_icache_page)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						movi	a3, PAGE_SIZE
 | 
				
			||||||
 | 
						icache_invalidate_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __invalidate_dcache_page(ulong start)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_dcache_page)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						movi	a3, PAGE_SIZE
 | 
				
			||||||
 | 
						dcache_invalidate_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __invalidate_icache_range(ulong start, ulong size)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_icache_range)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						icache_invalidate_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __invalidate_dcache_range(ulong start, ulong size)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_dcache_range)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						dcache_invalidate_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __flush_dcache_page(ulong start)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__flush_dcache_page)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						movi	a3, PAGE_SIZE
 | 
				
			||||||
 | 
						dcache_writeback_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __flush_invalidate_dcache_page(ulong start)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__flush_invalidate_dcache_page)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						movi	a3, PAGE_SIZE
 | 
				
			||||||
 | 
						dcache_writeback_inv_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __flush_invalidate_dcache_range(ulong start, ulong size)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__flush_invalidate_dcache_range)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						dcache_writeback_inv_region a2, a3, a4
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __invalidate_dcache_all(void)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_dcache_all)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
						dcache_invalidate_all a2, a3
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __flush_invalidate_dcache_page_phys(ulong start)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__flush_invalidate_dcache_page_phys)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a3, XCHAL_DCACHE_SIZE
 | 
				
			||||||
 | 
						movi	a4, PAGE_MASK | 1
 | 
				
			||||||
 | 
						addi	a2, a2, 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	addi	a3, a3, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ldct	a6, a3
 | 
				
			||||||
 | 
						dsync
 | 
				
			||||||
 | 
						and	a6, a6, a4
 | 
				
			||||||
 | 
						beq	a6, a2, 2f
 | 
				
			||||||
 | 
						bgeui	a3, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	diwbi	a3, 0
 | 
				
			||||||
 | 
						bgeui	a3, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(check_dcache_low0)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a3, XCHAL_DCACHE_SIZE / 4
 | 
				
			||||||
 | 
						movi	a4, PAGE_MASK | 1
 | 
				
			||||||
 | 
						addi	a2, a2, 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	addi	a3, a3, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ldct	a6, a3
 | 
				
			||||||
 | 
						dsync
 | 
				
			||||||
 | 
						and	a6, a6, a4
 | 
				
			||||||
 | 
						beq	a6, a2, 2f
 | 
				
			||||||
 | 
						bgeui	a3, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	j 2b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(check_dcache_high0)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a5, XCHAL_DCACHE_SIZE / 4
 | 
				
			||||||
 | 
						movi	a3, XCHAL_DCACHE_SIZE / 2
 | 
				
			||||||
 | 
						movi	a4, PAGE_MASK | 1
 | 
				
			||||||
 | 
						addi	a2, a2, 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	addi	a3, a3, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
						addi	a5, a5, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ldct	a6, a3
 | 
				
			||||||
 | 
						dsync
 | 
				
			||||||
 | 
						and	a6, a6, a4
 | 
				
			||||||
 | 
						beq	a6, a2, 2f
 | 
				
			||||||
 | 
						bgeui	a5, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	j 2b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(check_dcache_low1)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a5, XCHAL_DCACHE_SIZE / 4
 | 
				
			||||||
 | 
						movi	a3, XCHAL_DCACHE_SIZE * 3 / 4
 | 
				
			||||||
 | 
						movi	a4, PAGE_MASK | 1
 | 
				
			||||||
 | 
						addi	a2, a2, 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	addi	a3, a3, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
						addi	a5, a5, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ldct	a6, a3
 | 
				
			||||||
 | 
						dsync
 | 
				
			||||||
 | 
						and	a6, a6, a4
 | 
				
			||||||
 | 
						beq	a6, a2, 2f
 | 
				
			||||||
 | 
						bgeui	a5, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	j 2b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(check_dcache_high1)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a5, XCHAL_DCACHE_SIZE / 4
 | 
				
			||||||
 | 
						movi	a3, XCHAL_DCACHE_SIZE
 | 
				
			||||||
 | 
						movi	a4, PAGE_MASK | 1
 | 
				
			||||||
 | 
						addi	a2, a2, 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	addi	a3, a3, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
						addi	a5, a5, -XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ldct	a6, a3
 | 
				
			||||||
 | 
						dsync
 | 
				
			||||||
 | 
						and	a6, a6, a4
 | 
				
			||||||
 | 
						beq	a6, a2, 2f
 | 
				
			||||||
 | 
						bgeui	a5, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	j 2b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * void __invalidate_icache_page_phys(ulong start)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_icache_page_phys)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a3, XCHAL_ICACHE_SIZE
 | 
				
			||||||
 | 
						movi	a4, PAGE_MASK | 1
 | 
				
			||||||
 | 
						addi	a2, a2, 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	addi	a3, a3, -XCHAL_ICACHE_LINESIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lict	a6, a3
 | 
				
			||||||
 | 
						isync
 | 
				
			||||||
 | 
						and	a6, a6, a4
 | 
				
			||||||
 | 
						beq	a6, a2, 2f
 | 
				
			||||||
 | 
						bgeui	a3, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	iii	a3, 0
 | 
				
			||||||
 | 
						bgeui	a3, 2, 1b
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a3, XCHAL_DCACHE_WAYS - 1
 | 
				
			||||||
 | 
						movi	a4, PAGE_SIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	mov	a5, a2
 | 
				
			||||||
 | 
						add	a6, a2, a4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	diwbi	a5, 0
 | 
				
			||||||
 | 
						diwbi	a5, XCHAL_DCACHE_LINESIZE
 | 
				
			||||||
 | 
						diwbi	a5, XCHAL_DCACHE_LINESIZE * 2
 | 
				
			||||||
 | 
						diwbi	a5, XCHAL_DCACHE_LINESIZE * 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						addi	a5, a5, XCHAL_DCACHE_LINESIZE * 4
 | 
				
			||||||
 | 
						blt	a5, a6, 2b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						addi	a3, a3, -1
 | 
				
			||||||
 | 
						addi	a2, a2, XCHAL_DCACHE_SIZE / XCHAL_DCACHE_WAYS
 | 
				
			||||||
 | 
						bgez	a3, 1b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRY(__invalidate_icache_page_index)
 | 
				
			||||||
 | 
						entry	sp, 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						movi	a3, XCHAL_ICACHE_WAYS - 1
 | 
				
			||||||
 | 
						movi	a4, PAGE_SIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1:	mov	a5, a2
 | 
				
			||||||
 | 
						add	a6, a2, a4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	iii	a5, 0
 | 
				
			||||||
 | 
						iii	a5, XCHAL_ICACHE_LINESIZE
 | 
				
			||||||
 | 
						iii	a5, XCHAL_ICACHE_LINESIZE * 2
 | 
				
			||||||
 | 
						iii	a5, XCHAL_ICACHE_LINESIZE * 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						addi	a5, a5, XCHAL_ICACHE_LINESIZE * 4
 | 
				
			||||||
 | 
						blt	a5, a6, 2b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						addi	a3, a3, -1
 | 
				
			||||||
 | 
						addi	a2, a2, XCHAL_ICACHE_SIZE / XCHAL_ICACHE_WAYS
 | 
				
			||||||
 | 
						bgez	a3, 2b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						retw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										76
									
								
								arch/xtensa/mm/pgtable.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								arch/xtensa/mm/pgtable.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,76 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * arch/xtensa/mm/fault.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is subject to the terms and conditions of the GNU General Public
 | 
				
			||||||
 | 
					 * License.  See the file "COPYING" in the main directory of this archive
 | 
				
			||||||
 | 
					 * for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2001 - 2005 Tensilica Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Chris Zankel <chris@zankel.net>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (DCACHE_SIZE > PAGE_SIZE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pte_t* pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pte_t *pte, p;
 | 
				
			||||||
 | 
						int color = ADDR_COLOR(address);
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = (pte_t*) __get_free_pages(GFP_KERNEL|__GFP_REPEAT, COLOR_ORDER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (likely(p)) {
 | 
				
			||||||
 | 
							struct page *page;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < COLOR_SIZE; i++, p++) {
 | 
				
			||||||
 | 
								page = virt_to_page(pte);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								set_page_count(page, 1);
 | 
				
			||||||
 | 
								ClearPageCompound(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (ADDR_COLOR(p) == color)
 | 
				
			||||||
 | 
									pte = p;
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									free_page(p);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							clear_page(pte);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return pte;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef PROFILING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mask;
 | 
				
			||||||
 | 
					int hit;
 | 
				
			||||||
 | 
					int flush;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct page* pte_alloc_one(struct mm_struct *mm, unsigned long address)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct page *page, p;
 | 
				
			||||||
 | 
						int color = ADDR_COLOR(address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (likely(p)) {
 | 
				
			||||||
 | 
							for (i = 0; i < PAGE_ORDER; i++) {
 | 
				
			||||||
 | 
								set_page_count(p, 1);
 | 
				
			||||||
 | 
								ClearPageCompound(p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (PADDR_COLOR(page_address(pg)) == color)
 | 
				
			||||||
 | 
									page = p;
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									free_page(p);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							clear_highpage(page);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return page;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										545
									
								
								arch/xtensa/mm/tlb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										545
									
								
								arch/xtensa/mm/tlb.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,545 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * arch/xtensa/mm/mmu.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Logic that manipulates the Xtensa MMU.  Derived from MIPS.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is subject to the terms and conditions of the GNU General Public
 | 
				
			||||||
 | 
					 * License.  See the file "COPYING" in the main directory of this archive
 | 
				
			||||||
 | 
					 * for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2001 - 2003 Tensilica Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Joe Taylor
 | 
				
			||||||
 | 
					 * Chris Zankel	<chris@zankel.net>
 | 
				
			||||||
 | 
					 * Marc Gauthier
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/mm.h>
 | 
				
			||||||
 | 
					#include <asm/processor.h>
 | 
				
			||||||
 | 
					#include <asm/mmu_context.h>
 | 
				
			||||||
 | 
					#include <asm/tlbflush.h>
 | 
				
			||||||
 | 
					#include <asm/system.h>
 | 
				
			||||||
 | 
					#include <asm/cacheflush.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void __flush_itlb_all (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int way, index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (way = 0; way < XCHAL_ITLB_ARF_WAYS; way++) {
 | 
				
			||||||
 | 
							for (index = 0; index < ITLB_ENTRIES_PER_ARF_WAY; index++) {
 | 
				
			||||||
 | 
								int entry = way + (index << PAGE_SHIFT);
 | 
				
			||||||
 | 
								invalidate_itlb_entry_no_isync (entry);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						asm volatile ("isync\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void __flush_dtlb_all (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int way, index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (way = 0; way < XCHAL_DTLB_ARF_WAYS; way++) {
 | 
				
			||||||
 | 
							for (index = 0; index < DTLB_ENTRIES_PER_ARF_WAY; index++) {
 | 
				
			||||||
 | 
								int entry = way + (index << PAGE_SHIFT);
 | 
				
			||||||
 | 
								invalidate_dtlb_entry_no_isync (entry);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						asm volatile ("isync\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flush_tlb_all (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__flush_itlb_all();
 | 
				
			||||||
 | 
						__flush_dtlb_all();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* If mm is current, we simply assign the current task a new ASID, thus,
 | 
				
			||||||
 | 
					 * invalidating all previous tlb entries. If mm is someone else's user mapping,
 | 
				
			||||||
 | 
					 * wie invalidate the context, thus, when that user mapping is swapped in,
 | 
				
			||||||
 | 
					 * a new context will be assigned to it.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flush_tlb_mm(struct mm_struct *mm)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						printk("[tlbmm<%lx>]\n", (unsigned long)mm->context);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mm == current->active_mm) {
 | 
				
			||||||
 | 
							int flags;
 | 
				
			||||||
 | 
							local_save_flags(flags);
 | 
				
			||||||
 | 
							get_new_mmu_context(mm, asid_cache);
 | 
				
			||||||
 | 
							set_rasid_register(ASID_INSERT(mm->context));
 | 
				
			||||||
 | 
							local_irq_restore(flags);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							mm->context = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flush_tlb_range (struct vm_area_struct *vma,
 | 
				
			||||||
 | 
					    		      unsigned long start, unsigned long end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mm_struct *mm = vma->vm_mm;
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mm->context == NO_CONTEXT)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						printk("[tlbrange<%02lx,%08lx,%08lx>]\n",
 | 
				
			||||||
 | 
								(unsigned long)mm->context, start, end);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						local_save_flags(flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (end-start + (PAGE_SIZE-1) <= SMALLEST_NTLB_ENTRIES << PAGE_SHIFT) {
 | 
				
			||||||
 | 
							int oldpid = get_rasid_register();
 | 
				
			||||||
 | 
							set_rasid_register (ASID_INSERT(mm->context));
 | 
				
			||||||
 | 
							start &= PAGE_MASK;
 | 
				
			||||||
 | 
					 		if (vma->vm_flags & VM_EXEC)
 | 
				
			||||||
 | 
								while(start < end) {
 | 
				
			||||||
 | 
									invalidate_itlb_mapping(start);
 | 
				
			||||||
 | 
									invalidate_dtlb_mapping(start);
 | 
				
			||||||
 | 
									start += PAGE_SIZE;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								while(start < end) {
 | 
				
			||||||
 | 
									invalidate_dtlb_mapping(start);
 | 
				
			||||||
 | 
									start += PAGE_SIZE;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							set_rasid_register(oldpid);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							get_new_mmu_context(mm, asid_cache);
 | 
				
			||||||
 | 
							if (mm == current->active_mm)
 | 
				
			||||||
 | 
								set_rasid_register(ASID_INSERT(mm->context));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						local_irq_restore(flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flush_tlb_page (struct vm_area_struct *vma, unsigned long page)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mm_struct* mm = vma->vm_mm;
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
						int oldpid;
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						printk("[tlbpage<%02lx,%08lx>]\n",
 | 
				
			||||||
 | 
								(unsigned long)mm->context, page);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(mm->context == NO_CONTEXT)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local_save_flags(flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       	oldpid = get_rasid_register();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (vma->vm_flags & VM_EXEC)
 | 
				
			||||||
 | 
							invalidate_itlb_mapping(page);
 | 
				
			||||||
 | 
						invalidate_dtlb_mapping(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_rasid_register(oldpid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local_irq_restore(flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						flush_tlb_all();
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef DEBUG_TLB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define USE_ITLB  0
 | 
				
			||||||
 | 
					#define USE_DTLB  1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct way_config_t {
 | 
				
			||||||
 | 
						int indicies;
 | 
				
			||||||
 | 
						int indicies_log2;
 | 
				
			||||||
 | 
						int pgsz_log2;
 | 
				
			||||||
 | 
						int arf;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct way_config_t itlb[XCHAL_ITLB_WAYS] =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						{ XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ARF)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct way_config_t dtlb[XCHAL_DTLB_WAYS] =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ARF)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{ XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES_LOG2),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, PAGESZ_LOG2_MIN),
 | 
				
			||||||
 | 
						  XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ARF)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*  Total number of entries:  */
 | 
				
			||||||
 | 
					#define ITLB_TOTAL_ENTRIES	\
 | 
				
			||||||
 | 
							XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES)
 | 
				
			||||||
 | 
					#define DTLB_TOTAL_ENTRIES	\
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES) + \
 | 
				
			||||||
 | 
							XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    unsigned		va;
 | 
				
			||||||
 | 
					    unsigned		pa;
 | 
				
			||||||
 | 
					    unsigned char	asid;
 | 
				
			||||||
 | 
					    unsigned char	ca;
 | 
				
			||||||
 | 
					    unsigned char	way;
 | 
				
			||||||
 | 
					    unsigned char	index;
 | 
				
			||||||
 | 
					    unsigned char	pgsz_log2;	/* 0 .. 32 */
 | 
				
			||||||
 | 
					    unsigned char	type;		/* 0=ITLB 1=DTLB */
 | 
				
			||||||
 | 
					} tlb_dump_entry_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*  Return -1 if a precedes b, +1 if a follows b, 0 if same:  */
 | 
				
			||||||
 | 
					int cmp_tlb_dump_info( tlb_dump_entry_t *a, tlb_dump_entry_t *b )
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (a->asid < b->asid) return -1;
 | 
				
			||||||
 | 
					    if (a->asid > b->asid) return  1;
 | 
				
			||||||
 | 
					    if (a->va < b->va) return -1;
 | 
				
			||||||
 | 
					    if (a->va > b->va) return  1;
 | 
				
			||||||
 | 
					    if (a->pa < b->pa) return -1;
 | 
				
			||||||
 | 
					    if (a->pa > b->pa) return  1;
 | 
				
			||||||
 | 
					    if (a->ca < b->ca) return -1;
 | 
				
			||||||
 | 
					    if (a->ca > b->ca) return  1;
 | 
				
			||||||
 | 
					    if (a->way < b->way) return -1;
 | 
				
			||||||
 | 
					    if (a->way > b->way) return  1;
 | 
				
			||||||
 | 
					    if (a->index < b->index) return -1;
 | 
				
			||||||
 | 
					    if (a->index > b->index) return  1;
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sort_tlb_dump_info( tlb_dump_entry_t *t, int n )
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int i, j;
 | 
				
			||||||
 | 
					    /*  Simple O(n*n) sort:  */
 | 
				
			||||||
 | 
					    for (i = 0; i < n-1; i++)
 | 
				
			||||||
 | 
						for (j = i+1; j < n; j++)
 | 
				
			||||||
 | 
						    if (cmp_tlb_dump_info(t+i, t+j) > 0) {
 | 
				
			||||||
 | 
							tlb_dump_entry_t tmp = t[i];
 | 
				
			||||||
 | 
							t[i] = t[j];
 | 
				
			||||||
 | 
							t[j] = tmp;
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static tlb_dump_entry_t itlb_dump_info[ITLB_TOTAL_ENTRIES];
 | 
				
			||||||
 | 
					static tlb_dump_entry_t dtlb_dump_info[DTLB_TOTAL_ENTRIES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline char *way_type (int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return type ? "autorefill" : "non-autorefill";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_entry (struct way_config_t *way_info,
 | 
				
			||||||
 | 
							  unsigned int way,
 | 
				
			||||||
 | 
							  unsigned int index,
 | 
				
			||||||
 | 
							  unsigned int virtual,
 | 
				
			||||||
 | 
							  unsigned int translation)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char valid_chr;
 | 
				
			||||||
 | 
						unsigned int va, pa, asid, ca;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						va = virtual &
 | 
				
			||||||
 | 
						  	~((1 << (way_info->pgsz_log2 + way_info->indicies_log2)) - 1);
 | 
				
			||||||
 | 
						asid = virtual & ((1 << XCHAL_MMU_ASID_BITS) - 1);
 | 
				
			||||||
 | 
						pa = translation & ~((1 << way_info->pgsz_log2) - 1);
 | 
				
			||||||
 | 
						ca = translation & ((1 << XCHAL_MMU_CA_BITS) - 1);
 | 
				
			||||||
 | 
						valid_chr = asid ? 'V' : 'I';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Compute and incorporate the effect of the index bits on the
 | 
				
			||||||
 | 
						 * va.  It's more useful for kernel debugging, since we always
 | 
				
			||||||
 | 
						 * want to know the effective va anyway. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						va += index << way_info->pgsz_log2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printk ("\t[%d,%d] (%c) vpn 0x%.8x  ppn 0x%.8x  asid 0x%.2x  am 0x%x\n",
 | 
				
			||||||
 | 
							way, index, valid_chr, va, pa, asid, ca);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_itlb_entry (struct way_config_t *way_info, int way, int index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						print_entry (way_info, way, index,
 | 
				
			||||||
 | 
							     read_itlb_virtual (way + (index << way_info->pgsz_log2)),
 | 
				
			||||||
 | 
							     read_itlb_translation (way + (index << way_info->pgsz_log2)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_dtlb_entry (struct way_config_t *way_info, int way, int index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						print_entry (way_info, way, index,
 | 
				
			||||||
 | 
							     read_dtlb_virtual (way + (index << way_info->pgsz_log2)),
 | 
				
			||||||
 | 
							     read_dtlb_translation (way + (index << way_info->pgsz_log2)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_itlb (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int way, index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printk ("\nITLB: ways = %d\n", XCHAL_ITLB_WAYS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (way = 0; way < XCHAL_ITLB_WAYS; way++) {
 | 
				
			||||||
 | 
							printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n",
 | 
				
			||||||
 | 
								way, itlb[way].indicies,
 | 
				
			||||||
 | 
								itlb[way].pgsz_log2, way_type(itlb[way].arf));
 | 
				
			||||||
 | 
							for (index = 0; index < itlb[way].indicies; index++) {
 | 
				
			||||||
 | 
								print_itlb_entry(&itlb[way], way, index);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_dtlb (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int way, index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printk ("\nDTLB: ways = %d\n", XCHAL_DTLB_WAYS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (way = 0; way < XCHAL_DTLB_WAYS; way++) {
 | 
				
			||||||
 | 
							printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n",
 | 
				
			||||||
 | 
								way, dtlb[way].indicies,
 | 
				
			||||||
 | 
								dtlb[way].pgsz_log2, way_type(dtlb[way].arf));
 | 
				
			||||||
 | 
							for (index = 0; index < dtlb[way].indicies; index++) {
 | 
				
			||||||
 | 
								print_dtlb_entry(&dtlb[way], way, index);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_tlb (tlb_dump_entry_t *tinfo, struct way_config_t *config,
 | 
				
			||||||
 | 
							int entries, int ways, int type, int show_invalid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    tlb_dump_entry_t *e = tinfo;
 | 
				
			||||||
 | 
					    int way, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*  Gather all info:  */
 | 
				
			||||||
 | 
					    for (way = 0; way < ways; way++) {
 | 
				
			||||||
 | 
						struct way_config_t *cfg = config + way;
 | 
				
			||||||
 | 
						for (i = 0; i < cfg->indicies; i++) {
 | 
				
			||||||
 | 
						    unsigned wayindex = way + (i << cfg->pgsz_log2);
 | 
				
			||||||
 | 
						    unsigned vv = (type ? read_dtlb_virtual (wayindex)
 | 
				
			||||||
 | 
							    		: read_itlb_virtual (wayindex));
 | 
				
			||||||
 | 
						    unsigned pp = (type ? read_dtlb_translation (wayindex)
 | 
				
			||||||
 | 
							    		: read_itlb_translation (wayindex));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    /* Compute and incorporate the effect of the index bits on the
 | 
				
			||||||
 | 
						     * va.  It's more useful for kernel debugging, since we always
 | 
				
			||||||
 | 
						     * want to know the effective va anyway. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    e->va = (vv & ~((1 << (cfg->pgsz_log2 + cfg->indicies_log2)) - 1));
 | 
				
			||||||
 | 
						    e->va += (i << cfg->pgsz_log2);
 | 
				
			||||||
 | 
						    e->pa = (pp & ~((1 << cfg->pgsz_log2) - 1));
 | 
				
			||||||
 | 
						    e->asid = (vv & ((1 << XCHAL_MMU_ASID_BITS) - 1));
 | 
				
			||||||
 | 
						    e->ca = (pp & ((1 << XCHAL_MMU_CA_BITS) - 1));
 | 
				
			||||||
 | 
						    e->way = way;
 | 
				
			||||||
 | 
						    e->index = i;
 | 
				
			||||||
 | 
						    e->pgsz_log2 = cfg->pgsz_log2;
 | 
				
			||||||
 | 
						    e->type = type;
 | 
				
			||||||
 | 
						    e++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#if 1
 | 
				
			||||||
 | 
					    /*  Sort by ASID and VADDR:  */
 | 
				
			||||||
 | 
					    sort_tlb_dump_info (tinfo, entries);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*  Display all sorted info:  */
 | 
				
			||||||
 | 
					    printk ("\n%cTLB dump:\n", (type ? 'D' : 'I'));
 | 
				
			||||||
 | 
					    for (e = tinfo, i = 0; i < entries; i++, e++) {
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						if (e->asid == 0 && !show_invalid)
 | 
				
			||||||
 | 
						    continue;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						printk ("%c way=%d i=%d  ASID=%02X V=%08X -> P=%08X CA=%X (%d %cB)\n",
 | 
				
			||||||
 | 
							(e->type ? 'D' : 'I'), e->way, e->index,
 | 
				
			||||||
 | 
							e->asid, e->va, e->pa, e->ca,
 | 
				
			||||||
 | 
							(1 << (e->pgsz_log2 % 10)),
 | 
				
			||||||
 | 
							" kMG"[e->pgsz_log2 / 10]
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_tlbs2 (int showinv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    dump_tlb (itlb_dump_info, itlb, ITLB_TOTAL_ENTRIES, XCHAL_ITLB_WAYS, 0, showinv);
 | 
				
			||||||
 | 
					    dump_tlb (dtlb_dump_info, dtlb, DTLB_TOTAL_ENTRIES, XCHAL_DTLB_WAYS, 1, showinv);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_all_tlbs (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    dump_tlbs2 (1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_valid_tlbs (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    dump_tlbs2 (0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_tlbs (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dump_itlb();
 | 
				
			||||||
 | 
						dump_dtlb();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_cache_tag(int dcache, int idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int w, i, s, e;
 | 
				
			||||||
 | 
						unsigned long tag, index;
 | 
				
			||||||
 | 
						unsigned long num_lines, num_ways, cache_size, line_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_ways = dcache ? XCHAL_DCACHE_WAYS : XCHAL_ICACHE_WAYS;
 | 
				
			||||||
 | 
						cache_size = dcache ? XCHAL_DCACHE_SIZE : XCHAL_ICACHE_SIZE;
 | 
				
			||||||
 | 
						line_size = dcache ? XCHAL_DCACHE_LINESIZE : XCHAL_ICACHE_LINESIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_lines = cache_size / num_ways;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s = 0; e = num_lines;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (idx >= 0)
 | 
				
			||||||
 | 
							e = (s = idx * line_size) + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = s; i < e; i+= line_size) {
 | 
				
			||||||
 | 
							printk("\nline %#08x:", i);
 | 
				
			||||||
 | 
							for (w = 0; w < num_ways; w++) {
 | 
				
			||||||
 | 
								index = w * num_lines + i;
 | 
				
			||||||
 | 
								if (dcache)
 | 
				
			||||||
 | 
									__asm__ __volatile__("ldct %0, %1\n\t"
 | 
				
			||||||
 | 
											: "=a"(tag) : "a"(index));
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									__asm__ __volatile__("lict %0, %1\n\t"
 | 
				
			||||||
 | 
											: "=a"(tag) : "a"(index));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								printk(" %#010lx", tag);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printk ("\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_icache(int index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long data, addr;
 | 
				
			||||||
 | 
						int w, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const unsigned long num_ways = XCHAL_ICACHE_WAYS;
 | 
				
			||||||
 | 
						const unsigned long cache_size = XCHAL_ICACHE_SIZE;
 | 
				
			||||||
 | 
						const unsigned long line_size = XCHAL_ICACHE_LINESIZE;
 | 
				
			||||||
 | 
						const unsigned long num_lines = cache_size / num_ways / line_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (w = 0; w < num_ways; w++) {
 | 
				
			||||||
 | 
							printk ("\nWay %d", w);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < line_size; i+= 4) {
 | 
				
			||||||
 | 
								addr = w * num_lines + index * line_size + i;
 | 
				
			||||||
 | 
								__asm__ __volatile__("licw %0, %1\n\t"
 | 
				
			||||||
 | 
										: "=a"(data) : "a"(addr));
 | 
				
			||||||
 | 
								printk(" %#010lx", data);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printk ("\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dump_cache_tags(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						printk("Instruction cache\n");
 | 
				
			||||||
 | 
						dump_cache_tag(0, -1);
 | 
				
			||||||
 | 
						printk("Data cache\n");
 | 
				
			||||||
 | 
						dump_cache_tag(1, -1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		Loading…
	
		Reference in a new issue