1295 lines · 31.4 KB · Assembly
RawDownload
/* SPDX-License-Identifier: GPL-2.0-or-later *//* *  PARISC TLB and cache flushing support *  Copyright (C) 2000-2001 Hewlett-Packard (John Marvin) *  Copyright (C) 2001 Matthew Wilcox (willy at parisc-linux.org) *  Copyright (C) 2002 Richard Hirst (rhirst with parisc-linux.org) */ /* * NOTE: fdc,fic, and pdc instructions that use base register modification *       should only use index and base registers that are not shadowed, *       so that the fast path emulation in the non access miss handler *       can be used. */ #ifdef CONFIG_64BIT	.level	2.0w#else	.level	2.0#endif #include <asm/psw.h>#include <asm/assembly.h>#include <asm/cache.h>#include <asm/ldcw.h>#include <asm/alternative.h>#include <linux/linkage.h>#include <linux/init.h>#include <linux/pgtable.h> 	.section .text.hot	.align	16 ENTRY_CFI(flush_tlb_all_local)	/*	 * The pitlbe and pdtlbe instructions should only be used to	 * flush the entire tlb. Also, there needs to be no intervening	 * tlb operations, e.g. tlb misses, so the operation needs	 * to happen in real mode with all interruptions disabled.	 */ 	/* pcxt_ssm_bug	- relied upon translation! PA 2.0 Arch. F-4 and F-5 */	rsm		PSW_SM_I, %r19		/* save I-bit state */	load32		PA(1f), %r1	nop	nop	nop	nop	nop 	rsm		PSW_SM_Q, %r0		/* prep to load iia queue */	mtctl		%r0, %cr17		/* Clear IIASQ tail */	mtctl		%r0, %cr17		/* Clear IIASQ head */	mtctl		%r1, %cr18		/* IIAOQ head */	ldo		4(%r1), %r1	mtctl		%r1, %cr18		/* IIAOQ tail */	load32		REAL_MODE_PSW, %r1	mtctl           %r1, %ipsw	rfi	nop 1:      load32		PA(cache_info), %r1 	/* Flush Instruction Tlb */ 88:	LDREG		ITLB_SID_BASE(%r1), %r20	LDREG		ITLB_SID_STRIDE(%r1), %r21	LDREG		ITLB_SID_COUNT(%r1), %r22	LDREG		ITLB_OFF_BASE(%r1), %arg0	LDREG		ITLB_OFF_STRIDE(%r1), %arg1	LDREG		ITLB_OFF_COUNT(%r1), %arg2	LDREG		ITLB_LOOP(%r1), %arg3 	addib,COND(=)		-1, %arg3, fitoneloop	/* Preadjust and test */	movb,<,n	%arg3, %r31, fitdone	/* If loop < 0, skip */	copy		%arg0, %r28		/* Init base addr */ fitmanyloop:					/* Loop if LOOP >= 2 */	mtsp		%r20, %sr1	add		%r21, %r20, %r20	/* increment space */	copy		%arg2, %r29		/* Init middle loop count */ fitmanymiddle:					/* Loop if LOOP >= 2 */	addib,COND(>)		-1, %r31, fitmanymiddle	/* Adjusted inner loop decr */	pitlbe		%r0(%sr1, %r28)	pitlbe,m	%arg1(%sr1, %r28)	/* Last pitlbe and addr adjust */	addib,COND(>)		-1, %r29, fitmanymiddle	/* Middle loop decr */	copy		%arg3, %r31		/* Re-init inner loop count */ 	movb,tr		%arg0, %r28, fitmanyloop /* Re-init base addr */	addib,COND(<=),n	-1, %r22, fitdone	/* Outer loop count decr */ fitoneloop:					/* Loop if LOOP = 1 */	mtsp		%r20, %sr1	copy		%arg0, %r28		/* init base addr */	copy		%arg2, %r29		/* init middle loop count */ fitonemiddle:					/* Loop if LOOP = 1 */	addib,COND(>)		-1, %r29, fitonemiddle	/* Middle loop count decr */	pitlbe,m	%arg1(%sr1, %r28)	/* pitlbe for one loop */ 	addib,COND(>)		-1, %r22, fitoneloop	/* Outer loop count decr */	add		%r21, %r20, %r20		/* increment space */ fitdone:	ALTERNATIVE(88b, fitdone, ALT_COND_NO_SPLIT_TLB, INSN_NOP) 	/* Flush Data Tlb */ 	LDREG		DTLB_SID_BASE(%r1), %r20	LDREG		DTLB_SID_STRIDE(%r1), %r21	LDREG		DTLB_SID_COUNT(%r1), %r22	LDREG		DTLB_OFF_BASE(%r1), %arg0	LDREG		DTLB_OFF_STRIDE(%r1), %arg1	LDREG		DTLB_OFF_COUNT(%r1), %arg2	LDREG		DTLB_LOOP(%r1), %arg3 	addib,COND(=)		-1, %arg3, fdtoneloop	/* Preadjust and test */	movb,<,n	%arg3, %r31, fdtdone	/* If loop < 0, skip */	copy		%arg0, %r28		/* Init base addr */ fdtmanyloop:					/* Loop if LOOP >= 2 */	mtsp		%r20, %sr1	add		%r21, %r20, %r20	/* increment space */	copy		%arg2, %r29		/* Init middle loop count */ fdtmanymiddle:					/* Loop if LOOP >= 2 */	addib,COND(>)		-1, %r31, fdtmanymiddle	/* Adjusted inner loop decr */	pdtlbe		%r0(%sr1, %r28)	pdtlbe,m	%arg1(%sr1, %r28)	/* Last pdtlbe and addr adjust */	addib,COND(>)		-1, %r29, fdtmanymiddle	/* Middle loop decr */	copy		%arg3, %r31		/* Re-init inner loop count */ 	movb,tr		%arg0, %r28, fdtmanyloop /* Re-init base addr */	addib,COND(<=),n	-1, %r22,fdtdone	/* Outer loop count decr */ fdtoneloop:					/* Loop if LOOP = 1 */	mtsp		%r20, %sr1	copy		%arg0, %r28		/* init base addr */	copy		%arg2, %r29		/* init middle loop count */ fdtonemiddle:					/* Loop if LOOP = 1 */	addib,COND(>)		-1, %r29, fdtonemiddle	/* Middle loop count decr */	pdtlbe,m	%arg1(%sr1, %r28)	/* pdtlbe for one loop */ 	addib,COND(>)		-1, %r22, fdtoneloop	/* Outer loop count decr */	add		%r21, %r20, %r20	/* increment space */  fdtdone:	/*	 * Switch back to virtual mode	 */	/* pcxt_ssm_bug */	rsm		PSW_SM_I, %r0	load32		2f, %r1	nop	nop	nop	nop	nop 	rsm		PSW_SM_Q, %r0		/* prep to load iia queue */	mtctl		%r0, %cr17		/* Clear IIASQ tail */	mtctl		%r0, %cr17		/* Clear IIASQ head */	mtctl		%r1, %cr18		/* IIAOQ head */	ldo		4(%r1), %r1	mtctl		%r1, %cr18		/* IIAOQ tail */	load32		KERNEL_PSW, %r1	or		%r1, %r19, %r1	/* I-bit to state on entry */	mtctl		%r1, %ipsw	/* restore I-bit (entire PSW) */	rfi	nop 2:      bv		%r0(%r2)	nop 	/*	 * When running in qemu, drop whole flush_tlb_all_local function and	 * replace by one pdtlbe instruction, for which QEMU will drop all	 * local TLB entries.	 */3:	pdtlbe		%r0(%sr1,%r0)	bv,n		%r0(%r2)	ALTERNATIVE_CODE(flush_tlb_all_local, 2, ALT_COND_RUN_ON_QEMU, 3b)ENDPROC_CFI(flush_tlb_all_local) 	.import cache_info,data ENTRY_CFI(flush_instruction_cache_local)88:	load32		cache_info, %r1 	/* Flush Instruction Cache */ 	LDREG		ICACHE_BASE(%r1), %arg0	LDREG		ICACHE_STRIDE(%r1), %arg1	LDREG		ICACHE_COUNT(%r1), %arg2	LDREG		ICACHE_LOOP(%r1), %arg3	rsm		PSW_SM_I, %r22		/* No mmgt ops during loop*/	mtsp		%r0, %sr1	addib,COND(=)		-1, %arg3, fioneloop	/* Preadjust and test */	movb,<,n	%arg3, %r31, fisync	/* If loop < 0, do sync */ fimanyloop:					/* Loop if LOOP >= 2 */	addib,COND(>)		-1, %r31, fimanyloop	/* Adjusted inner loop decr */	fice            %r0(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	/* Last fice and addr adjust */	movb,tr		%arg3, %r31, fimanyloop	/* Re-init inner loop count */	addib,COND(<=),n	-1, %arg2, fisync	/* Outer loop decr */ fioneloop:					/* Loop if LOOP = 1 */	/* Some implementations may flush with a single fice instruction */	cmpib,COND(>>=),n	15, %arg2, fioneloop2 fioneloop1:	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	fice,m		%arg1(%sr1, %arg0)	addib,COND(>)	-16, %arg2, fioneloop1	fice,m		%arg1(%sr1, %arg0) 	/* Check if done */	cmpb,COND(=),n	%arg2, %r0, fisync	/* Predict branch taken */ fioneloop2:	addib,COND(>)	-1, %arg2, fioneloop2	/* Outer loop count decr */	fice,m		%arg1(%sr1, %arg0)	/* Fice for one loop */ fisync:	sync	mtsm		%r22			/* restore I-bit */89:	ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP)	bv		%r0(%r2)	nopENDPROC_CFI(flush_instruction_cache_local)  	.import cache_info, dataENTRY_CFI(flush_data_cache_local)88:	load32		cache_info, %r1 	/* Flush Data Cache */ 	LDREG		DCACHE_BASE(%r1), %arg0	LDREG		DCACHE_STRIDE(%r1), %arg1	LDREG		DCACHE_COUNT(%r1), %arg2	LDREG		DCACHE_LOOP(%r1), %arg3	rsm		PSW_SM_I, %r22		/* No mmgt ops during loop*/	mtsp		%r0, %sr1	addib,COND(=)		-1, %arg3, fdoneloop	/* Preadjust and test */	movb,<,n	%arg3, %r31, fdsync	/* If loop < 0, do sync */ fdmanyloop:					/* Loop if LOOP >= 2 */	addib,COND(>)		-1, %r31, fdmanyloop	/* Adjusted inner loop decr */	fdce		%r0(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	/* Last fdce and addr adjust */	movb,tr		%arg3, %r31, fdmanyloop	/* Re-init inner loop count */	addib,COND(<=),n	-1, %arg2, fdsync	/* Outer loop decr */ fdoneloop:					/* Loop if LOOP = 1 */	/* Some implementations may flush with a single fdce instruction */	cmpib,COND(>>=),n	15, %arg2, fdoneloop2 fdoneloop1:	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	fdce,m		%arg1(%sr1, %arg0)	addib,COND(>)	-16, %arg2, fdoneloop1	fdce,m		%arg1(%sr1, %arg0) 	/* Check if done */	cmpb,COND(=),n	%arg2, %r0, fdsync	/* Predict branch taken */ fdoneloop2:	addib,COND(>)	-1, %arg2, fdoneloop2	/* Outer loop count decr */	fdce,m		%arg1(%sr1, %arg0)	/* Fdce for one loop */ fdsync:	sync	mtsm		%r22			/* restore I-bit */89:	ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)	bv		%r0(%r2)	nopENDPROC_CFI(flush_data_cache_local) /* Clear page using kernel mapping.  */ ENTRY_CFI(clear_page_asm)#ifdef CONFIG_64BIT 	/* Unroll the loop.  */	ldi		(PAGE_SIZE / 128), %r1 1:	std		%r0, 0(%r26)	std		%r0, 8(%r26)	std		%r0, 16(%r26)	std		%r0, 24(%r26)	std		%r0, 32(%r26)	std		%r0, 40(%r26)	std		%r0, 48(%r26)	std		%r0, 56(%r26)	std		%r0, 64(%r26)	std		%r0, 72(%r26)	std		%r0, 80(%r26)	std		%r0, 88(%r26)	std		%r0, 96(%r26)	std		%r0, 104(%r26)	std		%r0, 112(%r26)	std		%r0, 120(%r26) 	/* Note reverse branch hint for addib is taken.  */	addib,COND(>),n	-1, %r1, 1b	ldo		128(%r26), %r26 #else 	/*	 * Note that until (if) we start saving the full 64-bit register	 * values on interrupt, we can't use std on a 32 bit kernel.	 */	ldi		(PAGE_SIZE / 64), %r1 1:	stw		%r0, 0(%r26)	stw		%r0, 4(%r26)	stw		%r0, 8(%r26)	stw		%r0, 12(%r26)	stw		%r0, 16(%r26)	stw		%r0, 20(%r26)	stw		%r0, 24(%r26)	stw		%r0, 28(%r26)	stw		%r0, 32(%r26)	stw		%r0, 36(%r26)	stw		%r0, 40(%r26)	stw		%r0, 44(%r26)	stw		%r0, 48(%r26)	stw		%r0, 52(%r26)	stw		%r0, 56(%r26)	stw		%r0, 60(%r26) 	addib,COND(>),n	-1, %r1, 1b	ldo		64(%r26), %r26#endif	bv		%r0(%r2)	nopENDPROC_CFI(clear_page_asm) /* Copy page using kernel mapping.  */ ENTRY_CFI(copy_page_asm)#ifdef CONFIG_64BIT	/* PA8x00 CPUs can consume 2 loads or 1 store per cycle.	 * Unroll the loop by hand and arrange insn appropriately.	 * Prefetch doesn't improve performance on rp3440.	 * GCC probably can do this just as well...	 */ 	ldi		(PAGE_SIZE / 128), %r1 1:	ldd		0(%r25), %r19	ldd		8(%r25), %r20 	ldd		16(%r25), %r21	ldd		24(%r25), %r22	std		%r19, 0(%r26)	std		%r20, 8(%r26) 	ldd		32(%r25), %r19	ldd		40(%r25), %r20	std		%r21, 16(%r26)	std		%r22, 24(%r26) 	ldd		48(%r25), %r21	ldd		56(%r25), %r22	std		%r19, 32(%r26)	std		%r20, 40(%r26) 	ldd		64(%r25), %r19	ldd		72(%r25), %r20	std		%r21, 48(%r26)	std		%r22, 56(%r26) 	ldd		80(%r25), %r21	ldd		88(%r25), %r22	std		%r19, 64(%r26)	std		%r20, 72(%r26) 	ldd		 96(%r25), %r19	ldd		104(%r25), %r20	std		%r21, 80(%r26)	std		%r22, 88(%r26) 	ldd		112(%r25), %r21	ldd		120(%r25), %r22	ldo		128(%r25), %r25	std		%r19, 96(%r26)	std		%r20, 104(%r26) 	std		%r21, 112(%r26)	std		%r22, 120(%r26) 	/* Note reverse branch hint for addib is taken.  */	addib,COND(>),n	-1, %r1, 1b	ldo		128(%r26), %r26 #else 	/*	 * This loop is optimized for PCXL/PCXL2 ldw/ldw and stw/stw	 * bundles (very restricted rules for bundling).	 * Note that until (if) we start saving	 * the full 64 bit register values on interrupt, we can't	 * use ldd/std on a 32 bit kernel.	 */	ldw		0(%r25), %r19	ldi		(PAGE_SIZE / 64), %r1 1:	ldw		4(%r25), %r20	ldw		8(%r25), %r21	ldw		12(%r25), %r22	stw		%r19, 0(%r26)	stw		%r20, 4(%r26)	stw		%r21, 8(%r26)	stw		%r22, 12(%r26)	ldw		16(%r25), %r19	ldw		20(%r25), %r20	ldw		24(%r25), %r21	ldw		28(%r25), %r22	stw		%r19, 16(%r26)	stw		%r20, 20(%r26)	stw		%r21, 24(%r26)	stw		%r22, 28(%r26)	ldw		32(%r25), %r19	ldw		36(%r25), %r20	ldw		40(%r25), %r21	ldw		44(%r25), %r22	stw		%r19, 32(%r26)	stw		%r20, 36(%r26)	stw		%r21, 40(%r26)	stw		%r22, 44(%r26)	ldw		48(%r25), %r19	ldw		52(%r25), %r20	ldw		56(%r25), %r21	ldw		60(%r25), %r22	stw		%r19, 48(%r26)	stw		%r20, 52(%r26)	ldo		64(%r25), %r25	stw		%r21, 56(%r26)	stw		%r22, 60(%r26)	ldo		64(%r26), %r26	addib,COND(>),n	-1, %r1, 1b	ldw		0(%r25), %r19#endif	bv		%r0(%r2)	nopENDPROC_CFI(copy_page_asm) /* * NOTE: Code in clear_user_page has a hard coded dependency on the *       maximum alias boundary being 4 Mb. We've been assured by the *       parisc chip designers that there will not ever be a parisc *       chip with a larger alias boundary (Never say never :-) ). * *       Yah, what about the PA8800 and PA8900 processors? * *       Subtle: the dtlb miss handlers support the temp alias region by *       "knowing" that if a dtlb miss happens within the temp alias *       region it must have occurred while in clear_user_page. Since *       this routine makes use of processor local translations, we *       don't want to insert them into the kernel page table. Instead, *       we load up some general registers (they need to be registers *       which aren't shadowed) with the physical page numbers (preshifted *       for tlb insertion) needed to insert the translations. When we *       miss on the translation, the dtlb miss handler inserts the

Lines 1–500 of 1295.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t