|  | /* tlb-miss.S: TLB miss handlers | 
|  | * | 
|  | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | 
|  | * Written by David Howells (dhowells@redhat.com) | 
|  | * | 
|  | * 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 of the License, or (at your option) any later version. | 
|  | */ | 
|  |  | 
|  | #include <linux/sys.h> | 
|  | #include <linux/linkage.h> | 
|  | #include <asm/page.h> | 
|  | #include <asm/pgtable.h> | 
|  | #include <asm/highmem.h> | 
|  | #include <asm/spr-regs.h> | 
|  |  | 
|  | .section	.text.tlbmiss | 
|  | .balign		4 | 
|  |  | 
|  | .globl		__entry_insn_mmu_miss | 
|  | __entry_insn_mmu_miss: | 
|  | break | 
|  | nop | 
|  |  | 
|  | .globl		__entry_insn_mmu_exception | 
|  | __entry_insn_mmu_exception: | 
|  | break | 
|  | nop | 
|  |  | 
|  | .globl		__entry_data_mmu_miss | 
|  | __entry_data_mmu_miss: | 
|  | break | 
|  | nop | 
|  |  | 
|  | .globl		__entry_data_mmu_exception | 
|  | __entry_data_mmu_exception: | 
|  | break | 
|  | nop | 
|  |  | 
|  | ############################################################################### | 
|  | # | 
|  | # handle a lookup failure of one sort or another in a kernel TLB handler | 
|  | # On entry: | 
|  | #   GR29 - faulting address | 
|  | #   SCR2 - saved CCR | 
|  | # | 
|  | ############################################################################### | 
|  | .type		__tlb_kernel_fault,@function | 
|  | __tlb_kernel_fault: | 
|  | # see if we're supposed to re-enable single-step mode upon return | 
|  | sethi.p		%hi(__break_tlb_miss_return_break),gr30 | 
|  | setlo		%lo(__break_tlb_miss_return_break),gr30 | 
|  | movsg		pcsr,gr31 | 
|  |  | 
|  | subcc		gr31,gr30,gr0,icc0 | 
|  | beq		icc0,#0,__tlb_kernel_fault_sstep | 
|  |  | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | movgs		gr29,scr2			/* save EAR0 value */ | 
|  | sethi.p		%hi(__kernel_current_task),gr29 | 
|  | setlo		%lo(__kernel_current_task),gr29 | 
|  | ldi.p		@(gr29,#0),gr29			/* restore GR29 */ | 
|  |  | 
|  | bra		__entry_kernel_handle_mmu_fault | 
|  |  | 
|  | # we've got to re-enable single-stepping | 
|  | __tlb_kernel_fault_sstep: | 
|  | sethi.p		%hi(__break_tlb_miss_real_return_info),gr30 | 
|  | setlo		%lo(__break_tlb_miss_real_return_info),gr30 | 
|  | lddi		@(gr30,0),gr30 | 
|  | movgs		gr30,pcsr | 
|  | movgs		gr31,psr | 
|  |  | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | movgs		gr29,scr2			/* save EAR0 value */ | 
|  | sethi.p		%hi(__kernel_current_task),gr29 | 
|  | setlo		%lo(__kernel_current_task),gr29 | 
|  | ldi.p		@(gr29,#0),gr29			/* restore GR29 */ | 
|  | bra		__entry_kernel_handle_mmu_fault_sstep | 
|  |  | 
|  | .size		__tlb_kernel_fault, .-__tlb_kernel_fault | 
|  |  | 
|  | ############################################################################### | 
|  | # | 
|  | # handle a lookup failure of one sort or another in a user TLB handler | 
|  | # On entry: | 
|  | #   GR28 - faulting address | 
|  | #   SCR2 - saved CCR | 
|  | # | 
|  | ############################################################################### | 
|  | .type		__tlb_user_fault,@function | 
|  | __tlb_user_fault: | 
|  | # see if we're supposed to re-enable single-step mode upon return | 
|  | sethi.p		%hi(__break_tlb_miss_return_break),gr30 | 
|  | setlo		%lo(__break_tlb_miss_return_break),gr30 | 
|  | movsg		pcsr,gr31 | 
|  | subcc		gr31,gr30,gr0,icc0 | 
|  | beq		icc0,#0,__tlb_user_fault_sstep | 
|  |  | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | bra		__entry_uspace_handle_mmu_fault | 
|  |  | 
|  | # we've got to re-enable single-stepping | 
|  | __tlb_user_fault_sstep: | 
|  | sethi.p		%hi(__break_tlb_miss_real_return_info),gr30 | 
|  | setlo		%lo(__break_tlb_miss_real_return_info),gr30 | 
|  | lddi		@(gr30,0),gr30 | 
|  | movgs		gr30,pcsr | 
|  | movgs		gr31,psr | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | bra		__entry_uspace_handle_mmu_fault_sstep | 
|  |  | 
|  | .size		__tlb_user_fault, .-__tlb_user_fault | 
|  |  | 
|  | ############################################################################### | 
|  | # | 
|  | # Kernel instruction TLB miss handler | 
|  | # On entry: | 
|  | #   GR1   - kernel stack pointer | 
|  | #   GR28  - saved exception frame pointer | 
|  | #   GR29  - faulting address | 
|  | #   GR31  - EAR0 ^ SCR0 | 
|  | #   SCR0  - base of virtual range covered by cached PGE from last ITLB miss (or 0xffffffff) | 
|  | #   DAMR3 - mapped page directory | 
|  | #   DAMR4 - mapped page table as matched by SCR0 | 
|  | # | 
|  | ############################################################################### | 
|  | .globl		__entry_kernel_insn_tlb_miss | 
|  | .type		__entry_kernel_insn_tlb_miss,@function | 
|  | __entry_kernel_insn_tlb_miss: | 
|  | #if 0 | 
|  | sethi.p		%hi(0xe1200004),gr30 | 
|  | setlo		%lo(0xe1200004),gr30 | 
|  | st		gr0,@(gr30,gr0) | 
|  | sethi.p		%hi(0xffc00100),gr30 | 
|  | setlo		%lo(0xffc00100),gr30 | 
|  | sth		gr30,@(gr30,gr0) | 
|  | membar | 
|  | #endif | 
|  |  | 
|  | movsg		ccr,gr30			/* save CCR */ | 
|  | movgs		gr30,scr2 | 
|  |  | 
|  | # see if the cached page table mapping is appropriate | 
|  | srlicc.p	gr31,#26,gr0,icc0 | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bne		icc0,#0,__itlb_k_PTD_miss | 
|  |  | 
|  | __itlb_k_PTD_mapped: | 
|  | # access the PTD with EAR0[25:14] | 
|  | # - DAMLR4 points to the virtual address of the appropriate page table | 
|  | # - the PTD holds 4096 PTEs | 
|  | # - the PTD must be accessed uncached | 
|  | # - the PTE must be marked accessed if it was valid | 
|  | # | 
|  | and		gr31,gr30,gr31 | 
|  | movsg		damlr4,gr30 | 
|  | add		gr30,gr31,gr31 | 
|  | ldi		@(gr31,#0),gr30			/* fetch the PTE */ | 
|  | andicc		gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | ori.p		gr30,#_PAGE_ACCESSED,gr30 | 
|  | beq		icc0,#0,__tlb_kernel_fault	/* jump if PTE invalid */ | 
|  | sti.p		gr30,@(gr31,#0)			/* update the PTE */ | 
|  | andi		gr30,#~_PAGE_ACCESSED,gr30 | 
|  |  | 
|  | # we're using IAMR1 as an extra TLB entry | 
|  | # - punt the entry here (if valid) to the real TLB and then replace with the new PTE | 
|  | # - need to check DAMR1 lest we cause an multiple-DAT-hit exception | 
|  | # - IAMPR1 has no WP bit, and we mustn't lose WP information | 
|  | movsg		iampr1,gr31 | 
|  | andicc		gr31,#xAMPRx_V,gr0,icc0 | 
|  | setlos.p	0xfffff000,gr31 | 
|  | beq		icc0,#0,__itlb_k_nopunt		/* punt not required */ | 
|  |  | 
|  | movsg		iamlr1,gr31 | 
|  | movgs		gr31,tplr			/* set TPLR.CXN */ | 
|  | tlbpr		gr31,gr0,#4,#0			/* delete matches from TLB, IAMR1, DAMR1 */ | 
|  |  | 
|  | movsg		dampr1,gr31 | 
|  | ori		gr31,#xAMPRx_V,gr31		/* entry was invalidated by tlbpr #4 */ | 
|  | movgs		gr31,tppr | 
|  | movsg		iamlr1,gr31			/* set TPLR.CXN */ | 
|  | movgs		gr31,tplr | 
|  | tlbpr		gr31,gr0,#2,#0			/* save to the TLB */ | 
|  | movsg		tpxr,gr31			/* check the TLB write error flag */ | 
|  | andicc.p	gr31,#TPXR_E,gr0,icc0 | 
|  | setlos		#0xfffff000,gr31 | 
|  | bne		icc0,#0,__tlb_kernel_fault | 
|  |  | 
|  | __itlb_k_nopunt: | 
|  |  | 
|  | # assemble the new TLB entry | 
|  | and		gr29,gr31,gr29 | 
|  | movsg		cxnr,gr31 | 
|  | or		gr29,gr31,gr29 | 
|  | movgs		gr29,iamlr1			/* xAMLR = address | context number */ | 
|  | movgs		gr30,iampr1 | 
|  | movgs		gr29,damlr1 | 
|  | movgs		gr30,dampr1 | 
|  |  | 
|  | # return, restoring registers | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | sethi.p		%hi(__kernel_current_task),gr29 | 
|  | setlo		%lo(__kernel_current_task),gr29 | 
|  | ldi		@(gr29,#0),gr29 | 
|  | rett		#0 | 
|  | beq		icc0,#3,0			/* prevent icache prefetch */ | 
|  |  | 
|  | # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more | 
|  | # appropriate page table and map that instead | 
|  | #   - access the PGD with EAR0[31:26] | 
|  | #   - DAMLR3 points to the virtual address of the page directory | 
|  | #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables | 
|  | __itlb_k_PTD_miss: | 
|  | srli		gr29,#26,gr31			/* calculate PGE offset */ | 
|  | slli		gr31,#8,gr31			/* and clear bottom bits */ | 
|  |  | 
|  | movsg		damlr3,gr30 | 
|  | ld		@(gr31,gr30),gr30		/* access the PGE */ | 
|  |  | 
|  | andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | andicc		gr30,#xAMPRx_SS,gr0,icc1 | 
|  |  | 
|  | # map this PTD instead and record coverage address | 
|  | ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30 | 
|  | beq		icc0,#0,__tlb_kernel_fault	/* jump if PGE not present */ | 
|  | slli.p		gr31,#18,gr31 | 
|  | bne		icc1,#0,__itlb_k_bigpage | 
|  | movgs		gr30,dampr4 | 
|  | movgs		gr31,scr0 | 
|  |  | 
|  | # we can now resume normal service | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bra		__itlb_k_PTD_mapped | 
|  |  | 
|  | __itlb_k_bigpage: | 
|  | break | 
|  | nop | 
|  |  | 
|  | .size		__entry_kernel_insn_tlb_miss, .-__entry_kernel_insn_tlb_miss | 
|  |  | 
|  | ############################################################################### | 
|  | # | 
|  | # Kernel data TLB miss handler | 
|  | # On entry: | 
|  | #   GR1   - kernel stack pointer | 
|  | #   GR28  - saved exception frame pointer | 
|  | #   GR29  - faulting address | 
|  | #   GR31  - EAR0 ^ SCR1 | 
|  | #   SCR1  - base of virtual range covered by cached PGE from last DTLB miss (or 0xffffffff) | 
|  | #   DAMR3 - mapped page directory | 
|  | #   DAMR5 - mapped page table as matched by SCR1 | 
|  | # | 
|  | ############################################################################### | 
|  | .globl		__entry_kernel_data_tlb_miss | 
|  | .type		__entry_kernel_data_tlb_miss,@function | 
|  | __entry_kernel_data_tlb_miss: | 
|  | #if 0 | 
|  | sethi.p		%hi(0xe1200004),gr30 | 
|  | setlo		%lo(0xe1200004),gr30 | 
|  | st		gr0,@(gr30,gr0) | 
|  | sethi.p		%hi(0xffc00100),gr30 | 
|  | setlo		%lo(0xffc00100),gr30 | 
|  | sth		gr30,@(gr30,gr0) | 
|  | membar | 
|  | #endif | 
|  |  | 
|  | movsg		ccr,gr30			/* save CCR */ | 
|  | movgs		gr30,scr2 | 
|  |  | 
|  | # see if the cached page table mapping is appropriate | 
|  | srlicc.p	gr31,#26,gr0,icc0 | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bne		icc0,#0,__dtlb_k_PTD_miss | 
|  |  | 
|  | __dtlb_k_PTD_mapped: | 
|  | # access the PTD with EAR0[25:14] | 
|  | # - DAMLR5 points to the virtual address of the appropriate page table | 
|  | # - the PTD holds 4096 PTEs | 
|  | # - the PTD must be accessed uncached | 
|  | # - the PTE must be marked accessed if it was valid | 
|  | # | 
|  | and		gr31,gr30,gr31 | 
|  | movsg		damlr5,gr30 | 
|  | add		gr30,gr31,gr31 | 
|  | ldi		@(gr31,#0),gr30			/* fetch the PTE */ | 
|  | andicc		gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | ori.p		gr30,#_PAGE_ACCESSED,gr30 | 
|  | beq		icc0,#0,__tlb_kernel_fault	/* jump if PTE invalid */ | 
|  | sti.p		gr30,@(gr31,#0)			/* update the PTE */ | 
|  | andi		gr30,#~_PAGE_ACCESSED,gr30 | 
|  |  | 
|  | # we're using DAMR1 as an extra TLB entry | 
|  | # - punt the entry here (if valid) to the real TLB and then replace with the new PTE | 
|  | # - need to check IAMR1 lest we cause an multiple-DAT-hit exception | 
|  | movsg		dampr1,gr31 | 
|  | andicc		gr31,#xAMPRx_V,gr0,icc0 | 
|  | setlos.p	0xfffff000,gr31 | 
|  | beq		icc0,#0,__dtlb_k_nopunt		/* punt not required */ | 
|  |  | 
|  | movsg		damlr1,gr31 | 
|  | movgs		gr31,tplr			/* set TPLR.CXN */ | 
|  | tlbpr		gr31,gr0,#4,#0			/* delete matches from TLB, IAMR1, DAMR1 */ | 
|  |  | 
|  | movsg		dampr1,gr31 | 
|  | ori		gr31,#xAMPRx_V,gr31		/* entry was invalidated by tlbpr #4 */ | 
|  | movgs		gr31,tppr | 
|  | movsg		damlr1,gr31			/* set TPLR.CXN */ | 
|  | movgs		gr31,tplr | 
|  | tlbpr		gr31,gr0,#2,#0			/* save to the TLB */ | 
|  | movsg		tpxr,gr31			/* check the TLB write error flag */ | 
|  | andicc.p	gr31,#TPXR_E,gr0,icc0 | 
|  | setlos		#0xfffff000,gr31 | 
|  | bne		icc0,#0,__tlb_kernel_fault | 
|  |  | 
|  | __dtlb_k_nopunt: | 
|  |  | 
|  | # assemble the new TLB entry | 
|  | and		gr29,gr31,gr29 | 
|  | movsg		cxnr,gr31 | 
|  | or		gr29,gr31,gr29 | 
|  | movgs		gr29,iamlr1			/* xAMLR = address | context number */ | 
|  | movgs		gr30,iampr1 | 
|  | movgs		gr29,damlr1 | 
|  | movgs		gr30,dampr1 | 
|  |  | 
|  | # return, restoring registers | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | sethi.p		%hi(__kernel_current_task),gr29 | 
|  | setlo		%lo(__kernel_current_task),gr29 | 
|  | ldi		@(gr29,#0),gr29 | 
|  | rett		#0 | 
|  | beq		icc0,#3,0			/* prevent icache prefetch */ | 
|  |  | 
|  | # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more | 
|  | # appropriate page table and map that instead | 
|  | #   - access the PGD with EAR0[31:26] | 
|  | #   - DAMLR3 points to the virtual address of the page directory | 
|  | #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables | 
|  | __dtlb_k_PTD_miss: | 
|  | srli		gr29,#26,gr31			/* calculate PGE offset */ | 
|  | slli		gr31,#8,gr31			/* and clear bottom bits */ | 
|  |  | 
|  | movsg		damlr3,gr30 | 
|  | ld		@(gr31,gr30),gr30		/* access the PGE */ | 
|  |  | 
|  | andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | andicc		gr30,#xAMPRx_SS,gr0,icc1 | 
|  |  | 
|  | # map this PTD instead and record coverage address | 
|  | ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30 | 
|  | beq		icc0,#0,__tlb_kernel_fault	/* jump if PGE not present */ | 
|  | slli.p		gr31,#18,gr31 | 
|  | bne		icc1,#0,__dtlb_k_bigpage | 
|  | movgs		gr30,dampr5 | 
|  | movgs		gr31,scr1 | 
|  |  | 
|  | # we can now resume normal service | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bra		__dtlb_k_PTD_mapped | 
|  |  | 
|  | __dtlb_k_bigpage: | 
|  | break | 
|  | nop | 
|  |  | 
|  | .size		__entry_kernel_data_tlb_miss, .-__entry_kernel_data_tlb_miss | 
|  |  | 
|  | ############################################################################### | 
|  | # | 
|  | # Userspace instruction TLB miss handler (with PGE prediction) | 
|  | # On entry: | 
|  | #   GR28  - faulting address | 
|  | #   GR31  - EAR0 ^ SCR0 | 
|  | #   SCR0  - base of virtual range covered by cached PGE from last ITLB miss (or 0xffffffff) | 
|  | #   DAMR3 - mapped page directory | 
|  | #   DAMR4 - mapped page table as matched by SCR0 | 
|  | # | 
|  | ############################################################################### | 
|  | .globl		__entry_user_insn_tlb_miss | 
|  | .type		__entry_user_insn_tlb_miss,@function | 
|  | __entry_user_insn_tlb_miss: | 
|  | #if 0 | 
|  | sethi.p		%hi(0xe1200004),gr30 | 
|  | setlo		%lo(0xe1200004),gr30 | 
|  | st		gr0,@(gr30,gr0) | 
|  | sethi.p		%hi(0xffc00100),gr30 | 
|  | setlo		%lo(0xffc00100),gr30 | 
|  | sth		gr30,@(gr30,gr0) | 
|  | membar | 
|  | #endif | 
|  |  | 
|  | movsg		ccr,gr30			/* save CCR */ | 
|  | movgs		gr30,scr2 | 
|  |  | 
|  | # see if the cached page table mapping is appropriate | 
|  | srlicc.p	gr31,#26,gr0,icc0 | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bne		icc0,#0,__itlb_u_PTD_miss | 
|  |  | 
|  | __itlb_u_PTD_mapped: | 
|  | # access the PTD with EAR0[25:14] | 
|  | # - DAMLR4 points to the virtual address of the appropriate page table | 
|  | # - the PTD holds 4096 PTEs | 
|  | # - the PTD must be accessed uncached | 
|  | # - the PTE must be marked accessed if it was valid | 
|  | # | 
|  | and		gr31,gr30,gr31 | 
|  | movsg		damlr4,gr30 | 
|  | add		gr30,gr31,gr31 | 
|  | ldi		@(gr31,#0),gr30			/* fetch the PTE */ | 
|  | andicc		gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | ori.p		gr30,#_PAGE_ACCESSED,gr30 | 
|  | beq		icc0,#0,__tlb_user_fault	/* jump if PTE invalid */ | 
|  | sti.p		gr30,@(gr31,#0)			/* update the PTE */ | 
|  | andi		gr30,#~_PAGE_ACCESSED,gr30 | 
|  |  | 
|  | # we're using IAMR1/DAMR1 as an extra TLB entry | 
|  | # - punt the entry here (if valid) to the real TLB and then replace with the new PTE | 
|  | movsg		dampr1,gr31 | 
|  | andicc		gr31,#xAMPRx_V,gr0,icc0 | 
|  | setlos.p	0xfffff000,gr31 | 
|  | beq		icc0,#0,__itlb_u_nopunt		/* punt not required */ | 
|  |  | 
|  | movsg		dampr1,gr31 | 
|  | movgs		gr31,tppr | 
|  | movsg		damlr1,gr31			/* set TPLR.CXN */ | 
|  | movgs		gr31,tplr | 
|  | tlbpr		gr31,gr0,#2,#0			/* save to the TLB */ | 
|  | movsg		tpxr,gr31			/* check the TLB write error flag */ | 
|  | andicc.p	gr31,#TPXR_E,gr0,icc0 | 
|  | setlos		#0xfffff000,gr31 | 
|  | bne		icc0,#0,__tlb_user_fault | 
|  |  | 
|  | __itlb_u_nopunt: | 
|  |  | 
|  | # assemble the new TLB entry | 
|  | and		gr28,gr31,gr28 | 
|  | movsg		cxnr,gr31 | 
|  | or		gr28,gr31,gr28 | 
|  | movgs		gr28,iamlr1			/* xAMLR = address | context number */ | 
|  | movgs		gr30,iampr1 | 
|  | movgs		gr28,damlr1 | 
|  | movgs		gr30,dampr1 | 
|  |  | 
|  | # return, restoring registers | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | rett		#0 | 
|  | beq		icc0,#3,0			/* prevent icache prefetch */ | 
|  |  | 
|  | # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more | 
|  | # appropriate page table and map that instead | 
|  | #   - access the PGD with EAR0[31:26] | 
|  | #   - DAMLR3 points to the virtual address of the page directory | 
|  | #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables | 
|  | __itlb_u_PTD_miss: | 
|  | srli		gr28,#26,gr31			/* calculate PGE offset */ | 
|  | slli		gr31,#8,gr31			/* and clear bottom bits */ | 
|  |  | 
|  | movsg		damlr3,gr30 | 
|  | ld		@(gr31,gr30),gr30		/* access the PGE */ | 
|  |  | 
|  | andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | andicc		gr30,#xAMPRx_SS,gr0,icc1 | 
|  |  | 
|  | # map this PTD instead and record coverage address | 
|  | ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30 | 
|  | beq		icc0,#0,__tlb_user_fault	/* jump if PGE not present */ | 
|  | slli.p		gr31,#18,gr31 | 
|  | bne		icc1,#0,__itlb_u_bigpage | 
|  | movgs		gr30,dampr4 | 
|  | movgs		gr31,scr0 | 
|  |  | 
|  | # we can now resume normal service | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bra		__itlb_u_PTD_mapped | 
|  |  | 
|  | __itlb_u_bigpage: | 
|  | break | 
|  | nop | 
|  |  | 
|  | .size		__entry_user_insn_tlb_miss, .-__entry_user_insn_tlb_miss | 
|  |  | 
|  | ############################################################################### | 
|  | # | 
|  | # Userspace data TLB miss handler | 
|  | # On entry: | 
|  | #   GR28  - faulting address | 
|  | #   GR31  - EAR0 ^ SCR1 | 
|  | #   SCR1  - base of virtual range covered by cached PGE from last DTLB miss (or 0xffffffff) | 
|  | #   DAMR3 - mapped page directory | 
|  | #   DAMR5 - mapped page table as matched by SCR1 | 
|  | # | 
|  | ############################################################################### | 
|  | .globl		__entry_user_data_tlb_miss | 
|  | .type		__entry_user_data_tlb_miss,@function | 
|  | __entry_user_data_tlb_miss: | 
|  | #if 0 | 
|  | sethi.p		%hi(0xe1200004),gr30 | 
|  | setlo		%lo(0xe1200004),gr30 | 
|  | st		gr0,@(gr30,gr0) | 
|  | sethi.p		%hi(0xffc00100),gr30 | 
|  | setlo		%lo(0xffc00100),gr30 | 
|  | sth		gr30,@(gr30,gr0) | 
|  | membar | 
|  | #endif | 
|  |  | 
|  | movsg		ccr,gr30			/* save CCR */ | 
|  | movgs		gr30,scr2 | 
|  |  | 
|  | # see if the cached page table mapping is appropriate | 
|  | srlicc.p	gr31,#26,gr0,icc0 | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bne		icc0,#0,__dtlb_u_PTD_miss | 
|  |  | 
|  | __dtlb_u_PTD_mapped: | 
|  | # access the PTD with EAR0[25:14] | 
|  | # - DAMLR5 points to the virtual address of the appropriate page table | 
|  | # - the PTD holds 4096 PTEs | 
|  | # - the PTD must be accessed uncached | 
|  | # - the PTE must be marked accessed if it was valid | 
|  | # | 
|  | and		gr31,gr30,gr31 | 
|  | movsg		damlr5,gr30 | 
|  |  | 
|  | __dtlb_u_using_iPTD: | 
|  | add		gr30,gr31,gr31 | 
|  | ldi		@(gr31,#0),gr30			/* fetch the PTE */ | 
|  | andicc		gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | ori.p		gr30,#_PAGE_ACCESSED,gr30 | 
|  | beq		icc0,#0,__tlb_user_fault	/* jump if PTE invalid */ | 
|  | sti.p		gr30,@(gr31,#0)			/* update the PTE */ | 
|  | andi		gr30,#~_PAGE_ACCESSED,gr30 | 
|  |  | 
|  | # we're using DAMR1 as an extra TLB entry | 
|  | # - punt the entry here (if valid) to the real TLB and then replace with the new PTE | 
|  | movsg		dampr1,gr31 | 
|  | andicc		gr31,#xAMPRx_V,gr0,icc0 | 
|  | setlos.p	0xfffff000,gr31 | 
|  | beq		icc0,#0,__dtlb_u_nopunt		/* punt not required */ | 
|  |  | 
|  | movsg		dampr1,gr31 | 
|  | movgs		gr31,tppr | 
|  | movsg		damlr1,gr31			/* set TPLR.CXN */ | 
|  | movgs		gr31,tplr | 
|  | tlbpr		gr31,gr0,#2,#0			/* save to the TLB */ | 
|  | movsg		tpxr,gr31			/* check the TLB write error flag */ | 
|  | andicc.p	gr31,#TPXR_E,gr0,icc0 | 
|  | setlos		#0xfffff000,gr31 | 
|  | bne		icc0,#0,__tlb_user_fault | 
|  |  | 
|  | __dtlb_u_nopunt: | 
|  |  | 
|  | # assemble the new TLB entry | 
|  | and		gr28,gr31,gr28 | 
|  | movsg		cxnr,gr31 | 
|  | or		gr28,gr31,gr28 | 
|  | movgs		gr28,iamlr1			/* xAMLR = address | context number */ | 
|  | movgs		gr30,iampr1 | 
|  | movgs		gr28,damlr1 | 
|  | movgs		gr30,dampr1 | 
|  |  | 
|  | # return, restoring registers | 
|  | movsg		scr2,gr30 | 
|  | movgs		gr30,ccr | 
|  | rett		#0 | 
|  | beq		icc0,#3,0			/* prevent icache prefetch */ | 
|  |  | 
|  | # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more | 
|  | # appropriate page table and map that instead | 
|  | #   - first of all, check the insn PGE cache - we may well get a hit there | 
|  | #   - access the PGD with EAR0[31:26] | 
|  | #   - DAMLR3 points to the virtual address of the page directory | 
|  | #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables | 
|  | __dtlb_u_PTD_miss: | 
|  | movsg		scr0,gr31			/* consult the insn-PGE-cache key */ | 
|  | xor		gr28,gr31,gr31 | 
|  | srlicc		gr31,#26,gr0,icc0 | 
|  | srli		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bne		icc0,#0,__dtlb_u_iPGE_miss | 
|  |  | 
|  | # what we're looking for is covered by the insn-PGE-cache | 
|  | setlos		0x3ffc,gr30 | 
|  | and		gr31,gr30,gr31 | 
|  | movsg		damlr4,gr30 | 
|  | bra		__dtlb_u_using_iPTD | 
|  |  | 
|  | __dtlb_u_iPGE_miss: | 
|  | srli		gr28,#26,gr31			/* calculate PGE offset */ | 
|  | slli		gr31,#8,gr31			/* and clear bottom bits */ | 
|  |  | 
|  | movsg		damlr3,gr30 | 
|  | ld		@(gr31,gr30),gr30		/* access the PGE */ | 
|  |  | 
|  | andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0 | 
|  | andicc		gr30,#xAMPRx_SS,gr0,icc1 | 
|  |  | 
|  | # map this PTD instead and record coverage address | 
|  | ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30 | 
|  | beq		icc0,#0,__tlb_user_fault	/* jump if PGE not present */ | 
|  | slli.p		gr31,#18,gr31 | 
|  | bne		icc1,#0,__dtlb_u_bigpage | 
|  | movgs		gr30,dampr5 | 
|  | movgs		gr31,scr1 | 
|  |  | 
|  | # we can now resume normal service | 
|  | setlos		0x3ffc,gr30 | 
|  | srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */ | 
|  | bra		__dtlb_u_PTD_mapped | 
|  |  | 
|  | __dtlb_u_bigpage: | 
|  | break | 
|  | nop | 
|  |  | 
|  | .size		__entry_user_data_tlb_miss, .-__entry_user_data_tlb_miss |