ia64/xen-unstable

view xenolinux-2.4.16-sparse/arch/xeno/drivers/dom0/dom0_memory.c @ 77:024aa2a06c1e

bitkeeper revision 1.7.3.52 (3e1019294uiW6rByAS_Bs30dgMr6AA)

debug
author lynx@idefix.cl.cam.ac.uk
date Mon Dec 30 10:00:09 2002 +0000 (2002-12-30)
parents cc3048fbafd8
children aac5f8f1210e
line source
2 #include <linux/slab.h>
3 #include <linux/mm.h>
4 #include <linux/mman.h>
5 #include <linux/swap.h>
6 #include <linux/smp_lock.h>
7 #include <linux/swapctl.h>
8 #include <linux/iobuf.h>
9 #include <linux/highmem.h>
10 #include <linux/pagemap.h>
11 #include <linux/list.h>
13 #include <asm/pgalloc.h>
14 #include <asm/uaccess.h>
15 #include <asm/tlb.h>
16 #include <asm/mmu.h>
18 #include "hypervisor_defs.h"
20 #define MAP_CONT 0
21 #define MAP_DISCONT 1
23 extern struct list_head * find_direct(struct list_head *, unsigned long);
25 /* now, this is grimm, kmalloc seems to have problems allocating small mem
26 * blocks, so i have decided to use fixed (a bit) larger blocks... this needs
27 * to be traced down but no time now.
28 */
29 #define KMALLOC_SIZE 128
31 /* bd240: functions below perform direct mapping to the real physical pages needed for
32 * mapping various hypervisor specific structures needed in dom0 userspace by various
33 * management applications such as domain builder etc.
34 */
36 #define direct_set_pte(pteptr, pteval) queue_l1_entry_update(__pa(pteptr) | PGREQ_UNCHECKED_UPDATE, (pteval).pte_low)
38 #define direct_pte_clear(pteptr) queue_l1_entry_update(__pa(pteptr) | PGREQ_UNCHECKED_UPDATE, 0)
40 #define __direct_pte(x) ((pte_t) { (x) } )
41 #define __direct_mk_pte(page_nr,pgprot) __direct_pte(((page_nr) << PAGE_SHIFT) | pgprot_val(pgprot))
42 #define direct_mk_pte_phys(physpage, pgprot) __direct_mk_pte((physpage) >> PAGE_SHIFT, pgprot)
44 static inline void forget_pte(pte_t page)
45 {
46 if (!pte_none(page)) {
47 printk("forget_pte: old mapping existed!\n");
48 BUG();
49 }
50 }
52 static inline void direct_remappte_range(pte_t * pte, unsigned long address, unsigned long size,
53 unsigned long phys_addr, pgprot_t prot)
54 {
55 unsigned long end;
57 address &= ~PMD_MASK;
58 end = address + size;
59 if (end > PMD_SIZE)
60 end = PMD_SIZE;
61 do {
62 pte_t oldpage;
63 oldpage = ptep_get_and_clear(pte);
64 /*
65 printk(KERN_ALERT "bd240 debug: %lx - %lx\n", pte, phys_addr);
66 */
67 direct_set_pte(pte, direct_mk_pte_phys(phys_addr, prot));
69 forget_pte(oldpage);
70 address += PAGE_SIZE;
71 phys_addr += PAGE_SIZE;
72 pte++;
73 } while (address && (address < end));
75 }
77 static inline int direct_remappmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned long address, unsigned long size,
78 unsigned long phys_addr, pgprot_t prot)
79 {
80 unsigned long end;
82 address &= ~PGDIR_MASK;
83 end = address + size;
84 if (end > PGDIR_SIZE)
85 end = PGDIR_SIZE;
86 phys_addr -= address;
87 do {
88 pte_t * pte = pte_alloc(mm, pmd, address);
89 if (!pte)
90 return -ENOMEM;
91 direct_remappte_range(pte, address, end - address, address + phys_addr, prot);
92 address = (address + PMD_SIZE) & PMD_MASK;
93 pmd++;
94 } while (address && (address < end));
95 return 0;
96 }
98 /* Note: this is only safe if the mm semaphore is held when called. */
99 int direct_remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long size, pgprot_t prot)
100 {
101 int error = 0;
102 pgd_t * dir;
103 unsigned long beg = from;
104 unsigned long end = from + size;
105 struct mm_struct *mm = current->mm;
107 phys_addr -= from;
108 dir = pgd_offset(mm, from);
109 flush_cache_range(mm, beg, end);
110 if (from >= end)
111 BUG();
113 spin_lock(&mm->page_table_lock);
114 do {
115 pmd_t *pmd = pmd_alloc(mm, dir, from);
116 error = -ENOMEM;
117 if (!pmd)
118 break;
119 error = direct_remappmd_range(mm, pmd, from, end - from, phys_addr + from, prot);
120 if (error)
121 break;
122 from = (from + PGDIR_SIZE) & PGDIR_MASK;
123 dir++;
124 } while (from && (from < end));
125 spin_unlock(&mm->page_table_lock);
126 flush_tlb_range(mm, beg, end);
127 return error;
128 }
130 /*
131 * used for remapping discontiguous bits of domain's memory, pages to map are
132 * found from frame table beginning at the given first_pg index
133 */
134 int direct_remap_disc_page_range(unsigned long from, unsigned long first_pg,
135 int tot_pages, pgprot_t prot)
136 {
137 frame_table_t * current_ft;
138 unsigned long current_pfn;
139 unsigned long start = from;
140 int count = 0;
142 current_ft = (frame_table_t *)(frame_table + first_pg);
143 current_pfn = first_pg;
144 while(count < tot_pages){
145 if(direct_remap_page_range(start, current_pfn << PAGE_SHIFT, PAGE_SIZE, prot))
146 goto out;
147 start += PAGE_SIZE;
148 current_pfn = current_ft->next;
149 current_ft = (frame_table_t *)(frame_table + current_pfn);
150 count++;
151 }
153 out:
155 return tot_pages - count;
156 }
158 /* below functions replace standard sys_mmap and sys_munmap which are absolutely useless
159 * for direct memory mapping. direct_zap* functions are minor ammendments to the
160 * original versions in mm/memory.c. the changes are to enable unmapping of real physical
161 * addresses.
162 */
164 unsigned long direct_mmap(unsigned long phys_addr, unsigned long size,
165 pgprot_t prot, int flag, int tot_pages)
166 {
167 direct_mmap_node_t * dmmap;
168 struct list_head * entry;
169 unsigned long addr;
170 int ret = 0;
172 if(!capable(CAP_SYS_ADMIN)){
173 ret = -EPERM;
174 goto out;
175 }
177 /* get unmapped area invokes xen specific arch_get_unmapped_area */
178 addr = get_unmapped_area(NULL, 0, size, 0, 0);
179 if(addr & ~PAGE_MASK){
180 ret = -ENOMEM;
181 goto out;
182 }
184 /* add node on the list of directly mapped areas, make sure the
185 * list remains sorted.
186 */
187 dmmap = (direct_mmap_node_t *)kmalloc(KMALLOC_SIZE, GFP_KERNEL);
188 dmmap->vm_start = addr;
189 dmmap->vm_end = addr + size;
190 entry = find_direct(&current->mm->context.direct_list, addr);
191 if(entry != &current->mm->context.direct_list){
192 list_add_tail(&dmmap->list, entry);
193 printk(KERN_ALERT "bd240 debug: added node %lx in the middle\n", node->vm_start);
194 } else {
195 list_add(&dmmap->list, &current->mm->context.direct_list);
196 printk(KERN_ALERT "bd240 debug: added node %lx at tail\n", node->vm_start);
197 }
199 /* and perform the mapping */
200 if(flag == MAP_DISCONT){
201 ret = direct_remap_disc_page_range(addr, phys_addr, tot_pages, prot);
202 } else {
203 ret = direct_remap_page_range(addr, phys_addr, size, prot);
204 }
206 if(ret == 0)
207 ret = addr;
209 out:
210 return ret;
211 }
213 /* most of the checks, refcnt updates, cache stuff have been thrown out as they are not
214 * needed
215 */
216 static inline int direct_zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address,
217 unsigned long size)
218 {
219 unsigned long offset;
220 pte_t * ptep;
221 int freed = 0;
223 if (pmd_none(*pmd))
224 return 0;
225 if (pmd_bad(*pmd)) {
226 pmd_ERROR(*pmd);
227 pmd_clear(pmd);
228 return 0;
229 }
230 ptep = pte_offset(pmd, address);
231 offset = address & ~PMD_MASK;
232 if (offset + size > PMD_SIZE)
233 size = PMD_SIZE - offset;
234 size &= PAGE_MASK;
235 for (offset=0; offset < size; ptep++, offset += PAGE_SIZE) {
236 pte_t pte = *ptep;
237 if (pte_none(pte))
238 continue;
239 freed ++;
240 direct_pte_clear(ptep);
241 }
243 return freed;
244 }
246 static inline int direct_zap_pmd_range(mmu_gather_t *tlb, pgd_t * dir,
247 unsigned long address, unsigned long size)
248 {
249 pmd_t * pmd;
250 unsigned long end;
251 int freed;
253 if (pgd_none(*dir))
254 return 0;
255 if (pgd_bad(*dir)) {
256 pgd_ERROR(*dir);
257 pgd_clear(dir);
258 return 0;
259 }
260 pmd = pmd_offset(dir, address);
261 end = address + size;
262 if (end > ((address + PGDIR_SIZE) & PGDIR_MASK))
263 end = ((address + PGDIR_SIZE) & PGDIR_MASK);
264 freed = 0;
265 do {
266 freed += direct_zap_pte_range(tlb, pmd, address, end - address);
267 address = (address + PMD_SIZE) & PMD_MASK;
268 pmd++;
269 } while (address < end);
270 return freed;
271 }
273 /*
274 * remove user pages in a given range.
275 */
276 void direct_zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
277 {
278 mmu_gather_t *tlb;
279 pgd_t * dir;
280 unsigned long start = address, end = address + size;
281 int freed = 0;
283 dir = pgd_offset(mm, address);
285 /*
286 * This is a long-lived spinlock. That's fine.
287 * There's no contention, because the page table
288 * lock only protects against kswapd anyway, and
289 * even if kswapd happened to be looking at this
290 * process we _want_ it to get stuck.
291 */
292 if (address >= end)
293 BUG();
294 spin_lock(&mm->page_table_lock);
295 flush_cache_range(mm, address, end);
296 tlb = tlb_gather_mmu(mm);
298 do {
299 freed += direct_zap_pmd_range(tlb, dir, address, end - address);
300 address = (address + PGDIR_SIZE) & PGDIR_MASK;
301 dir++;
302 } while (address && (address < end));
304 /* this will flush any remaining tlb entries */
305 tlb_finish_mmu(tlb, start, end);
307 /* decrementing rss removed */
309 spin_unlock(&mm->page_table_lock);
310 }
312 int direct_unmap(unsigned long addr, unsigned long size)
313 {
314 direct_mmap_node_t * node;
315 struct list_head * curr;
316 struct list_head * direct_list = &current->mm->context.direct_list;
318 curr = direct_list->next;
319 while(curr != direct_list){
320 node = list_entry(curr, direct_mmap_node_t, list);
321 if(node->vm_start == addr)
322 break;
323 curr = curr->next;
324 }
326 if(curr == direct_list)
327 return -1;
329 list_del(&node->list);
330 kfree(node);
332 direct_zap_page_range(current->mm, addr, size);
334 return 0;
335 }
337 int direct_disc_unmap(unsigned long from, unsigned long first_pg, int tot_pages)
338 {
339 int count = 0;
340 direct_mmap_node_t * node;
341 struct list_head * curr;
342 struct list_head * direct_list = &current->mm->context.direct_list;
344 printk(KERN_ALERT "bd240 debug: direct_disc_unmap\n");
346 curr = direct_list->next;
347 while(curr != direct_list){
348 node = list_entry(curr, direct_mmap_node_t, list);
350 if(node->vm_start == from)
351 break;
352 curr = curr->next;
353 }
355 if(curr == direct_list)
356 return -1;
358 printk(KERN_ALERT "bd240 debug: direct_disc_unmap, deleted from direct_list\n");
360 list_del(&node->list);
361 kfree(node);
363 printk(KERN_ALERT "bd240 debug: direct_disc_unmap, from %lx, tot_pages %lx\n", from, tot_pages);
365 while(count < tot_pages){
366 direct_zap_page_range(current->mm, from, PAGE_SIZE);
367 from += PAGE_SIZE;
368 count++;
369 }
371 return 0;
372 }