Linux-2.6.12-rc2
[safe/jmp/linux-2.6] / arch / alpha / mm / remap.c
1 #include <linux/vmalloc.h>
2 #include <asm/pgalloc.h>
3 #include <asm/cacheflush.h>
4
5 /* called with the page_table_lock held */
6 static inline void 
7 remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, 
8                unsigned long phys_addr, unsigned long flags)
9 {
10         unsigned long end;
11         unsigned long pfn;
12
13         address &= ~PMD_MASK;
14         end = address + size;
15         if (end > PMD_SIZE)
16                 end = PMD_SIZE;
17         if (address >= end)
18                 BUG();
19         pfn = phys_addr >> PAGE_SHIFT;
20         do {
21                 if (!pte_none(*pte)) {
22                         printk("remap_area_pte: page already exists\n");
23                         BUG();
24                 }
25                 set_pte(pte, pfn_pte(pfn, 
26                                      __pgprot(_PAGE_VALID | _PAGE_ASM | 
27                                               _PAGE_KRE | _PAGE_KWE | flags)));
28                 address += PAGE_SIZE;
29                 pfn++;
30                 pte++;
31         } while (address && (address < end));
32 }
33
34 /* called with the page_table_lock held */
35 static inline int 
36 remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, 
37                unsigned long phys_addr, unsigned long flags)
38 {
39         unsigned long end;
40
41         address &= ~PGDIR_MASK;
42         end = address + size;
43         if (end > PGDIR_SIZE)
44                 end = PGDIR_SIZE;
45         phys_addr -= address;
46         if (address >= end)
47                 BUG();
48         do {
49                 pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address);
50                 if (!pte)
51                         return -ENOMEM;
52                 remap_area_pte(pte, address, end - address, 
53                                      address + phys_addr, flags);
54                 address = (address + PMD_SIZE) & PMD_MASK;
55                 pmd++;
56         } while (address && (address < end));
57         return 0;
58 }
59
60 int
61 __alpha_remap_area_pages(unsigned long address, unsigned long phys_addr,
62                          unsigned long size, unsigned long flags)
63 {
64         pgd_t * dir;
65         int error = 0;
66         unsigned long end = address + size;
67
68         phys_addr -= address;
69         dir = pgd_offset(&init_mm, address);
70         flush_cache_all();
71         if (address >= end)
72                 BUG();
73         spin_lock(&init_mm.page_table_lock);
74         do {
75                 pmd_t *pmd;
76                 pmd = pmd_alloc(&init_mm, dir, address);
77                 error = -ENOMEM;
78                 if (!pmd)
79                         break;
80                 if (remap_area_pmd(pmd, address, end - address,
81                                    phys_addr + address, flags))
82                         break;
83                 error = 0;
84                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
85                 dir++;
86         } while (address && (address < end));
87         spin_unlock(&init_mm.page_table_lock);
88         return error;
89 }
90