| /* |
| * include/asm-xtensa/mmu_context.h |
| * |
| * Switch an MMU context. |
| * |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| * |
| * Copyright (C) 2001 - 2005 Tensilica Inc. |
| */ |
| |
| #ifndef _XTENSA_MMU_CONTEXT_H |
| #define _XTENSA_MMU_CONTEXT_H |
| |
| #include <linux/config.h> |
| #include <linux/stringify.h> |
| |
| #include <asm/pgtable.h> |
| #include <asm/mmu_context.h> |
| #include <asm/cacheflush.h> |
| #include <asm/tlbflush.h> |
| |
| /* |
| * Linux was ported to Xtensa assuming all auto-refill ways in set 0 |
| * had the same properties (a very likely assumption). Multiple sets |
| * of auto-refill ways will still work properly, but not as optimally |
| * as the Xtensa designer may have assumed. |
| * |
| * We make this case a hard #error, killing the kernel build, to alert |
| * the developer to this condition (which is more likely an error). |
| * You super-duper clever developers can change it to a warning or |
| * remove it altogether if you think you know what you're doing. :) |
| */ |
| |
| #if (XCHAL_HAVE_TLBS != 1) |
| # error "Linux must have an MMU!" |
| #endif |
| |
| #if ((XCHAL_ITLB_ARF_WAYS == 0) || (XCHAL_DTLB_ARF_WAYS == 0)) |
| # error "MMU must have auto-refill ways" |
| #endif |
| |
| #if ((XCHAL_ITLB_ARF_SETS != 1) || (XCHAL_DTLB_ARF_SETS != 1)) |
| # error Linux may not use all auto-refill ways as efficiently as you think |
| #endif |
| |
| #if (XCHAL_MMU_MAX_PTE_PAGE_SIZE != XCHAL_MMU_MIN_PTE_PAGE_SIZE) |
| # error Only one page size allowed! |
| #endif |
| |
| extern unsigned long asid_cache; |
| extern pgd_t *current_pgd; |
| |
| /* |
| * Define the number of entries per auto-refill way in set 0 of both I and D |
| * TLBs. We deal only with set 0 here (an assumption further explained in |
| * assertions.h). Also, define the total number of ARF entries in both TLBs. |
| */ |
| |
| #define ITLB_ENTRIES_PER_ARF_WAY (XCHAL_ITLB_SET(XCHAL_ITLB_ARF_SET0,ENTRIES)) |
| #define DTLB_ENTRIES_PER_ARF_WAY (XCHAL_DTLB_SET(XCHAL_DTLB_ARF_SET0,ENTRIES)) |
| |
| #define ITLB_ENTRIES \ |
| (ITLB_ENTRIES_PER_ARF_WAY * (XCHAL_ITLB_SET(XCHAL_ITLB_ARF_SET0,WAYS))) |
| #define DTLB_ENTRIES \ |
| (DTLB_ENTRIES_PER_ARF_WAY * (XCHAL_DTLB_SET(XCHAL_DTLB_ARF_SET0,WAYS))) |
| |
| |
| /* |
| * SMALLEST_NTLB_ENTRIES is the smaller of ITLB_ENTRIES and DTLB_ENTRIES. |
| * In practice, they are probably equal. This macro simplifies function |
| * flush_tlb_range(). |
| */ |
| |
| #if (DTLB_ENTRIES < ITLB_ENTRIES) |
| # define SMALLEST_NTLB_ENTRIES DTLB_ENTRIES |
| #else |
| # define SMALLEST_NTLB_ENTRIES ITLB_ENTRIES |
| #endif |
| |
| |
| /* |
| * asid_cache tracks only the ASID[USER_RING] field of the RASID special |
| * register, which is the current user-task asid allocation value. |
| * mm->context has the same meaning. When it comes time to write the |
| * asid_cache or mm->context values to the RASID special register, we first |
| * shift the value left by 8, then insert the value. |
| * ASID[0] always contains the kernel's asid value, and we reserve three |
| * other asid values that we never assign to user tasks. |
| */ |
| |
| #define ASID_INC 0x1 |
| #define ASID_MASK ((1 << XCHAL_MMU_ASID_BITS) - 1) |
| |
| /* |
| * XCHAL_MMU_ASID_INVALID is a configurable Xtensa processor constant |
| * indicating invalid address space. XCHAL_MMU_ASID_KERNEL is a configurable |
| * Xtensa processor constant indicating the kernel address space. They can |
| * be arbitrary values. |
| * |
| * We identify three more unique, reserved ASID values to use in the unused |
| * ring positions. No other user process will be assigned these reserved |
| * ASID values. |
| * |
| * For example, given that |
| * |
| * XCHAL_MMU_ASID_INVALID == 0 |
| * XCHAL_MMU_ASID_KERNEL == 1 |
| * |
| * the following maze of #if statements would generate |
| * |
| * ASID_RESERVED_1 == 2 |
| * ASID_RESERVED_2 == 3 |
| * ASID_RESERVED_3 == 4 |
| * ASID_FIRST_NONRESERVED == 5 |
| */ |
| |
| #if (XCHAL_MMU_ASID_INVALID != XCHAL_MMU_ASID_KERNEL + 1) |
| # define ASID_RESERVED_1 ((XCHAL_MMU_ASID_KERNEL + 1) & ASID_MASK) |
| #else |
| # define ASID_RESERVED_1 ((XCHAL_MMU_ASID_KERNEL + 2) & ASID_MASK) |
| #endif |
| |
| #if (XCHAL_MMU_ASID_INVALID != ASID_RESERVED_1 + 1) |
| # define ASID_RESERVED_2 ((ASID_RESERVED_1 + 1) & ASID_MASK) |
| #else |
| # define ASID_RESERVED_2 ((ASID_RESERVED_1 + 2) & ASID_MASK) |
| #endif |
| |
| #if (XCHAL_MMU_ASID_INVALID != ASID_RESERVED_2 + 1) |
| # define ASID_RESERVED_3 ((ASID_RESERVED_2 + 1) & ASID_MASK) |
| #else |
| # define ASID_RESERVED_3 ((ASID_RESERVED_2 + 2) & ASID_MASK) |
| #endif |
| |
| #if (XCHAL_MMU_ASID_INVALID != ASID_RESERVED_3 + 1) |
| # define ASID_FIRST_NONRESERVED ((ASID_RESERVED_3 + 1) & ASID_MASK) |
| #else |
| # define ASID_FIRST_NONRESERVED ((ASID_RESERVED_3 + 2) & ASID_MASK) |
| #endif |
| |
| #define ASID_ALL_RESERVED ( ((ASID_RESERVED_1) << 24) + \ |
| ((ASID_RESERVED_2) << 16) + \ |
| ((ASID_RESERVED_3) << 8) + \ |
| ((XCHAL_MMU_ASID_KERNEL)) ) |
| |
| |
| /* |
| * NO_CONTEXT is the invalid ASID value that we don't ever assign to |
| * any user or kernel context. NO_CONTEXT is a better mnemonic than |
| * XCHAL_MMU_ASID_INVALID, so we use it in code instead. |
| */ |
| |
| #define NO_CONTEXT XCHAL_MMU_ASID_INVALID |
| |
| #if (KERNEL_RING != 0) |
| # error The KERNEL_RING really should be zero. |
| #endif |
| |
| #if (USER_RING >= XCHAL_MMU_RINGS) |
| # error USER_RING cannot be greater than the highest numbered ring. |
| #endif |
| |
| #if (USER_RING == KERNEL_RING) |
| # error The user and kernel rings really should not be equal. |
| #endif |
| |
| #if (USER_RING == 1) |
| #define ASID_INSERT(x) ( ((ASID_RESERVED_1) << 24) + \ |
| ((ASID_RESERVED_2) << 16) + \ |
| (((x) & (ASID_MASK)) << 8) + \ |
| ((XCHAL_MMU_ASID_KERNEL)) ) |
| |
| #elif (USER_RING == 2) |
| #define ASID_INSERT(x) ( ((ASID_RESERVED_1) << 24) + \ |
| (((x) & (ASID_MASK)) << 16) + \ |
| ((ASID_RESERVED_2) << 8) + \ |
| ((XCHAL_MMU_ASID_KERNEL)) ) |
| |
| #elif (USER_RING == 3) |
| #define ASID_INSERT(x) ( (((x) & (ASID_MASK)) << 24) + \ |
| ((ASID_RESERVED_1) << 16) + \ |
| ((ASID_RESERVED_2) << 8) + \ |
| ((XCHAL_MMU_ASID_KERNEL)) ) |
| |
| #else |
| #error Goofy value for USER_RING |
| |
| #endif /* USER_RING == 1 */ |
| |
| |
| /* |
| * All unused by hardware upper bits will be considered |
| * as a software asid extension. |
| */ |
| |
| #define ASID_VERSION_MASK ((unsigned long)~(ASID_MASK|(ASID_MASK-1))) |
| #define ASID_FIRST_VERSION \ |
| ((unsigned long)(~ASID_VERSION_MASK) + 1 + ASID_FIRST_NONRESERVED) |
| |
| extern inline void set_rasid_register (unsigned long val) |
| { |
| __asm__ __volatile__ (" wsr %0, "__stringify(RASID)"\n\t" |
| " isync\n" : : "a" (val)); |
| } |
| |
| extern inline unsigned long get_rasid_register (void) |
| { |
| unsigned long tmp; |
| __asm__ __volatile__ (" rsr %0, "__stringify(RASID)"\n\t" : "=a" (tmp)); |
| return tmp; |
| } |
| |
| |
| #if ((XCHAL_MMU_ASID_INVALID == 0) && (XCHAL_MMU_ASID_KERNEL == 1)) |
| |
| extern inline void |
| get_new_mmu_context(struct mm_struct *mm, unsigned long asid) |
| { |
| extern void flush_tlb_all(void); |
| if (! ((asid += ASID_INC) & ASID_MASK) ) { |
| flush_tlb_all(); /* start new asid cycle */ |
| if (!asid) /* fix version if needed */ |
| asid = ASID_FIRST_VERSION - ASID_FIRST_NONRESERVED; |
| asid += ASID_FIRST_NONRESERVED; |
| } |
| mm->context = asid_cache = asid; |
| } |
| |
| #else |
| #warning ASID_{INVALID,KERNEL} values impose non-optimal get_new_mmu_context implementation |
| |
| /* XCHAL_MMU_ASID_INVALID == 0 and XCHAL_MMU_ASID_KERNEL ==1 are |
| really the best, but if you insist... */ |
| |
| extern inline int validate_asid (unsigned long asid) |
| { |
| switch (asid) { |
| case XCHAL_MMU_ASID_INVALID: |
| case XCHAL_MMU_ASID_KERNEL: |
| case ASID_RESERVED_1: |
| case ASID_RESERVED_2: |
| case ASID_RESERVED_3: |
| return 0; /* can't use these values as ASIDs */ |
| } |
| return 1; /* valid */ |
| } |
| |
| extern inline void |
| get_new_mmu_context(struct mm_struct *mm, unsigned long asid) |
| { |
| extern void flush_tlb_all(void); |
| while (1) { |
| asid += ASID_INC; |
| if ( ! (asid & ASID_MASK) ) { |
| flush_tlb_all(); /* start new asid cycle */ |
| if (!asid) /* fix version if needed */ |
| asid = ASID_FIRST_VERSION - ASID_FIRST_NONRESERVED; |
| asid += ASID_FIRST_NONRESERVED; |
| break; /* no need to validate here */ |
| } |
| if (validate_asid (asid & ASID_MASK)) |
| break; |
| } |
| mm->context = asid_cache = asid; |
| } |
| |
| #endif |
| |
| |
| /* |
| * Initialize the context related info for a new mm_struct |
| * instance. |
| */ |
| |
| extern inline int |
| init_new_context(struct task_struct *tsk, struct mm_struct *mm) |
| { |
| mm->context = NO_CONTEXT; |
| return 0; |
| } |
| |
| extern inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, |
| struct task_struct *tsk) |
| { |
| unsigned long asid = asid_cache; |
| |
| /* Check if our ASID is of an older version and thus invalid */ |
| |
| if ((next->context ^ asid) & ASID_VERSION_MASK) |
| get_new_mmu_context(next, asid); |
| |
| set_rasid_register (ASID_INSERT(next->context)); |
| invalidate_page_directory(); |
| } |
| |
| #define deactivate_mm(tsk, mm) do { } while(0) |
| |
| /* |
| * Destroy context related info for an mm_struct that is about |
| * to be put to rest. |
| */ |
| extern inline void destroy_context(struct mm_struct *mm) |
| { |
| /* Nothing to do. */ |
| } |
| |
| /* |
| * After we have set current->mm to a new value, this activates |
| * the context for the new mm so we see the new mappings. |
| */ |
| extern inline void |
| activate_mm(struct mm_struct *prev, struct mm_struct *next) |
| { |
| /* Unconditionally get a new ASID. */ |
| |
| get_new_mmu_context(next, asid_cache); |
| set_rasid_register (ASID_INSERT(next->context)); |
| invalidate_page_directory(); |
| } |
| |
| |
| static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) |
| { |
| /* Nothing to do. */ |
| |
| } |
| |
| #endif /* _XTENSA_MMU_CONTEXT_H */ |