File: /usr/src/linux/include/asm-parisc/pgalloc.h

1     #ifndef _ASM_PGALLOC_H
2     #define _ASM_PGALLOC_H
3     
4     /* The usual comment is "Caches aren't brain-dead on the <architecture>".
5      * Unfortunately, that doesn't apply to PA-RISC. */
6     
7     #include <asm/processor.h>
8     #include <asm/fixmap.h>
9     #include <linux/threads.h>
10     
11     #include <asm/pgtable.h>
12     #include <asm/cache.h>
13     
14     
15     /* Internal use D/I cache flushing routines... */
16     /* XXX: these functions must not access memory between f[di]ce instructions. */
17     
18     static inline void __flush_dcache_range(unsigned long start, unsigned long size)
19     {
20     #if 0
21     	register unsigned long count = (size / L1_CACHE_BYTES);
22     	register unsigned long loop = cache_info.dc_loop;
23     	register unsigned long i, j;
24     
25     	if (size > 64 * 1024) {
26     		/* Just punt and clear the whole damn thing */
27     		flush_data_cache();
28     		return;
29     	}
30     
31     	for(i = 0; i <= count; i++, start += L1_CACHE_BYTES)
32     		for(j = 0; j < loop; j++)
33     			fdce(start);
34     #else
35     	flush_data_cache();
36     #endif
37     }
38     
39     
40     static inline void __flush_icache_range(unsigned long start, unsigned long size)
41     {
42     #if 0
43     	register unsigned long count = (size / L1_CACHE_BYTES);
44     	register unsigned long loop = cache_info.ic_loop;
45     	register unsigned long i, j;
46     
47     	if (size > 64 * 1024) {
48     		/* Just punt and clear the whole damn thing */
49     		flush_instruction_cache();
50     		return;
51     	}
52     
53     	for(i = 0; i <= count; i++, start += L1_CACHE_BYTES)
54     		for(j = 0; j < loop; j++)
55     			fice(start);
56     #else
57     	flush_instruction_cache();
58     #endif
59     }
60     
61     static inline void
62     flush_kernel_dcache_range(unsigned long start, unsigned long size)
63     {
64     	register unsigned long end = start + size;
65     	register unsigned long i;
66     
67     	start &= ~(L1_CACHE_BYTES - 1);
68     	for (i = start; i < end; i += L1_CACHE_BYTES) {
69     		kernel_fdc(i);
70     	}
71     	asm volatile("sync" : : );
72     	asm volatile("syncdma" : : );
73     }
74     
75     extern void __flush_page_to_ram(unsigned long address);
76     
77     #define flush_cache_all()			flush_all_caches()
78     #define flush_cache_mm(foo)			flush_all_caches()
79     
80     #if 0
81     /* This is how I think the cache flushing should be done -- mrw */
82     extern inline void flush_cache_mm(struct mm_struct *mm) {
83     	if (mm == current->mm) {
84     		flush_user_dcache_range(mm->start_data, mm->end_data);
85     		flush_user_icache_range(mm->start_code, mm->end_code);
86     	} else {
87     		flush_other_dcache_range(mm->context, mm->start_data, mm->end_data);
88     		flush_other_icache_range(mm->context, mm->start_code, mm->end_code);
89     	}
90     }
91     #endif
92     
93     #define flush_cache_range(mm, start, end) do { \
94                     __flush_dcache_range(start, (unsigned long)end - (unsigned long)start); \
95                     __flush_icache_range(start, (unsigned long)end - (unsigned long)start); \
96     } while(0)
97     
98     #define flush_cache_page(vma, vmaddr) do { \
99                     __flush_dcache_range(vmaddr, PAGE_SIZE); \
100                     __flush_icache_range(vmaddr, PAGE_SIZE); \
101     } while(0)
102     
103     #define flush_page_to_ram(page)	\
104             __flush_page_to_ram((unsigned long)page_address(page))
105     
106     #define flush_icache_range(start, end) \
107             __flush_icache_range(start, end - start)
108     
109     #define flush_icache_page(vma, page) \
110     	__flush_icache_range(page_address(page), PAGE_SIZE)
111     
112     #define flush_dcache_page(page) \
113     	__flush_dcache_range(page_address(page), PAGE_SIZE)
114     
115     /* TLB flushing routines.... */
116     
117     extern void flush_data_tlb(void);
118     extern void flush_instruction_tlb(void);
119     
120     #define flush_tlb() do { \
121             flush_data_tlb(); \
122     	flush_instruction_tlb(); \
123     } while(0);
124     
125     #define flush_tlb_all() 	flush_tlb()	/* XXX p[id]tlb */
126     
127     extern __inline__ void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end)
128     {
129     }
130      
131     static inline void flush_instruction_tlb_range(unsigned long start,
132     					unsigned long size)
133     {
134     #if 0
135     	register unsigned long count = (size / PAGE_SIZE);
136     	register unsigned long loop = cache_info.it_loop;
137     	register unsigned long i, j;
138     	
139     	for(i = 0; i <= count; i++, start += PAGE_SIZE)
140     		for(j = 0; j < loop; j++)
141     			pitlbe(start);
142     #else
143     	flush_instruction_tlb();
144     #endif
145     }
146     
147     static inline void flush_data_tlb_range(unsigned long start,
148     					unsigned long size)
149     {
150     #if 0
151     	register unsigned long count = (size / PAGE_SIZE);
152     	register unsigned long loop = cache_info.dt_loop;
153     	register unsigned long i, j;
154     	
155     	for(i = 0; i <= count; i++, start += PAGE_SIZE)
156     		for(j = 0; j < loop; j++)
157     			pdtlbe(start);
158     #else
159     	flush_data_tlb();
160     #endif
161     }
162     
163     
164     
165     static inline void __flush_tlb_range(unsigned long space, unsigned long start,
166     		       unsigned long size)
167     {
168     	unsigned long old_sr1;
169     
170     	if(!size)
171     		return;
172     
173     	old_sr1 = mfsp(1);
174     	mtsp(space, 1);
175     	
176     	flush_data_tlb_range(start, size);
177     	flush_instruction_tlb_range(start, size);
178     
179     	mtsp(old_sr1, 1);
180     }
181     
182     extern void __flush_tlb_space(unsigned long space);
183     
184     static inline void flush_tlb_mm(struct mm_struct *mm)
185     {
186     #if 0
187     	__flush_tlb_space(mm->context);
188     #else
189     	flush_tlb();
190     #endif
191     }
192     
193     static inline void flush_tlb_page(struct vm_area_struct *vma,
194     	unsigned long addr)
195     {
196     	__flush_tlb_range(vma->vm_mm->context, addr, PAGE_SIZE);
197     		
198     }
199     
200     static inline void flush_tlb_range(struct mm_struct *mm,
201     	unsigned long start, unsigned long end)
202     {
203     	__flush_tlb_range(mm->context, start, end - start);
204     }
205     
206     /*
207      * NOTE: Many of the below macros use PT_NLEVELS because
208      *       it is convenient that PT_NLEVELS == LOG2(pte size in bytes),
209      *       i.e. we use 3 level page tables when we use 8 byte pte's
210      *       (for 64 bit) and 2 level page tables when we use 4 byte pte's
211      */
212     
213     #ifdef __LP64__
214     #define PT_NLEVELS 3
215     #define PT_INITIAL 4 /* Number of initial page tables */
216     #else
217     #define PT_NLEVELS 2
218     #define PT_INITIAL 2 /* Number of initial page tables */
219     #endif
220     
221     /* Definitions for 1st level */
222     
223     #define PGDIR_SHIFT  (PAGE_SHIFT + (PT_NLEVELS - 1)*(PAGE_SHIFT - PT_NLEVELS))
224     #define PGDIR_SIZE	(1UL << PGDIR_SHIFT)
225     #define PGDIR_MASK	(~(PGDIR_SIZE-1))
226     #define PTRS_PER_PGD    (1UL << (PAGE_SHIFT - PT_NLEVELS))
227     #define USER_PTRS_PER_PGD	(TASK_SIZE/PGDIR_SIZE)
228     
229     /* Definitions for 2nd level */
230     
231     #define PMD_SHIFT       (PAGE_SHIFT + (PAGE_SHIFT - PT_NLEVELS))
232     #define PMD_SIZE	(1UL << PMD_SHIFT)
233     #define PMD_MASK	(~(PMD_SIZE-1))
234     #if PT_NLEVELS == 3
235     #define PTRS_PER_PMD    (1UL << (PAGE_SHIFT - PT_NLEVELS))
236     #else
237     #define PTRS_PER_PMD    1
238     #endif
239     
240     /* Definitions for 3rd level */
241     
242     #define PTRS_PER_PTE    (1UL << (PAGE_SHIFT - PT_NLEVELS))
243     
244     
245     #define get_pgd_fast get_pgd_slow
246     #define free_pgd_fast free_pgd_slow
247     
248     extern __inline__ pgd_t *get_pgd_slow(void)
249     {
250     	extern unsigned long gateway_pgd_offset;
251     	extern unsigned long gateway_pgd_entry;
252     	pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL);
253     
254     	if (ret) {
255     	    memset (ret, 0, PTRS_PER_PGD * sizeof(pgd_t));
256     
257     	    /* Install HP-UX and Linux gateway page translations */
258     
259     	    pgd_val(*(ret + gateway_pgd_offset)) = gateway_pgd_entry;
260     	}
261     	return ret;
262     }
263     
264     extern __inline__ void free_pgd_slow(pgd_t *pgd)
265     {
266     	free_page((unsigned long)pgd);
267     }
268     
269     #if PT_NLEVELS == 3
270     
271     /* Three Level Page Table Support for pmd's */
272     
273     extern __inline__ pmd_t *get_pmd_fast(void)
274     {
275     	return NULL; /* la la */
276     }
277     
278     #if 0
279     extern __inline__ void free_pmd_fast(pmd_t *pmd)
280     {
281     }
282     #else
283     #define free_pmd_fast free_pmd_slow
284     #endif
285     
286     extern __inline__ pmd_t *get_pmd_slow(void)
287     {
288     	pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
289     
290     	if (pmd)
291     		clear_page(pmd);
292     	return pmd;
293     }
294     
295     extern __inline__ void free_pmd_slow(pmd_t *pmd)
296     {
297     	free_page((unsigned long)pmd);
298     }
299     
300     extern void __bad_pgd(pgd_t *pgd);
301     
302     extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address)
303     {
304     	address = (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1);
305     
306     	if (pgd_none(*pgd))
307     		goto getnew;
308     	if (pgd_bad(*pgd))
309     		goto fix;
310     	return (pmd_t *) pgd_page(*pgd) + address;
311     getnew:
312     {
313     	pmd_t *page = get_pmd_fast();
314     	
315     	if (!page)
316     		page = get_pmd_slow();
317     	if (page) {
318     		if (pgd_none(*pgd)) {
319     		    pgd_val(*pgd) = _PAGE_TABLE + __pa((unsigned long)page);
320     		    return page + address;
321     		}
322     		else
323     		    free_pmd_fast(page);
324     	}
325     	else {
326     		return NULL;
327     	}
328     }
329     fix:
330     	__bad_pgd(pgd);
331     	return NULL;
332     }
333     
334     #else
335     
336     /* Two Level Page Table Support for pmd's */
337     
338     extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
339     {
340     	return (pmd_t *) pgd;
341     }
342     
343     extern inline void free_pmd_fast(pmd_t * pmd)
344     {
345     }
346     
347     #endif
348     
349     extern __inline__ pte_t *get_pte_fast(void)
350     {
351     	return NULL; /* la la */
352     }
353     
354     #if 0
355     extern __inline__ void free_pte_fast(pte_t *pte)
356     {
357     }
358     #else
359     #define free_pte_fast free_pte_slow
360     #endif
361     
362     extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted);
363     
364     extern __inline__ void free_pte_slow(pte_t *pte)
365     {
366     	free_page((unsigned long)pte);
367     }
368     
369     #define pmd_alloc_kernel	pmd_alloc
370     #define pte_alloc_kernel	pte_alloc
371     
372     #define pte_free(pte)		free_pte_fast(pte)
373     #define pmd_free(pmd)           free_pmd_fast(pmd)
374     #define pgd_free(pgd)		free_pgd_fast(pgd)
375     #define pgd_alloc(mm)		get_pgd_fast()
376     
377     extern void __bad_pmd(pmd_t *pmd);
378     
379     extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
380     {
381     	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
382     
383     	if (pmd_none(*pmd))
384     		goto getnew;
385     	if (pmd_bad(*pmd))
386     		goto fix;
387     	return (pte_t *) pmd_page(*pmd) + address;
388     getnew:
389     {
390     	pte_t *page = get_pte_fast();
391     	
392     	if (!page)
393     		return get_pte_slow(pmd, address);
394     	pmd_val(*pmd) = _PAGE_TABLE + __pa((unsigned long)page);
395     	return page + address;
396     }
397     fix:
398     	__bad_pmd(pmd);
399     	return NULL;
400     }
401     
402     extern int do_check_pgt_cache(int, int);
403     
404     #endif
405