| #ifndef _ASM_PGALLOC_H |
| #define _ASM_PGALLOC_H |
| |
| /* The usual comment is "Caches aren't brain-dead on the <architecture>". |
| * Unfortunately, that doesn't apply to PA-RISC. */ |
| |
| #include <asm/processor.h> |
| #include <asm/fixmap.h> |
| #include <linux/threads.h> |
| |
| #include <asm/pgtable.h> |
| #include <asm/cache.h> |
| |
| |
| /* Internal use D/I cache flushing routines... */ |
| /* XXX: these functions must not access memory between f[di]ce instructions. */ |
| |
| static inline void __flush_dcache_range(unsigned long start, unsigned long size) |
| { |
| #if 0 |
| register unsigned long count = (size / L1_CACHE_BYTES); |
| register unsigned long loop = cache_info.dc_loop; |
| register unsigned long i, j; |
| |
| if (size > 64 * 1024) { |
| /* Just punt and clear the whole damn thing */ |
| flush_data_cache(); |
| return; |
| } |
| |
| for(i = 0; i <= count; i++, start += L1_CACHE_BYTES) |
| for(j = 0; j < loop; j++) |
| fdce(start); |
| #else |
| flush_data_cache(); |
| #endif |
| } |
| |
| |
| static inline void __flush_icache_range(unsigned long start, unsigned long size) |
| { |
| #if 0 |
| register unsigned long count = (size / L1_CACHE_BYTES); |
| register unsigned long loop = cache_info.ic_loop; |
| register unsigned long i, j; |
| |
| if (size > 64 * 1024) { |
| /* Just punt and clear the whole damn thing */ |
| flush_instruction_cache(); |
| return; |
| } |
| |
| for(i = 0; i <= count; i++, start += L1_CACHE_BYTES) |
| for(j = 0; j < loop; j++) |
| fice(start); |
| #else |
| flush_instruction_cache(); |
| #endif |
| } |
| |
| static inline void |
| flush_kernel_dcache_range(unsigned long start, unsigned long size) |
| { |
| register unsigned long end = start + size; |
| register unsigned long i; |
| |
| start &= ~(L1_CACHE_BYTES - 1); |
| for (i = start; i < end; i += L1_CACHE_BYTES) { |
| kernel_fdc(i); |
| } |
| asm volatile("sync" : : ); |
| asm volatile("syncdma" : : ); |
| } |
| |
| extern void __flush_page_to_ram(unsigned long address); |
| |
| #define flush_cache_all() flush_all_caches() |
| #define flush_cache_mm(foo) flush_all_caches() |
| |
| #if 0 |
| /* This is how I think the cache flushing should be done -- mrw */ |
| extern inline void flush_cache_mm(struct mm_struct *mm) { |
| if (mm == current->mm) { |
| flush_user_dcache_range(mm->start_data, mm->end_data); |
| flush_user_icache_range(mm->start_code, mm->end_code); |
| } else { |
| flush_other_dcache_range(mm->context, mm->start_data, mm->end_data); |
| flush_other_icache_range(mm->context, mm->start_code, mm->end_code); |
| } |
| } |
| #endif |
| |
| #define flush_cache_range(vma, start, end) do { \ |
| __flush_dcache_range(start, (unsigned long)end - (unsigned long)start); \ |
| __flush_icache_range(start, (unsigned long)end - (unsigned long)start); \ |
| } while(0) |
| |
| #define flush_cache_page(vma, vmaddr) do { \ |
| __flush_dcache_range(vmaddr, PAGE_SIZE); \ |
| __flush_icache_range(vmaddr, PAGE_SIZE); \ |
| } while(0) |
| |
| #define flush_page_to_ram(page) \ |
| __flush_page_to_ram((unsigned long)page_address(page)) |
| |
| #define flush_icache_range(start, end) \ |
| __flush_icache_range(start, end - start) |
| |
| #define flush_icache_page(vma, page) \ |
| __flush_icache_range(page_address(page), PAGE_SIZE) |
| |
| #define flush_dcache_page(page) \ |
| __flush_dcache_range(page_address(page), PAGE_SIZE) |
| |
| /* TLB flushing routines.... */ |
| |
| extern void flush_data_tlb(void); |
| extern void flush_instruction_tlb(void); |
| |
| #define flush_tlb() do { \ |
| flush_data_tlb(); \ |
| flush_instruction_tlb(); \ |
| } while(0); |
| |
| #define flush_tlb_all() flush_tlb() /* XXX p[id]tlb */ |
| |
| extern __inline__ void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end) |
| { |
| } |
| |
| static inline void flush_instruction_tlb_range(unsigned long start, |
| unsigned long size) |
| { |
| #if 0 |
| register unsigned long count = (size / PAGE_SIZE); |
| register unsigned long loop = cache_info.it_loop; |
| register unsigned long i, j; |
| |
| for(i = 0; i <= count; i++, start += PAGE_SIZE) |
| for(j = 0; j < loop; j++) |
| pitlbe(start); |
| #else |
| flush_instruction_tlb(); |
| #endif |
| } |
| |
| static inline void flush_data_tlb_range(unsigned long start, |
| unsigned long size) |
| { |
| #if 0 |
| register unsigned long count = (size / PAGE_SIZE); |
| register unsigned long loop = cache_info.dt_loop; |
| register unsigned long i, j; |
| |
| for(i = 0; i <= count; i++, start += PAGE_SIZE) |
| for(j = 0; j < loop; j++) |
| pdtlbe(start); |
| #else |
| flush_data_tlb(); |
| #endif |
| } |
| |
| |
| |
| static inline void __flush_tlb_range(unsigned long space, unsigned long start, |
| unsigned long size) |
| { |
| unsigned long old_sr1; |
| |
| if(!size) |
| return; |
| |
| old_sr1 = mfsp(1); |
| mtsp(space, 1); |
| |
| flush_data_tlb_range(start, size); |
| flush_instruction_tlb_range(start, size); |
| |
| mtsp(old_sr1, 1); |
| } |
| |
| extern void __flush_tlb_space(unsigned long space); |
| |
| static inline void flush_tlb_mm(struct mm_struct *mm) |
| { |
| #if 0 |
| __flush_tlb_space(mm->context); |
| #else |
| flush_tlb(); |
| #endif |
| } |
| |
| static inline void flush_tlb_page(struct vm_area_struct *vma, |
| unsigned long addr) |
| { |
| __flush_tlb_range(vma->vm_mm->context, addr, PAGE_SIZE); |
| |
| } |
| |
| static inline void flush_tlb_range(struct vm_area_struct *vma, |
| unsigned long start, unsigned long end) |
| { |
| __flush_tlb_range(vma->vm_mm->context, start, end - start); |
| } |
| |
| /* |
| * NOTE: Many of the below macros use PT_NLEVELS because |
| * it is convenient that PT_NLEVELS == LOG2(pte size in bytes), |
| * i.e. we use 3 level page tables when we use 8 byte pte's |
| * (for 64 bit) and 2 level page tables when we use 4 byte pte's |
| */ |
| |
| #ifdef __LP64__ |
| #define PT_NLEVELS 3 |
| #define PT_INITIAL 4 /* Number of initial page tables */ |
| #else |
| #define PT_NLEVELS 2 |
| #define PT_INITIAL 2 /* Number of initial page tables */ |
| #endif |
| |
| /* Definitions for 1st level */ |
| |
| #define PGDIR_SHIFT (PAGE_SHIFT + (PT_NLEVELS - 1)*(PAGE_SHIFT - PT_NLEVELS)) |
| #define PGDIR_SIZE (1UL << PGDIR_SHIFT) |
| #define PGDIR_MASK (~(PGDIR_SIZE-1)) |
| #define PTRS_PER_PGD (1UL << (PAGE_SHIFT - PT_NLEVELS)) |
| #define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) |
| |
| /* Definitions for 2nd level */ |
| |
| #define PMD_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - PT_NLEVELS)) |
| #define PMD_SIZE (1UL << PMD_SHIFT) |
| #define PMD_MASK (~(PMD_SIZE-1)) |
| #if PT_NLEVELS == 3 |
| #define PTRS_PER_PMD (1UL << (PAGE_SHIFT - PT_NLEVELS)) |
| #else |
| #define PTRS_PER_PMD 1 |
| #endif |
| |
| /* Definitions for 3rd level */ |
| |
| #define PTRS_PER_PTE (1UL << (PAGE_SHIFT - PT_NLEVELS)) |
| |
| |
| #define get_pgd_fast get_pgd_slow |
| #define free_pgd_fast free_pgd_slow |
| |
| extern __inline__ pgd_t *get_pgd_slow(void) |
| { |
| extern unsigned long gateway_pgd_offset; |
| extern unsigned long gateway_pgd_entry; |
| pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL); |
| |
| if (ret) { |
| memset (ret, 0, PTRS_PER_PGD * sizeof(pgd_t)); |
| |
| /* Install HP-UX and Linux gateway page translations */ |
| |
| pgd_val(*(ret + gateway_pgd_offset)) = gateway_pgd_entry; |
| } |
| return ret; |
| } |
| |
| extern __inline__ void free_pgd_slow(pgd_t *pgd) |
| { |
| free_page((unsigned long)pgd); |
| } |
| |
| #if PT_NLEVELS == 3 |
| |
| /* Three Level Page Table Support for pmd's */ |
| |
| extern __inline__ pmd_t *get_pmd_fast(void) |
| { |
| return NULL; /* la la */ |
| } |
| |
| #if 0 |
| extern __inline__ void free_pmd_fast(pmd_t *pmd) |
| { |
| } |
| #else |
| #define free_pmd_fast free_pmd_slow |
| #endif |
| |
| extern __inline__ pmd_t *get_pmd_slow(void) |
| { |
| pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL); |
| |
| if (pmd) |
| clear_page(pmd); |
| return pmd; |
| } |
| |
| extern __inline__ void free_pmd_slow(pmd_t *pmd) |
| { |
| free_page((unsigned long)pmd); |
| } |
| |
| extern void __bad_pgd(pgd_t *pgd); |
| |
| extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address) |
| { |
| address = (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1); |
| |
| if (pgd_none(*pgd)) |
| goto getnew; |
| if (pgd_bad(*pgd)) |
| goto fix; |
| return (pmd_t *) pgd_page(*pgd) + address; |
| getnew: |
| { |
| pmd_t *page = get_pmd_fast(); |
| |
| if (!page) |
| page = get_pmd_slow(); |
| if (page) { |
| if (pgd_none(*pgd)) { |
| pgd_val(*pgd) = _PAGE_TABLE + __pa((unsigned long)page); |
| return page + address; |
| } |
| else |
| free_pmd_fast(page); |
| } |
| else { |
| return NULL; |
| } |
| } |
| fix: |
| __bad_pgd(pgd); |
| return NULL; |
| } |
| |
| #else |
| |
| /* Two Level Page Table Support for pmd's */ |
| |
| extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address) |
| { |
| return (pmd_t *) pgd; |
| } |
| |
| extern inline void free_pmd_fast(pmd_t * pmd) |
| { |
| } |
| |
| #endif |
| |
| extern __inline__ pte_t *get_pte_fast(void) |
| { |
| return NULL; /* la la */ |
| } |
| |
| #if 0 |
| extern __inline__ void free_pte_fast(pte_t *pte) |
| { |
| } |
| #else |
| #define free_pte_fast free_pte_slow |
| #endif |
| |
| extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted); |
| |
| extern __inline__ void free_pte_slow(pte_t *pte) |
| { |
| free_page((unsigned long)pte); |
| } |
| |
| #define pmd_alloc_kernel pmd_alloc |
| #define pte_alloc_kernel pte_alloc |
| |
| #define pte_free(pte) free_pte_fast(pte) |
| #define pmd_free(pmd) free_pmd_fast(pmd) |
| #define pgd_free(pgd) free_pgd_fast(pgd) |
| #define pgd_alloc(mm) get_pgd_fast() |
| |
| extern void __bad_pmd(pmd_t *pmd); |
| |
| extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address) |
| { |
| address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); |
| |
| if (pmd_none(*pmd)) |
| goto getnew; |
| if (pmd_bad(*pmd)) |
| goto fix; |
| return (pte_t *) pmd_page(*pmd) + address; |
| getnew: |
| { |
| pte_t *page = get_pte_fast(); |
| |
| if (!page) |
| return get_pte_slow(pmd, address); |
| pmd_val(*pmd) = _PAGE_TABLE + __pa((unsigned long)page); |
| return page + address; |
| } |
| fix: |
| __bad_pmd(pmd); |
| return NULL; |
| } |
| |
| extern int do_check_pgt_cache(int, int); |
| |
| #endif |