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