This source file includes following definitions.
- set_pgdir
- free_area_pte
- free_area_pmd
- free_area_pages
- alloc_area_pte
- alloc_area_pmd
- alloc_area_pages
- remap_area_pte
- remap_area_pmd
- remap_area_pages
- get_vm_area
- vfree
- vmalloc
- vremap
- vread
1
2
3
4
5
6
7 #include <asm/system.h>
8
9 #include <linux/signal.h>
10 #include <linux/sched.h>
11 #include <linux/head.h>
12 #include <linux/kernel.h>
13 #include <linux/errno.h>
14 #include <linux/types.h>
15 #include <linux/malloc.h>
16 #include <linux/mm.h>
17
18 #include <asm/segment.h>
19 #include <asm/pgtable.h>
20
21 struct vm_struct {
22 unsigned long flags;
23 void * addr;
24 unsigned long size;
25 struct vm_struct * next;
26 };
27
28 static struct vm_struct * vmlist = NULL;
29
30 static inline void set_pgdir(unsigned long address, pgd_t entry)
31 {
32 struct task_struct * p;
33
34 for_each_task(p) {
35 if (!p->mm)
36 continue;
37 *pgd_offset(p->mm,address) = entry;
38 }
39 }
40
41 static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
42 {
43 pte_t * pte;
44 unsigned long end;
45
46 if (pmd_none(*pmd))
47 return;
48 if (pmd_bad(*pmd)) {
49 printk("free_area_pte: bad pmd (%08lx)\n", pmd_val(*pmd));
50 pmd_clear(pmd);
51 return;
52 }
53 pte = pte_offset(pmd, address);
54 address &= ~PMD_MASK;
55 end = address + size;
56 if (end > PMD_SIZE)
57 end = PMD_SIZE;
58 while (address < end) {
59 pte_t page = *pte;
60 pte_clear(pte);
61 address += PAGE_SIZE;
62 pte++;
63 if (pte_none(page))
64 continue;
65 if (pte_present(page)) {
66 free_page(pte_page(page));
67 continue;
68 }
69 printk("Whee.. Swapped out page in kernel page table\n");
70 }
71 }
72
73 static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
74 {
75 pmd_t * pmd;
76 unsigned long end;
77
78 if (pgd_none(*dir))
79 return;
80 if (pgd_bad(*dir)) {
81 printk("free_area_pmd: bad pgd (%08lx)\n", pgd_val(*dir));
82 pgd_clear(dir);
83 return;
84 }
85 pmd = pmd_offset(dir, address);
86 address &= ~PGDIR_MASK;
87 end = address + size;
88 if (end > PGDIR_SIZE)
89 end = PGDIR_SIZE;
90 while (address < end) {
91 free_area_pte(pmd, address, end - address);
92 address = (address + PMD_SIZE) & PMD_MASK;
93 pmd++;
94 }
95 }
96
97 static void free_area_pages(unsigned long address, unsigned long size)
98 {
99 pgd_t * dir;
100 unsigned long end = address + size;
101
102 dir = pgd_offset(&init_mm, address);
103 flush_cache_all();
104 while (address < end) {
105 free_area_pmd(dir, address, end - address);
106 address = (address + PGDIR_SIZE) & PGDIR_MASK;
107 dir++;
108 }
109 flush_tlb_all();
110 }
111
112 static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned long size)
113 {
114 unsigned long end;
115
116 address &= ~PMD_MASK;
117 end = address + size;
118 if (end > PMD_SIZE)
119 end = PMD_SIZE;
120 while (address < end) {
121 unsigned long page;
122 if (!pte_none(*pte))
123 printk("alloc_area_pte: page already exists\n");
124 page = __get_free_page(GFP_KERNEL);
125 if (!page)
126 return -ENOMEM;
127 set_pte(pte, mk_pte(page, PAGE_KERNEL));
128 address += PAGE_SIZE;
129 pte++;
130 }
131 return 0;
132 }
133
134 static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size)
135 {
136 unsigned long end;
137
138 address &= ~PGDIR_MASK;
139 end = address + size;
140 if (end > PGDIR_SIZE)
141 end = PGDIR_SIZE;
142 while (address < end) {
143 pte_t * pte = pte_alloc_kernel(pmd, address);
144 if (!pte)
145 return -ENOMEM;
146 if (alloc_area_pte(pte, address, end - address))
147 return -ENOMEM;
148 address = (address + PMD_SIZE) & PMD_MASK;
149 pmd++;
150 }
151 return 0;
152 }
153
154 static int alloc_area_pages(unsigned long address, unsigned long size)
155 {
156 pgd_t * dir;
157 unsigned long end = address + size;
158
159 dir = pgd_offset(&init_mm, address);
160 flush_cache_all();
161 while (address < end) {
162 pmd_t *pmd = pmd_alloc_kernel(dir, address);
163 if (!pmd)
164 return -ENOMEM;
165 if (alloc_area_pmd(pmd, address, end - address))
166 return -ENOMEM;
167 set_pgdir(address, *dir);
168 address = (address + PGDIR_SIZE) & PGDIR_MASK;
169 dir++;
170 }
171 flush_tlb_all();
172 return 0;
173 }
174
175 static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
176 unsigned long offset)
177 {
178 unsigned long end;
179
180 address &= ~PMD_MASK;
181 end = address + size;
182 if (end > PMD_SIZE)
183 end = PMD_SIZE;
184 do {
185 if (!pte_none(*pte))
186 printk("remap_area_pte: page already exists\n");
187 set_pte(pte, mk_pte(offset, PAGE_KERNEL));
188 address += PAGE_SIZE;
189 offset += PAGE_SIZE;
190 pte++;
191 } while (address < end);
192 }
193
194 static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
195 unsigned long offset)
196 {
197 unsigned long end;
198
199 address &= ~PGDIR_MASK;
200 end = address + size;
201 if (end > PGDIR_SIZE)
202 end = PGDIR_SIZE;
203 offset -= address;
204 do {
205 pte_t * pte = pte_alloc_kernel(pmd, address);
206 if (!pte)
207 return -ENOMEM;
208 remap_area_pte(pte, address, end - address, address + offset);
209 address = (address + PMD_SIZE) & PMD_MASK;
210 pmd++;
211 } while (address < end);
212 return 0;
213 }
214
215 static int remap_area_pages(unsigned long address, unsigned long offset, unsigned long size)
216 {
217 pgd_t * dir;
218 unsigned long end = address + size;
219
220 offset -= address;
221 dir = pgd_offset(&init_mm, address);
222 flush_cache_all();
223 while (address < end) {
224 pmd_t *pmd = pmd_alloc_kernel(dir, address);
225 if (!pmd)
226 return -ENOMEM;
227 if (remap_area_pmd(pmd, address, end - address, offset + address))
228 return -ENOMEM;
229 set_pgdir(address, *dir);
230 address = (address + PGDIR_SIZE) & PGDIR_MASK;
231 dir++;
232 }
233 flush_tlb_all();
234 return 0;
235 }
236
237 static struct vm_struct * get_vm_area(unsigned long size)
238 {
239 void *addr;
240 struct vm_struct **p, *tmp, *area;
241
242 area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
243 if (!area)
244 return NULL;
245 addr = (void *) VMALLOC_START;
246 area->size = size + PAGE_SIZE;
247 area->next = NULL;
248 for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
249 if (size + (unsigned long) addr < (unsigned long) tmp->addr)
250 break;
251 addr = (void *) (tmp->size + (unsigned long) tmp->addr);
252 }
253 area->addr = addr;
254 area->next = *p;
255 *p = area;
256 return area;
257 }
258
259 void vfree(void * addr)
260 {
261 struct vm_struct **p, *tmp;
262
263 if (!addr)
264 return;
265 if ((PAGE_SIZE-1) & (unsigned long) addr) {
266 printk("Trying to vfree() bad address (%p)\n", addr);
267 return;
268 }
269 for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
270 if (tmp->addr == addr) {
271 *p = tmp->next;
272 free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
273 kfree(tmp);
274 return;
275 }
276 }
277 printk("Trying to vfree() nonexistent vm area (%p)\n", addr);
278 }
279
280 void * vmalloc(unsigned long size)
281 {
282 void * addr;
283 struct vm_struct *area;
284
285 size = PAGE_ALIGN(size);
286 if (!size || size > (MAP_NR(high_memory) << PAGE_SHIFT))
287 return NULL;
288 area = get_vm_area(size);
289 if (!area)
290 return NULL;
291 addr = area->addr;
292 if (alloc_area_pages(VMALLOC_VMADDR(addr), size)) {
293 vfree(addr);
294 return NULL;
295 }
296 return addr;
297 }
298
299
300
301
302
303
304 void * vremap(unsigned long offset, unsigned long size)
305 {
306 void * addr;
307 struct vm_struct * area;
308
309 if (offset < high_memory)
310 return NULL;
311 if (offset & ~PAGE_MASK)
312 return NULL;
313 size = PAGE_ALIGN(size);
314 if (!size || size > offset + size)
315 return NULL;
316 area = get_vm_area(size);
317 if (!area)
318 return NULL;
319 addr = area->addr;
320 if (remap_area_pages(VMALLOC_VMADDR(addr), offset, size)) {
321 vfree(addr);
322 return NULL;
323 }
324 return addr;
325 }
326
327 int vread(char *buf, char *addr, int count)
328 {
329 struct vm_struct **p, *tmp;
330 char *vaddr, *buf_start = buf;
331 int n;
332
333 for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
334 vaddr = (char *) tmp->addr;
335 while (addr < vaddr) {
336 if (count == 0)
337 goto finished;
338 put_user('\0', buf++), addr++, count--;
339 }
340 n = tmp->size - PAGE_SIZE;
341 if (addr > vaddr)
342 n -= addr - vaddr;
343 while (--n >= 0) {
344 if (count == 0)
345 goto finished;
346 put_user(*addr++, buf++), count--;
347 }
348 }
349 finished:
350 return buf - buf_start;
351 }