| /* |
| * ppc64 MMU hashtable management routines |
| * |
| * (c) Copyright IBM Corp. 2003, 2005 |
| * |
| * Maintained by: Benjamin Herrenschmidt |
| * <benh@kernel.crashing.org> |
| * |
| * This file is covered by the GNU Public Licence v2 as |
| * described in the kernel's COPYING file. |
| */ |
| |
| #include <asm/reg.h> |
| #include <asm/pgtable.h> |
| #include <asm/mmu.h> |
| #include <asm/page.h> |
| #include <asm/types.h> |
| #include <asm/ppc_asm.h> |
| #include <asm/asm-offsets.h> |
| #include <asm/cputable.h> |
| |
| .text |
| |
| /* |
| * Stackframe: |
| * |
| * +-> Back chain (SP + 256) |
| * | General register save area (SP + 112) |
| * | Parameter save area (SP + 48) |
| * | TOC save area (SP + 40) |
| * | link editor doubleword (SP + 32) |
| * | compiler doubleword (SP + 24) |
| * | LR save area (SP + 16) |
| * | CR save area (SP + 8) |
| * SP ---> +-- Back chain (SP + 0) |
| */ |
| |
| #ifndef CONFIG_PPC_64K_PAGES |
| |
| /***************************************************************************** |
| * * |
| * 4K SW & 4K HW pages implementation * |
| * * |
| *****************************************************************************/ |
| |
| |
| /* |
| * _hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid, |
| * pte_t *ptep, unsigned long trap, int local, int ssize) |
| * |
| * Adds a 4K page to the hash table in a segment of 4K pages only |
| */ |
| |
| _GLOBAL(__hash_page_4K) |
| mflr r0 |
| std r0,16(r1) |
| stdu r1,-STACKFRAMESIZE(r1) |
| /* Save all params that we need after a function call */ |
| std r6,STK_PARAM(R6)(r1) |
| std r8,STK_PARAM(R8)(r1) |
| std r9,STK_PARAM(R9)(r1) |
| |
| /* Save non-volatile registers. |
| * r31 will hold "old PTE" |
| * r30 is "new PTE" |
| * r29 is vpn |
| * r28 is a hash value |
| * r27 is hashtab mask (maybe dynamic patched instead ?) |
| */ |
| std r27,STK_REG(R27)(r1) |
| std r28,STK_REG(R28)(r1) |
| std r29,STK_REG(R29)(r1) |
| std r30,STK_REG(R30)(r1) |
| std r31,STK_REG(R31)(r1) |
| |
| /* Step 1: |
| * |
| * Check permissions, atomically mark the linux PTE busy |
| * and hashed. |
| */ |
| 1: |
| ldarx r31,0,r6 |
| /* Check access rights (access & ~(pte_val(*ptep))) */ |
| andc. r0,r4,r31 |
| bne- htab_wrong_access |
| /* Check if PTE is busy */ |
| andi. r0,r31,_PAGE_BUSY |
| /* If so, just bail out and refault if needed. Someone else |
| * is changing this PTE anyway and might hash it. |
| */ |
| bne- htab_bail_ok |
| |
| /* Prepare new PTE value (turn access RW into DIRTY, then |
| * add BUSY,HASHPTE and ACCESSED) |
| */ |
| rlwinm r30,r4,32-9+7,31-7,31-7 /* _PAGE_RW -> _PAGE_DIRTY */ |
| or r30,r30,r31 |
| ori r30,r30,_PAGE_BUSY | _PAGE_ACCESSED | _PAGE_HASHPTE |
| /* Write the linux PTE atomically (setting busy) */ |
| stdcx. r30,0,r6 |
| bne- 1b |
| isync |
| |
| /* Step 2: |
| * |
| * Insert/Update the HPTE in the hash table. At this point, |
| * r4 (access) is re-useable, we use it for the new HPTE flags |
| */ |
| |
| BEGIN_FTR_SECTION |
| cmpdi r9,0 /* check segment size */ |
| bne 3f |
| END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) |
| /* Calc vpn and put it in r29 */ |
| sldi r29,r5,SID_SHIFT - VPN_SHIFT |
| rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT) |
| or r29,r28,r29 |
| /* |
| * Calculate hash value for primary slot and store it in r28 |
| * r3 = va, r5 = vsid |
| * r0 = (va >> 12) & ((1ul << (28 - 12)) -1) |
| */ |
| rldicl r0,r3,64-12,48 |
| xor r28,r5,r0 /* hash */ |
| b 4f |
| |
| 3: /* Calc vpn and put it in r29 */ |
| sldi r29,r5,SID_SHIFT_1T - VPN_SHIFT |
| rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT_1T - VPN_SHIFT) |
| or r29,r28,r29 |
| |
| /* |
| * calculate hash value for primary slot and |
| * store it in r28 for 1T segment |
| * r3 = va, r5 = vsid |
| */ |
| sldi r28,r5,25 /* vsid << 25 */ |
| /* r0 = (va >> 12) & ((1ul << (40 - 12)) -1) */ |
| rldicl r0,r3,64-12,36 |
| xor r28,r28,r5 /* vsid ^ ( vsid << 25) */ |
| xor r28,r28,r0 /* hash */ |
| |
| /* Convert linux PTE bits into HW equivalents */ |
| 4: andi. r3,r30,0x1fe /* Get basic set of flags */ |
| xori r3,r3,HPTE_R_N /* _PAGE_EXEC -> NOEXEC */ |
| rlwinm r0,r30,32-9+1,30,30 /* _PAGE_RW -> _PAGE_USER (r0) */ |
| rlwinm r4,r30,32-7+1,30,30 /* _PAGE_DIRTY -> _PAGE_USER (r4) */ |
| and r0,r0,r4 /* _PAGE_RW & _PAGE_DIRTY ->r0 bit 30*/ |
| andc r0,r30,r0 /* r0 = pte & ~r0 */ |
| rlwimi r3,r0,32-1,31,31 /* Insert result into PP lsb */ |
| /* |
| * Always add "C" bit for perf. Memory coherence is always enabled |
| */ |
| ori r3,r3,HPTE_R_C | HPTE_R_M |
| |
| /* We eventually do the icache sync here (maybe inline that |
| * code rather than call a C function...) |
| */ |
| BEGIN_FTR_SECTION |
| mr r4,r30 |
| mr r5,r7 |
| bl hash_page_do_lazy_icache |
| END_FTR_SECTION(CPU_FTR_NOEXECUTE|CPU_FTR_COHERENT_ICACHE, CPU_FTR_NOEXECUTE) |
| |
| /* At this point, r3 contains new PP bits, save them in |
| * place of "access" in the param area (sic) |
| */ |
| std r3,STK_PARAM(R4)(r1) |
| |
| /* Get htab_hash_mask */ |
| ld r4,htab_hash_mask@got(2) |
| ld r27,0(r4) /* htab_hash_mask -> r27 */ |
| |
| /* Check if we may already be in the hashtable, in this case, we |
| * go to out-of-line code to try to modify the HPTE |
| */ |
| andi. r0,r31,_PAGE_HASHPTE |
| bne htab_modify_pte |
| |
| htab_insert_pte: |
| /* Clear hpte bits in new pte (we also clear BUSY btw) and |
| * add _PAGE_HASHPTE |
| */ |
| lis r0,_PAGE_HPTEFLAGS@h |
| ori r0,r0,_PAGE_HPTEFLAGS@l |
| andc r30,r30,r0 |
| ori r30,r30,_PAGE_HASHPTE |
| |
| /* physical address r5 */ |
| rldicl r5,r31,64-PTE_RPN_SHIFT,PTE_RPN_SHIFT |
| sldi r5,r5,PAGE_SHIFT |
| |
| /* Calculate primary group hash */ |
| and r0,r28,r27 |
| rldicr r3,r0,3,63-3 /* r3 = (hash & mask) << 3 */ |
| |
| /* Call ppc_md.hpte_insert */ |
| ld r6,STK_PARAM(R4)(r1) /* Retrieve new pp bits */ |
| mr r4,r29 /* Retrieve vpn */ |
| li r7,0 /* !bolted, !secondary */ |
| li r8,MMU_PAGE_4K /* page size */ |
| li r9,MMU_PAGE_4K /* actual page size */ |
| ld r10,STK_PARAM(R9)(r1) /* segment size */ |
| .globl htab_call_hpte_insert1 |
| htab_call_hpte_insert1: |
| bl . /* Patched by htab_finish_init() */ |
| cmpdi 0,r3,0 |
| bge htab_pte_insert_ok /* Insertion successful */ |
| cmpdi 0,r3,-2 /* Critical failure */ |
| beq- htab_pte_insert_failure |
| |
| /* Now try secondary slot */ |
| |
| /* physical address r5 */ |
| rldicl r5,r31,64-PTE_RPN_SHIFT,PTE_RPN_SHIFT |
| sldi r5,r5,PAGE_SHIFT |
| |
| /* Calculate secondary group hash */ |
| andc r0,r27,r28 |
| rldicr r3,r0,3,63-3 /* r0 = (~hash & mask) << 3 */ |
| |
| /* Call ppc_md.hpte_insert */ |
| ld r6,STK_PARAM(R4)(r1) /* Retrieve new pp bits */ |
| mr r4,r29 /* Retrieve vpn */ |
| li r7,HPTE_V_SECONDARY /* !bolted, secondary */ |
| li r8,MMU_PAGE_4K /* page size */ |
| li r9,MMU_PAGE_4K /* actual page size */ |
| ld r10,STK_PARAM(R9)(r1) /* segment size */ |
| .globl htab_call_hpte_insert2 |
| htab_call_hpte_insert2: |
| bl . /* Patched by htab_finish_init() */ |
| cmpdi 0,r3,0 |
| bge+ htab_pte_insert_ok /* Insertion successful */ |
| cmpdi 0,r3,-2 /* Critical failure */ |
| beq- htab_pte_insert_failure |
| |
| /* Both are full, we need to evict something */ |
| mftb r0 |
| /* Pick a random group based on TB */ |
| andi. r0,r0,1 |
| mr r5,r28 |
| bne 2f |
| not r5,r5 |
| 2: and r0,r5,r27 |
| rldicr r3,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| /* Call ppc_md.hpte_remove */ |
| .globl htab_call_hpte_remove |
| htab_call_hpte_remove: |
| bl . /* Patched by htab_finish_init() */ |
| |
| /* Try all again */ |
| b htab_insert_pte |
| |
| htab_bail_ok: |
| li r3,0 |
| b htab_bail |
| |
| htab_pte_insert_ok: |
| /* Insert slot number & secondary bit in PTE */ |
| rldimi r30,r3,12,63-15 |
| |
| /* Write out the PTE with a normal write |
| * (maybe add eieio may be good still ?) |
| */ |
| htab_write_out_pte: |
| ld r6,STK_PARAM(R6)(r1) |
| std r30,0(r6) |
| li r3, 0 |
| htab_bail: |
| ld r27,STK_REG(R27)(r1) |
| ld r28,STK_REG(R28)(r1) |
| ld r29,STK_REG(R29)(r1) |
| ld r30,STK_REG(R30)(r1) |
| ld r31,STK_REG(R31)(r1) |
| addi r1,r1,STACKFRAMESIZE |
| ld r0,16(r1) |
| mtlr r0 |
| blr |
| |
| htab_modify_pte: |
| /* Keep PP bits in r4 and slot idx from the PTE around in r3 */ |
| mr r4,r3 |
| rlwinm r3,r31,32-12,29,31 |
| |
| /* Secondary group ? if yes, get a inverted hash value */ |
| mr r5,r28 |
| andi. r0,r31,_PAGE_SECONDARY |
| beq 1f |
| not r5,r5 |
| 1: |
| /* Calculate proper slot value for ppc_md.hpte_updatepp */ |
| and r0,r5,r27 |
| rldicr r0,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| add r3,r0,r3 /* add slot idx */ |
| |
| /* Call ppc_md.hpte_updatepp */ |
| mr r5,r29 /* vpn */ |
| li r6,MMU_PAGE_4K /* base page size */ |
| li r7,MMU_PAGE_4K /* actual page size */ |
| ld r8,STK_PARAM(R9)(r1) /* segment size */ |
| ld r9,STK_PARAM(R8)(r1) /* get "local" param */ |
| .globl htab_call_hpte_updatepp |
| htab_call_hpte_updatepp: |
| bl . /* Patched by htab_finish_init() */ |
| |
| /* if we failed because typically the HPTE wasn't really here |
| * we try an insertion. |
| */ |
| cmpdi 0,r3,-1 |
| beq- htab_insert_pte |
| |
| /* Clear the BUSY bit and Write out the PTE */ |
| li r0,_PAGE_BUSY |
| andc r30,r30,r0 |
| b htab_write_out_pte |
| |
| htab_wrong_access: |
| /* Bail out clearing reservation */ |
| stdcx. r31,0,r6 |
| li r3,1 |
| b htab_bail |
| |
| htab_pte_insert_failure: |
| /* Bail out restoring old PTE */ |
| ld r6,STK_PARAM(R6)(r1) |
| std r31,0(r6) |
| li r3,-1 |
| b htab_bail |
| |
| |
| #else /* CONFIG_PPC_64K_PAGES */ |
| |
| |
| /***************************************************************************** |
| * * |
| * 64K SW & 4K or 64K HW in a 4K segment pages implementation * |
| * * |
| *****************************************************************************/ |
| |
| /* _hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid, |
| * pte_t *ptep, unsigned long trap, int local, int ssize, |
| * int subpg_prot) |
| */ |
| |
| /* |
| * For now, we do NOT implement Admixed pages |
| */ |
| _GLOBAL(__hash_page_4K) |
| mflr r0 |
| std r0,16(r1) |
| stdu r1,-STACKFRAMESIZE(r1) |
| /* Save all params that we need after a function call */ |
| std r6,STK_PARAM(R6)(r1) |
| std r8,STK_PARAM(R8)(r1) |
| std r9,STK_PARAM(R9)(r1) |
| |
| /* Save non-volatile registers. |
| * r31 will hold "old PTE" |
| * r30 is "new PTE" |
| * r29 is vpn |
| * r28 is a hash value |
| * r27 is hashtab mask (maybe dynamic patched instead ?) |
| * r26 is the hidx mask |
| * r25 is the index in combo page |
| */ |
| std r25,STK_REG(R25)(r1) |
| std r26,STK_REG(R26)(r1) |
| std r27,STK_REG(R27)(r1) |
| std r28,STK_REG(R28)(r1) |
| std r29,STK_REG(R29)(r1) |
| std r30,STK_REG(R30)(r1) |
| std r31,STK_REG(R31)(r1) |
| |
| /* Step 1: |
| * |
| * Check permissions, atomically mark the linux PTE busy |
| * and hashed. |
| */ |
| 1: |
| ldarx r31,0,r6 |
| /* Check access rights (access & ~(pte_val(*ptep))) */ |
| andc. r0,r4,r31 |
| bne- htab_wrong_access |
| /* Check if PTE is busy */ |
| andi. r0,r31,_PAGE_BUSY |
| /* If so, just bail out and refault if needed. Someone else |
| * is changing this PTE anyway and might hash it. |
| */ |
| bne- htab_bail_ok |
| /* Prepare new PTE value (turn access RW into DIRTY, then |
| * add BUSY and ACCESSED) |
| */ |
| rlwinm r30,r4,32-9+7,31-7,31-7 /* _PAGE_RW -> _PAGE_DIRTY */ |
| or r30,r30,r31 |
| ori r30,r30,_PAGE_BUSY | _PAGE_ACCESSED |
| oris r30,r30,_PAGE_COMBO@h |
| /* Write the linux PTE atomically (setting busy) */ |
| stdcx. r30,0,r6 |
| bne- 1b |
| isync |
| |
| /* Step 2: |
| * |
| * Insert/Update the HPTE in the hash table. At this point, |
| * r4 (access) is re-useable, we use it for the new HPTE flags |
| */ |
| |
| /* Load the hidx index */ |
| rldicl r25,r3,64-12,60 |
| |
| BEGIN_FTR_SECTION |
| cmpdi r9,0 /* check segment size */ |
| bne 3f |
| END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) |
| /* Calc vpn and put it in r29 */ |
| sldi r29,r5,SID_SHIFT - VPN_SHIFT |
| /* |
| * clrldi r3,r3,64 - SID_SHIFT --> ea & 0xfffffff |
| * srdi r28,r3,VPN_SHIFT |
| */ |
| rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT) |
| or r29,r28,r29 |
| /* |
| * Calculate hash value for primary slot and store it in r28 |
| * r3 = va, r5 = vsid |
| * r0 = (va >> 12) & ((1ul << (28 - 12)) -1) |
| */ |
| rldicl r0,r3,64-12,48 |
| xor r28,r5,r0 /* hash */ |
| b 4f |
| |
| 3: /* Calc vpn and put it in r29 */ |
| sldi r29,r5,SID_SHIFT_1T - VPN_SHIFT |
| /* |
| * clrldi r3,r3,64 - SID_SHIFT_1T --> ea & 0xffffffffff |
| * srdi r28,r3,VPN_SHIFT |
| */ |
| rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT_1T - VPN_SHIFT) |
| or r29,r28,r29 |
| |
| /* |
| * Calculate hash value for primary slot and |
| * store it in r28 for 1T segment |
| * r3 = va, r5 = vsid |
| */ |
| sldi r28,r5,25 /* vsid << 25 */ |
| /* r0 = (va >> 12) & ((1ul << (40 - 12)) -1) */ |
| rldicl r0,r3,64-12,36 |
| xor r28,r28,r5 /* vsid ^ ( vsid << 25) */ |
| xor r28,r28,r0 /* hash */ |
| |
| /* Convert linux PTE bits into HW equivalents */ |
| 4: |
| #ifdef CONFIG_PPC_SUBPAGE_PROT |
| andc r10,r30,r10 |
| andi. r3,r10,0x1fe /* Get basic set of flags */ |
| rlwinm r0,r10,32-9+1,30,30 /* _PAGE_RW -> _PAGE_USER (r0) */ |
| #else |
| andi. r3,r30,0x1fe /* Get basic set of flags */ |
| rlwinm r0,r30,32-9+1,30,30 /* _PAGE_RW -> _PAGE_USER (r0) */ |
| #endif |
| xori r3,r3,HPTE_R_N /* _PAGE_EXEC -> NOEXEC */ |
| rlwinm r4,r30,32-7+1,30,30 /* _PAGE_DIRTY -> _PAGE_USER (r4) */ |
| and r0,r0,r4 /* _PAGE_RW & _PAGE_DIRTY ->r0 bit 30*/ |
| andc r0,r3,r0 /* r0 = pte & ~r0 */ |
| rlwimi r3,r0,32-1,31,31 /* Insert result into PP lsb */ |
| /* |
| * Always add "C" bit for perf. Memory coherence is always enabled |
| */ |
| ori r3,r3,HPTE_R_C | HPTE_R_M |
| |
| /* We eventually do the icache sync here (maybe inline that |
| * code rather than call a C function...) |
| */ |
| BEGIN_FTR_SECTION |
| mr r4,r30 |
| mr r5,r7 |
| bl hash_page_do_lazy_icache |
| END_FTR_SECTION(CPU_FTR_NOEXECUTE|CPU_FTR_COHERENT_ICACHE, CPU_FTR_NOEXECUTE) |
| |
| /* At this point, r3 contains new PP bits, save them in |
| * place of "access" in the param area (sic) |
| */ |
| std r3,STK_PARAM(R4)(r1) |
| |
| /* Get htab_hash_mask */ |
| ld r4,htab_hash_mask@got(2) |
| ld r27,0(r4) /* htab_hash_mask -> r27 */ |
| |
| /* Check if we may already be in the hashtable, in this case, we |
| * go to out-of-line code to try to modify the HPTE. We look for |
| * the bit at (1 >> (index + 32)) |
| */ |
| rldicl. r0,r31,64-12,48 |
| li r26,0 /* Default hidx */ |
| beq htab_insert_pte |
| |
| /* |
| * Check if the pte was already inserted into the hash table |
| * as a 64k HW page, and invalidate the 64k HPTE if so. |
| */ |
| andis. r0,r31,_PAGE_COMBO@h |
| beq htab_inval_old_hpte |
| |
| ld r6,STK_PARAM(R6)(r1) |
| ori r26,r6,PTE_PAGE_HIDX_OFFSET /* Load the hidx mask. */ |
| ld r26,0(r26) |
| addi r5,r25,36 /* Check actual HPTE_SUB bit, this */ |
| rldcr. r0,r31,r5,0 /* must match pgtable.h definition */ |
| bne htab_modify_pte |
| |
| htab_insert_pte: |
| /* real page number in r5, PTE RPN value + index */ |
| andis. r0,r31,_PAGE_4K_PFN@h |
| srdi r5,r31,PTE_RPN_SHIFT |
| bne- htab_special_pfn |
| sldi r5,r5,PAGE_FACTOR |
| add r5,r5,r25 |
| htab_special_pfn: |
| sldi r5,r5,HW_PAGE_SHIFT |
| |
| /* Calculate primary group hash */ |
| and r0,r28,r27 |
| rldicr r3,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| |
| /* Call ppc_md.hpte_insert */ |
| ld r6,STK_PARAM(R4)(r1) /* Retrieve new pp bits */ |
| mr r4,r29 /* Retrieve vpn */ |
| li r7,0 /* !bolted, !secondary */ |
| li r8,MMU_PAGE_4K /* page size */ |
| li r9,MMU_PAGE_4K /* actual page size */ |
| ld r10,STK_PARAM(R9)(r1) /* segment size */ |
| .globl htab_call_hpte_insert1 |
| htab_call_hpte_insert1: |
| bl . /* patched by htab_finish_init() */ |
| cmpdi 0,r3,0 |
| bge htab_pte_insert_ok /* Insertion successful */ |
| cmpdi 0,r3,-2 /* Critical failure */ |
| beq- htab_pte_insert_failure |
| |
| /* Now try secondary slot */ |
| |
| /* real page number in r5, PTE RPN value + index */ |
| andis. r0,r31,_PAGE_4K_PFN@h |
| srdi r5,r31,PTE_RPN_SHIFT |
| bne- 3f |
| sldi r5,r5,PAGE_FACTOR |
| add r5,r5,r25 |
| 3: sldi r5,r5,HW_PAGE_SHIFT |
| |
| /* Calculate secondary group hash */ |
| andc r0,r27,r28 |
| rldicr r3,r0,3,63-3 /* r0 = (~hash & mask) << 3 */ |
| |
| /* Call ppc_md.hpte_insert */ |
| ld r6,STK_PARAM(R4)(r1) /* Retrieve new pp bits */ |
| mr r4,r29 /* Retrieve vpn */ |
| li r7,HPTE_V_SECONDARY /* !bolted, secondary */ |
| li r8,MMU_PAGE_4K /* page size */ |
| li r9,MMU_PAGE_4K /* actual page size */ |
| ld r10,STK_PARAM(R9)(r1) /* segment size */ |
| .globl htab_call_hpte_insert2 |
| htab_call_hpte_insert2: |
| bl . /* patched by htab_finish_init() */ |
| cmpdi 0,r3,0 |
| bge+ htab_pte_insert_ok /* Insertion successful */ |
| cmpdi 0,r3,-2 /* Critical failure */ |
| beq- htab_pte_insert_failure |
| |
| /* Both are full, we need to evict something */ |
| mftb r0 |
| /* Pick a random group based on TB */ |
| andi. r0,r0,1 |
| mr r5,r28 |
| bne 2f |
| not r5,r5 |
| 2: and r0,r5,r27 |
| rldicr r3,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| /* Call ppc_md.hpte_remove */ |
| .globl htab_call_hpte_remove |
| htab_call_hpte_remove: |
| bl . /* patched by htab_finish_init() */ |
| |
| /* Try all again */ |
| b htab_insert_pte |
| |
| /* |
| * Call out to C code to invalidate an 64k HW HPTE that is |
| * useless now that the segment has been switched to 4k pages. |
| */ |
| htab_inval_old_hpte: |
| mr r3,r29 /* vpn */ |
| mr r4,r31 /* PTE.pte */ |
| li r5,0 /* PTE.hidx */ |
| li r6,MMU_PAGE_64K /* psize */ |
| ld r7,STK_PARAM(R9)(r1) /* ssize */ |
| ld r8,STK_PARAM(R8)(r1) /* local */ |
| bl flush_hash_page |
| /* Clear out _PAGE_HPTE_SUB bits in the new linux PTE */ |
| lis r0,_PAGE_HPTE_SUB@h |
| ori r0,r0,_PAGE_HPTE_SUB@l |
| andc r30,r30,r0 |
| b htab_insert_pte |
| |
| htab_bail_ok: |
| li r3,0 |
| b htab_bail |
| |
| htab_pte_insert_ok: |
| /* Insert slot number & secondary bit in PTE second half, |
| * clear _PAGE_BUSY and set approriate HPTE slot bit |
| */ |
| ld r6,STK_PARAM(R6)(r1) |
| li r0,_PAGE_BUSY |
| andc r30,r30,r0 |
| /* HPTE SUB bit */ |
| li r0,1 |
| subfic r5,r25,27 /* Must match bit position in */ |
| sld r0,r0,r5 /* pgtable.h */ |
| or r30,r30,r0 |
| /* hindx */ |
| sldi r5,r25,2 |
| sld r3,r3,r5 |
| li r4,0xf |
| sld r4,r4,r5 |
| andc r26,r26,r4 |
| or r26,r26,r3 |
| ori r5,r6,PTE_PAGE_HIDX_OFFSET |
| std r26,0(r5) |
| lwsync |
| std r30,0(r6) |
| li r3, 0 |
| htab_bail: |
| ld r25,STK_REG(R25)(r1) |
| ld r26,STK_REG(R26)(r1) |
| ld r27,STK_REG(R27)(r1) |
| ld r28,STK_REG(R28)(r1) |
| ld r29,STK_REG(R29)(r1) |
| ld r30,STK_REG(R30)(r1) |
| ld r31,STK_REG(R31)(r1) |
| addi r1,r1,STACKFRAMESIZE |
| ld r0,16(r1) |
| mtlr r0 |
| blr |
| |
| htab_modify_pte: |
| /* Keep PP bits in r4 and slot idx from the PTE around in r3 */ |
| mr r4,r3 |
| sldi r5,r25,2 |
| srd r3,r26,r5 |
| |
| /* Secondary group ? if yes, get a inverted hash value */ |
| mr r5,r28 |
| andi. r0,r3,0x8 /* page secondary ? */ |
| beq 1f |
| not r5,r5 |
| 1: andi. r3,r3,0x7 /* extract idx alone */ |
| |
| /* Calculate proper slot value for ppc_md.hpte_updatepp */ |
| and r0,r5,r27 |
| rldicr r0,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| add r3,r0,r3 /* add slot idx */ |
| |
| /* Call ppc_md.hpte_updatepp */ |
| mr r5,r29 /* vpn */ |
| li r6,MMU_PAGE_4K /* base page size */ |
| li r7,MMU_PAGE_4K /* actual page size */ |
| ld r8,STK_PARAM(R9)(r1) /* segment size */ |
| ld r9,STK_PARAM(R8)(r1) /* get "local" param */ |
| .globl htab_call_hpte_updatepp |
| htab_call_hpte_updatepp: |
| bl . /* patched by htab_finish_init() */ |
| |
| /* if we failed because typically the HPTE wasn't really here |
| * we try an insertion. |
| */ |
| cmpdi 0,r3,-1 |
| beq- htab_insert_pte |
| |
| /* Clear the BUSY bit and Write out the PTE */ |
| li r0,_PAGE_BUSY |
| andc r30,r30,r0 |
| ld r6,STK_PARAM(R6)(r1) |
| std r30,0(r6) |
| li r3,0 |
| b htab_bail |
| |
| htab_wrong_access: |
| /* Bail out clearing reservation */ |
| stdcx. r31,0,r6 |
| li r3,1 |
| b htab_bail |
| |
| htab_pte_insert_failure: |
| /* Bail out restoring old PTE */ |
| ld r6,STK_PARAM(R6)(r1) |
| std r31,0(r6) |
| li r3,-1 |
| b htab_bail |
| |
| #endif /* CONFIG_PPC_64K_PAGES */ |
| |
| #ifdef CONFIG_PPC_HAS_HASH_64K |
| |
| /***************************************************************************** |
| * * |
| * 64K SW & 64K HW in a 64K segment pages implementation * |
| * * |
| *****************************************************************************/ |
| |
| _GLOBAL(__hash_page_64K) |
| mflr r0 |
| std r0,16(r1) |
| stdu r1,-STACKFRAMESIZE(r1) |
| /* Save all params that we need after a function call */ |
| std r6,STK_PARAM(R6)(r1) |
| std r8,STK_PARAM(R8)(r1) |
| std r9,STK_PARAM(R9)(r1) |
| |
| /* Save non-volatile registers. |
| * r31 will hold "old PTE" |
| * r30 is "new PTE" |
| * r29 is vpn |
| * r28 is a hash value |
| * r27 is hashtab mask (maybe dynamic patched instead ?) |
| */ |
| std r27,STK_REG(R27)(r1) |
| std r28,STK_REG(R28)(r1) |
| std r29,STK_REG(R29)(r1) |
| std r30,STK_REG(R30)(r1) |
| std r31,STK_REG(R31)(r1) |
| |
| /* Step 1: |
| * |
| * Check permissions, atomically mark the linux PTE busy |
| * and hashed. |
| */ |
| 1: |
| ldarx r31,0,r6 |
| /* Check access rights (access & ~(pte_val(*ptep))) */ |
| andc. r0,r4,r31 |
| bne- ht64_wrong_access |
| /* Check if PTE is busy */ |
| andi. r0,r31,_PAGE_BUSY |
| /* If so, just bail out and refault if needed. Someone else |
| * is changing this PTE anyway and might hash it. |
| */ |
| bne- ht64_bail_ok |
| BEGIN_FTR_SECTION |
| /* Check if PTE has the cache-inhibit bit set */ |
| andi. r0,r31,_PAGE_NO_CACHE |
| /* If so, bail out and refault as a 4k page */ |
| bne- ht64_bail_ok |
| END_MMU_FTR_SECTION_IFCLR(MMU_FTR_CI_LARGE_PAGE) |
| /* Prepare new PTE value (turn access RW into DIRTY, then |
| * add BUSY and ACCESSED) |
| */ |
| rlwinm r30,r4,32-9+7,31-7,31-7 /* _PAGE_RW -> _PAGE_DIRTY */ |
| or r30,r30,r31 |
| ori r30,r30,_PAGE_BUSY | _PAGE_ACCESSED |
| /* Write the linux PTE atomically (setting busy) */ |
| stdcx. r30,0,r6 |
| bne- 1b |
| isync |
| |
| /* Step 2: |
| * |
| * Insert/Update the HPTE in the hash table. At this point, |
| * r4 (access) is re-useable, we use it for the new HPTE flags |
| */ |
| |
| BEGIN_FTR_SECTION |
| cmpdi r9,0 /* check segment size */ |
| bne 3f |
| END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) |
| /* Calc vpn and put it in r29 */ |
| sldi r29,r5,SID_SHIFT - VPN_SHIFT |
| rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT) |
| or r29,r28,r29 |
| |
| /* Calculate hash value for primary slot and store it in r28 |
| * r3 = va, r5 = vsid |
| * r0 = (va >> 16) & ((1ul << (28 - 16)) -1) |
| */ |
| rldicl r0,r3,64-16,52 |
| xor r28,r5,r0 /* hash */ |
| b 4f |
| |
| 3: /* Calc vpn and put it in r29 */ |
| sldi r29,r5,SID_SHIFT_1T - VPN_SHIFT |
| rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT_1T - VPN_SHIFT) |
| or r29,r28,r29 |
| /* |
| * calculate hash value for primary slot and |
| * store it in r28 for 1T segment |
| * r3 = va, r5 = vsid |
| */ |
| sldi r28,r5,25 /* vsid << 25 */ |
| /* r0 = (va >> 16) & ((1ul << (40 - 16)) -1) */ |
| rldicl r0,r3,64-16,40 |
| xor r28,r28,r5 /* vsid ^ ( vsid << 25) */ |
| xor r28,r28,r0 /* hash */ |
| |
| /* Convert linux PTE bits into HW equivalents */ |
| 4: andi. r3,r30,0x1fe /* Get basic set of flags */ |
| xori r3,r3,HPTE_R_N /* _PAGE_EXEC -> NOEXEC */ |
| rlwinm r0,r30,32-9+1,30,30 /* _PAGE_RW -> _PAGE_USER (r0) */ |
| rlwinm r4,r30,32-7+1,30,30 /* _PAGE_DIRTY -> _PAGE_USER (r4) */ |
| and r0,r0,r4 /* _PAGE_RW & _PAGE_DIRTY ->r0 bit 30*/ |
| andc r0,r30,r0 /* r0 = pte & ~r0 */ |
| rlwimi r3,r0,32-1,31,31 /* Insert result into PP lsb */ |
| /* |
| * Always add "C" bit for perf. Memory coherence is always enabled |
| */ |
| ori r3,r3,HPTE_R_C | HPTE_R_M |
| |
| /* We eventually do the icache sync here (maybe inline that |
| * code rather than call a C function...) |
| */ |
| BEGIN_FTR_SECTION |
| mr r4,r30 |
| mr r5,r7 |
| bl hash_page_do_lazy_icache |
| END_FTR_SECTION(CPU_FTR_NOEXECUTE|CPU_FTR_COHERENT_ICACHE, CPU_FTR_NOEXECUTE) |
| |
| /* At this point, r3 contains new PP bits, save them in |
| * place of "access" in the param area (sic) |
| */ |
| std r3,STK_PARAM(R4)(r1) |
| |
| /* Get htab_hash_mask */ |
| ld r4,htab_hash_mask@got(2) |
| ld r27,0(r4) /* htab_hash_mask -> r27 */ |
| |
| /* Check if we may already be in the hashtable, in this case, we |
| * go to out-of-line code to try to modify the HPTE |
| */ |
| rldicl. r0,r31,64-12,48 |
| bne ht64_modify_pte |
| |
| ht64_insert_pte: |
| /* Clear hpte bits in new pte (we also clear BUSY btw) and |
| * add _PAGE_HPTE_SUB0 |
| */ |
| lis r0,_PAGE_HPTEFLAGS@h |
| ori r0,r0,_PAGE_HPTEFLAGS@l |
| andc r30,r30,r0 |
| #ifdef CONFIG_PPC_64K_PAGES |
| oris r30,r30,_PAGE_HPTE_SUB0@h |
| #else |
| ori r30,r30,_PAGE_HASHPTE |
| #endif |
| /* Phyical address in r5 */ |
| rldicl r5,r31,64-PTE_RPN_SHIFT,PTE_RPN_SHIFT |
| sldi r5,r5,PAGE_SHIFT |
| |
| /* Calculate primary group hash */ |
| and r0,r28,r27 |
| rldicr r3,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| |
| /* Call ppc_md.hpte_insert */ |
| ld r6,STK_PARAM(R4)(r1) /* Retrieve new pp bits */ |
| mr r4,r29 /* Retrieve vpn */ |
| li r7,0 /* !bolted, !secondary */ |
| li r8,MMU_PAGE_64K |
| li r9,MMU_PAGE_64K /* actual page size */ |
| ld r10,STK_PARAM(R9)(r1) /* segment size */ |
| .globl ht64_call_hpte_insert1 |
| ht64_call_hpte_insert1: |
| bl . /* patched by htab_finish_init() */ |
| cmpdi 0,r3,0 |
| bge ht64_pte_insert_ok /* Insertion successful */ |
| cmpdi 0,r3,-2 /* Critical failure */ |
| beq- ht64_pte_insert_failure |
| |
| /* Now try secondary slot */ |
| |
| /* Phyical address in r5 */ |
| rldicl r5,r31,64-PTE_RPN_SHIFT,PTE_RPN_SHIFT |
| sldi r5,r5,PAGE_SHIFT |
| |
| /* Calculate secondary group hash */ |
| andc r0,r27,r28 |
| rldicr r3,r0,3,63-3 /* r0 = (~hash & mask) << 3 */ |
| |
| /* Call ppc_md.hpte_insert */ |
| ld r6,STK_PARAM(R4)(r1) /* Retrieve new pp bits */ |
| mr r4,r29 /* Retrieve vpn */ |
| li r7,HPTE_V_SECONDARY /* !bolted, secondary */ |
| li r8,MMU_PAGE_64K |
| li r9,MMU_PAGE_64K /* actual page size */ |
| ld r10,STK_PARAM(R9)(r1) /* segment size */ |
| .globl ht64_call_hpte_insert2 |
| ht64_call_hpte_insert2: |
| bl . /* patched by htab_finish_init() */ |
| cmpdi 0,r3,0 |
| bge+ ht64_pte_insert_ok /* Insertion successful */ |
| cmpdi 0,r3,-2 /* Critical failure */ |
| beq- ht64_pte_insert_failure |
| |
| /* Both are full, we need to evict something */ |
| mftb r0 |
| /* Pick a random group based on TB */ |
| andi. r0,r0,1 |
| mr r5,r28 |
| bne 2f |
| not r5,r5 |
| 2: and r0,r5,r27 |
| rldicr r3,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| /* Call ppc_md.hpte_remove */ |
| .globl ht64_call_hpte_remove |
| ht64_call_hpte_remove: |
| bl . /* patched by htab_finish_init() */ |
| |
| /* Try all again */ |
| b ht64_insert_pte |
| |
| ht64_bail_ok: |
| li r3,0 |
| b ht64_bail |
| |
| ht64_pte_insert_ok: |
| /* Insert slot number & secondary bit in PTE */ |
| rldimi r30,r3,12,63-15 |
| |
| /* Write out the PTE with a normal write |
| * (maybe add eieio may be good still ?) |
| */ |
| ht64_write_out_pte: |
| ld r6,STK_PARAM(R6)(r1) |
| std r30,0(r6) |
| li r3, 0 |
| ht64_bail: |
| ld r27,STK_REG(R27)(r1) |
| ld r28,STK_REG(R28)(r1) |
| ld r29,STK_REG(R29)(r1) |
| ld r30,STK_REG(R30)(r1) |
| ld r31,STK_REG(R31)(r1) |
| addi r1,r1,STACKFRAMESIZE |
| ld r0,16(r1) |
| mtlr r0 |
| blr |
| |
| ht64_modify_pte: |
| /* Keep PP bits in r4 and slot idx from the PTE around in r3 */ |
| mr r4,r3 |
| rlwinm r3,r31,32-12,29,31 |
| |
| /* Secondary group ? if yes, get a inverted hash value */ |
| mr r5,r28 |
| andi. r0,r31,_PAGE_F_SECOND |
| beq 1f |
| not r5,r5 |
| 1: |
| /* Calculate proper slot value for ppc_md.hpte_updatepp */ |
| and r0,r5,r27 |
| rldicr r0,r0,3,63-3 /* r0 = (hash & mask) << 3 */ |
| add r3,r0,r3 /* add slot idx */ |
| |
| /* Call ppc_md.hpte_updatepp */ |
| mr r5,r29 /* vpn */ |
| li r6,MMU_PAGE_64K /* base page size */ |
| li r7,MMU_PAGE_64K /* actual page size */ |
| ld r8,STK_PARAM(R9)(r1) /* segment size */ |
| ld r9,STK_PARAM(R8)(r1) /* get "local" param */ |
| .globl ht64_call_hpte_updatepp |
| ht64_call_hpte_updatepp: |
| bl . /* patched by htab_finish_init() */ |
| |
| /* if we failed because typically the HPTE wasn't really here |
| * we try an insertion. |
| */ |
| cmpdi 0,r3,-1 |
| beq- ht64_insert_pte |
| |
| /* Clear the BUSY bit and Write out the PTE */ |
| li r0,_PAGE_BUSY |
| andc r30,r30,r0 |
| b ht64_write_out_pte |
| |
| ht64_wrong_access: |
| /* Bail out clearing reservation */ |
| stdcx. r31,0,r6 |
| li r3,1 |
| b ht64_bail |
| |
| ht64_pte_insert_failure: |
| /* Bail out restoring old PTE */ |
| ld r6,STK_PARAM(R6)(r1) |
| std r31,0(r6) |
| li r3,-1 |
| b ht64_bail |
| |
| |
| #endif /* CONFIG_PPC_HAS_HASH_64K */ |
| |
| |
| /***************************************************************************** |
| * * |
| * Huge pages implementation is in hugetlbpage.c * |
| * * |
| *****************************************************************************/ |