diff options
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r-- | arch/powerpc/mm/hash_low_64.S | 5 | ||||
-rw-r--r-- | arch/powerpc/mm/hash_utils_64.c | 7 | ||||
-rw-r--r-- | arch/powerpc/mm/slb.c | 35 |
3 files changed, 40 insertions, 7 deletions
diff --git a/arch/powerpc/mm/hash_low_64.S b/arch/powerpc/mm/hash_low_64.S index ad253b95903..e935edd6b72 100644 --- a/arch/powerpc/mm/hash_low_64.S +++ b/arch/powerpc/mm/hash_low_64.S @@ -331,7 +331,7 @@ htab_pte_insert_failure: *****************************************************************************/ /* _hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid, - * pte_t *ptep, unsigned long trap, int local) + * pte_t *ptep, unsigned long trap, int local, int ssize) */ /* @@ -557,7 +557,8 @@ htab_inval_old_hpte: mr r4,r31 /* PTE.pte */ li r5,0 /* PTE.hidx */ li r6,MMU_PAGE_64K /* psize */ - ld r7,STK_PARM(r8)(r1) /* local */ + ld r7,STK_PARM(r9)(r1) /* ssize */ + ld r8,STK_PARM(r8)(r1) /* local */ bl .flush_hash_page b htab_insert_pte diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index c78dc912411..f09730bf3a3 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -51,6 +51,7 @@ #include <asm/cputable.h> #include <asm/sections.h> #include <asm/spu.h> +#include <asm/udbg.h> #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -791,8 +792,7 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap) } if (user_region) { if (psize != get_paca()->context.user_psize) { - get_paca()->context.user_psize = - mm->context.user_psize; + get_paca()->context = mm->context; slb_flush_and_rebolt(); } } else if (get_paca()->vmalloc_sllp != @@ -885,6 +885,9 @@ void hash_preload(struct mm_struct *mm, unsigned long ea, local_irq_restore(flags); } +/* WARNING: This is called from hash_low_64.S, if you change this prototype, + * do not forget to update the assembly call site ! + */ void flush_hash_page(unsigned long va, real_pte_t pte, int psize, int ssize, int local) { diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c index bbd2c512ee0..27922dff8b9 100644 --- a/arch/powerpc/mm/slb.c +++ b/arch/powerpc/mm/slb.c @@ -25,6 +25,7 @@ #include <asm/smp.h> #include <asm/firmware.h> #include <linux/compiler.h> +#include <asm/udbg.h> #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -148,6 +149,35 @@ void slb_vmalloc_update(void) slb_flush_and_rebolt(); } +/* Helper function to compare esids. There are four cases to handle. + * 1. The system is not 1T segment size capable. Use the GET_ESID compare. + * 2. The system is 1T capable, both addresses are < 1T, use the GET_ESID compare. + * 3. The system is 1T capable, only one of the two addresses is > 1T. This is not a match. + * 4. The system is 1T capable, both addresses are > 1T, use the GET_ESID_1T macro to compare. + */ +static inline int esids_match(unsigned long addr1, unsigned long addr2) +{ + int esid_1t_count; + + /* System is not 1T segment size capable. */ + if (!cpu_has_feature(CPU_FTR_1T_SEGMENT)) + return (GET_ESID(addr1) == GET_ESID(addr2)); + + esid_1t_count = (((addr1 >> SID_SHIFT_1T) != 0) + + ((addr2 >> SID_SHIFT_1T) != 0)); + + /* both addresses are < 1T */ + if (esid_1t_count == 0) + return (GET_ESID(addr1) == GET_ESID(addr2)); + + /* One address < 1T, the other > 1T. Not a match */ + if (esid_1t_count == 1) + return 0; + + /* Both addresses are > 1T. */ + return (GET_ESID_1T(addr1) == GET_ESID_1T(addr2)); +} + /* Flush all user entries from the segment table of the current processor. */ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) { @@ -193,15 +223,14 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) return; slb_allocate(pc); - if (GET_ESID(pc) == GET_ESID(stack)) + if (esids_match(pc,stack)) return; if (is_kernel_addr(stack)) return; slb_allocate(stack); - if ((GET_ESID(pc) == GET_ESID(unmapped_base)) - || (GET_ESID(stack) == GET_ESID(unmapped_base))) + if (esids_match(pc,unmapped_base) || esids_match(stack,unmapped_base)) return; if (is_kernel_addr(unmapped_base)) |