2218 lines · 51.8 KB · Assembly
RawDownload
/* * Linux/PA-RISC Project (http://www.parisc-linux.org/) * * kernel entry points (interruptions, system call wrappers) *  Copyright (C) 1999,2000 Philipp Rumpf  *  Copyright (C) 1999 SuSE GmbH Nuernberg  *  Copyright (C) 2000 Hewlett-Packard (John Marvin) *  Copyright (C) 1999 Hewlett-Packard (Frank Rowand) * *    This program is free software; you can redistribute it and/or modify *    it under the terms of the GNU General Public License as published by *    the Free Software Foundation; either version 2, or (at your option) *    any later version. * *    This program is distributed in the hope that it will be useful, *    but WITHOUT ANY WARRANTY; without even the implied warranty of *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *    GNU General Public License for more details. * *    You should have received a copy of the GNU General Public License *    along with this program; if not, write to the Free Software *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <asm/asm-offsets.h> /* we have the following possibilities to act on an interruption: *  - handle in assembly and use shadowed registers only *  - save registers to kernel stack and handle in assembly or C */  #include <asm/psw.h>#include <asm/cache.h>		/* for L1_CACHE_SHIFT */#include <asm/assembly.h>	/* for LDREG/STREG defines */#include <asm/pgtable.h>#include <asm/signal.h>#include <asm/unistd.h>#include <asm/thread_info.h> #include <linux/linkage.h> #ifdef CONFIG_64BIT	.level 2.0w#else	.level 2.0#endif 	.import         pa_dbit_lock,data 	/* space_to_prot macro creates a prot id from a space id */ #if (SPACEID_SHIFT) == 0	.macro  space_to_prot spc prot	depd,z  \spc,62,31,\prot	.endm#else	.macro  space_to_prot spc prot	extrd,u \spc,(64 - (SPACEID_SHIFT)),32,\prot	.endm#endif 	/* Switch to virtual mapping, trashing only %r1 */	.macro  virt_map	/* pcxt_ssm_bug */	rsm	PSW_SM_I, %r0	/* barrier for "Relied upon Translation */	mtsp	%r0, %sr4	mtsp	%r0, %sr5	mtsp	%r0, %sr6	tovirt_r1 %r29	load32	KERNEL_PSW, %r1 	rsm     PSW_SM_QUIET,%r0	/* second "heavy weight" ctl op */	mtctl	%r0, %cr17	/* Clear IIASQ tail */	mtctl	%r0, %cr17	/* Clear IIASQ head */	mtctl	%r1, %ipsw	load32	4f, %r1	mtctl	%r1, %cr18	/* Set IIAOQ tail */	ldo	4(%r1), %r1	mtctl	%r1, %cr18	/* Set IIAOQ head */	rfir	nop4:	.endm 	/*	 * The "get_stack" macros are responsible for determining the	 * kernel stack value.	 *	 *      If sr7 == 0	 *          Already using a kernel stack, so call the	 *          get_stack_use_r30 macro to push a pt_regs structure	 *          on the stack, and store registers there.	 *      else	 *          Need to set up a kernel stack, so call the	 *          get_stack_use_cr30 macro to set up a pointer	 *          to the pt_regs structure contained within the	 *          task pointer pointed to by cr30. Set the stack	 *          pointer to point to the end of the task structure.	 *	 * Note that we use shadowed registers for temps until	 * we can save %r26 and %r29. %r26 is used to preserve	 * %r8 (a shadowed register) which temporarily contained	 * either the fault type ("code") or the eirr. We need	 * to use a non-shadowed register to carry the value over	 * the rfir in virt_map. We use %r26 since this value winds	 * up being passed as the argument to either do_cpu_irq_mask	 * or handle_interruption. %r29 is used to hold a pointer	 * the register save area, and once again, it needs to	 * be a non-shadowed register so that it survives the rfir.	 *	 * N.B. TASK_SZ_ALGN and PT_SZ_ALGN include space for a stack frame.	 */ 	.macro  get_stack_use_cr30 	/* we save the registers in the task struct */ 	copy	%r30, %r17	mfctl   %cr30, %r1	ldo	THREAD_SZ_ALGN(%r1), %r30	mtsp	%r0,%sr7	mtsp	%r16,%sr3	tophys  %r1,%r9	LDREG	TI_TASK(%r9), %r1	/* thread_info -> task_struct */	tophys  %r1,%r9	ldo     TASK_REGS(%r9),%r9	STREG   %r17,PT_GR30(%r9)	STREG   %r29,PT_GR29(%r9)	STREG   %r26,PT_GR26(%r9)	STREG	%r16,PT_SR7(%r9)	copy    %r9,%r29	.endm 	.macro  get_stack_use_r30 	/* we put a struct pt_regs on the stack and save the registers there */ 	tophys  %r30,%r9	copy	%r30,%r1	ldo	PT_SZ_ALGN(%r30),%r30	STREG   %r1,PT_GR30(%r9)	STREG   %r29,PT_GR29(%r9)	STREG   %r26,PT_GR26(%r9)	STREG	%r16,PT_SR7(%r9)	copy    %r9,%r29	.endm 	.macro  rest_stack	LDREG   PT_GR1(%r29), %r1	LDREG   PT_GR30(%r29),%r30	LDREG   PT_GR29(%r29),%r29	.endm 	/* default interruption handler	 * (calls traps.c:handle_interruption) */	.macro	def code	b	intr_save	ldi     \code, %r8	.align	32	.endm 	/* Interrupt interruption handler	 * (calls irq.c:do_cpu_irq_mask) */	.macro	extint code	b	intr_extint	mfsp    %sr7,%r16	.align	32	.endm	 	.import	os_hpmc, code 	/* HPMC handler */	.macro	hpmc code	nop			/* must be a NOP, will be patched later */	load32	PA(os_hpmc), %r3	bv,n	0(%r3)	nop	.word	0		/* checksum (will be patched) */	.word	PA(os_hpmc)	/* address of handler */	.word	0		/* length of handler */	.endm 	/*	 * Performance Note: Instructions will be moved up into	 * this part of the code later on, once we are sure	 * that the tlb miss handlers are close to final form.	 */ 	/* Register definitions for tlb miss handler macros */ 	va  = r8	/* virtual address for which the trap occurred */	spc = r24	/* space for which the trap occurred */ #ifndef CONFIG_64BIT 	/*	 * itlb miss interruption handler (parisc 1.1 - 32 bit)	 */ 	.macro	itlb_11 code 	mfctl	%pcsq, spc	b	itlb_miss_11	mfctl	%pcoq, va 	.align		32	.endm#endif		/*	 * itlb miss interruption handler (parisc 2.0)	 */ 	.macro	itlb_20 code	mfctl	%pcsq, spc#ifdef CONFIG_64BIT	b       itlb_miss_20w#else	b	itlb_miss_20#endif	mfctl	%pcoq, va 	.align		32	.endm	#ifndef CONFIG_64BIT	/*	 * naitlb miss interruption handler (parisc 1.1 - 32 bit)	 */ 	.macro	naitlb_11 code 	mfctl	%isr,spc	b	naitlb_miss_11	mfctl 	%ior,va 	.align		32	.endm#endif		/*	 * naitlb miss interruption handler (parisc 2.0)	 */ 	.macro	naitlb_20 code 	mfctl	%isr,spc#ifdef CONFIG_64BIT	b       naitlb_miss_20w#else	b	naitlb_miss_20#endif	mfctl 	%ior,va 	.align		32	.endm	#ifndef CONFIG_64BIT	/*	 * dtlb miss interruption handler (parisc 1.1 - 32 bit)	 */ 	.macro	dtlb_11 code 	mfctl	%isr, spc	b	dtlb_miss_11	mfctl	%ior, va 	.align		32	.endm#endif 	/*	 * dtlb miss interruption handler (parisc 2.0)	 */ 	.macro	dtlb_20 code 	mfctl	%isr, spc#ifdef CONFIG_64BIT	b       dtlb_miss_20w#else	b	dtlb_miss_20#endif	mfctl	%ior, va 	.align		32	.endm	#ifndef CONFIG_64BIT	/* nadtlb miss interruption handler (parisc 1.1 - 32 bit) */ 	.macro	nadtlb_11 code 	mfctl	%isr,spc	b       nadtlb_miss_11	mfctl	%ior,va 	.align		32	.endm#endif		/* nadtlb miss interruption handler (parisc 2.0) */ 	.macro	nadtlb_20 code 	mfctl	%isr,spc#ifdef CONFIG_64BIT	b       nadtlb_miss_20w#else	b       nadtlb_miss_20#endif	mfctl	%ior,va 	.align		32	.endm	#ifndef CONFIG_64BIT	/*	 * dirty bit trap interruption handler (parisc 1.1 - 32 bit)	 */ 	.macro	dbit_11 code 	mfctl	%isr,spc	b	dbit_trap_11	mfctl	%ior,va 	.align		32	.endm#endif 	/*	 * dirty bit trap interruption handler (parisc 2.0)	 */ 	.macro	dbit_20 code 	mfctl	%isr,spc#ifdef CONFIG_64BIT	b       dbit_trap_20w#else	b	dbit_trap_20#endif	mfctl	%ior,va 	.align		32	.endm 	/* In LP64, the space contains part of the upper 32 bits of the	 * fault.  We have to extract this and place it in the va,	 * zeroing the corresponding bits in the space register */	.macro		space_adjust	spc,va,tmp#ifdef CONFIG_64BIT	extrd,u		\spc,63,SPACEID_SHIFT,\tmp	depd		%r0,63,SPACEID_SHIFT,\spc	depd		\tmp,31,SPACEID_SHIFT,\va#endif	.endm 	.import		swapper_pg_dir,code 	/* Get the pgd.  For faults on space zero (kernel space), this	 * is simply swapper_pg_dir.  For user space faults, the	 * pgd is stored in %cr25 */	.macro		get_pgd		spc,reg	ldil		L%PA(swapper_pg_dir),\reg	ldo		R%PA(swapper_pg_dir)(\reg),\reg	or,COND(=)	%r0,\spc,%r0	mfctl		%cr25,\reg	.endm 	/* 		space_check(spc,tmp,fault) 		spc - The space we saw the fault with.		tmp - The place to store the current space.		fault - Function to call on failure. 		Only allow faults on different spaces from the		currently active one if we're the kernel  	*/	.macro		space_check	spc,tmp,fault	mfsp		%sr7,\tmp	or,COND(<>)	%r0,\spc,%r0	/* user may execute gateway page					 * as kernel, so defeat the space					 * check if it is */	copy		\spc,\tmp	or,COND(=)	%r0,\tmp,%r0	/* nullify if executing as kernel */	cmpb,COND(<>),n	\tmp,\spc,\fault	.endm 	/* Look up a PTE in a 2-Level scheme (faulting at each	 * level if the entry isn't present 	 *	 * NOTE: we use ldw even for LP64, since the short pointers	 * can address up to 1TB	 */	.macro		L2_ptep	pmd,pte,index,va,fault#if PT_NLEVELS == 3	extru		\va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index#else# if defined(CONFIG_64BIT)	extrd,u		\va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index  #else  # if PAGE_SIZE > 4096	extru		\va,31-ASM_PGDIR_SHIFT,32-ASM_PGDIR_SHIFT,\index  # else	extru		\va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index  # endif# endif#endif	dep             %r0,31,PAGE_SHIFT,\pmd  /* clear offset */	copy		%r0,\pte	ldw,s		\index(\pmd),\pmd	bb,>=,n		\pmd,_PxD_PRESENT_BIT,\fault	dep		%r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */	copy		\pmd,%r9	SHLREG		%r9,PxD_VALUE_SHIFT,\pmd	extru		\va,31-PAGE_SHIFT,ASM_BITS_PER_PTE,\index	dep		%r0,31,PAGE_SHIFT,\pmd  /* clear offset */	shladd		\index,BITS_PER_PTE_ENTRY,\pmd,\pmd	LDREG		%r0(\pmd),\pte		/* pmd is now pte */	bb,>=,n		\pte,_PAGE_PRESENT_BIT,\fault	.endm 	/* Look up PTE in a 3-Level scheme.	 *	 * Here we implement a Hybrid L2/L3 scheme: we allocate the	 * first pmd adjacent to the pgd.  This means that we can	 * subtract a constant offset to get to it.  The pmd and pgd	 * sizes are arranged so that a single pmd covers 4GB (giving	 * a full LP64 process access to 8TB) so our lookups are	 * effectively L2 for the first 4GB of the kernel (i.e. for	 * all ILP32 processes and all the kernel for machines with	 * under 4GB of memory) */	.macro		L3_ptep pgd,pte,index,va,fault#if PT_NLEVELS == 3 /* we might have a 2-Level scheme, e.g. with 16kb page size */	extrd,u		\va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index	copy		%r0,\pte	extrd,u,*=	\va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0	ldw,s		\index(\pgd),\pgd	extrd,u,*=	\va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0	bb,>=,n		\pgd,_PxD_PRESENT_BIT,\fault	extrd,u,*=	\va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0	shld		\pgd,PxD_VALUE_SHIFT,\index	extrd,u,*=	\va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0	copy		\index,\pgd	extrd,u,*<>	\va,63-ASM_PGDIR_SHIFT,64-ASM_PGDIR_SHIFT,%r0	ldo		ASM_PGD_PMD_OFFSET(\pgd),\pgd#endif	L2_ptep		\pgd,\pte,\index,\va,\fault	.endm 	/* Acquire pa_dbit_lock lock. */	.macro		dbit_lock	spc,tmp,tmp1#ifdef CONFIG_SMP	cmpib,COND(=),n	0,\spc,2f	load32		PA(pa_dbit_lock),\tmp1:	LDCW		0(\tmp),\tmp1	cmpib,COND(=)	0,\tmp1,1b	nop2:#endif	.endm 	/* Release pa_dbit_lock lock without reloading lock address. */	.macro		dbit_unlock0	spc,tmp#ifdef CONFIG_SMP	or,COND(=)	%r0,\spc,%r0	stw             \spc,0(\tmp)#endif	.endm 	/* Release pa_dbit_lock lock. */	.macro		dbit_unlock1	spc,tmp#ifdef CONFIG_SMP	load32		PA(pa_dbit_lock),\tmp	dbit_unlock0	\spc,\tmp#endif	.endm 	/* Set the _PAGE_ACCESSED bit of the PTE.  Be clever and	 * don't needlessly dirty the cache line if it was already set */	.macro		update_ptep	spc,ptep,pte,tmp,tmp1#ifdef CONFIG_SMP	or,COND(=)	%r0,\spc,%r0	LDREG		0(\ptep),\pte#endif	ldi		_PAGE_ACCESSED,\tmp1	or		\tmp1,\pte,\tmp	and,COND(<>)	\tmp1,\pte,%r0	STREG		\tmp,0(\ptep)	.endm 	/* Set the dirty bit (and accessed bit).  No need to be	 * clever, this is only used from the dirty fault */	.macro		update_dirty	spc,ptep,pte,tmp#ifdef CONFIG_SMP

Lines 1–500 of 2218.