ia64/xen-unstable

view xenolinux-2.4.16-sparse/arch/xeno/drivers/dom0/dom0_memory.c @ 73:4900a90a3842

bitkeeper revision 1.7.3.48 (3e0af1e1iJY4-_hnftVa46WD4oWiFw)

debug
author lynx@idefix.cl.cam.ac.uk
date Thu Dec 26 12:11:13 2002 +0000 (2002-12-26)
parents 7c6228c9e98f
children cc3048fbafd8
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(GFP_KERNEL, sizeof(direct_mmap_node_t));
188 dmmap = (direct_mmap_node_t *)kmalloc(GFP_KERNEL, KMALLOC_SIZE);
189 dmmap->vm_start = addr;
190 dmmap->vm_end = addr + size;
191 entry = find_direct(&current->mm->context.direct_list, addr);
192 if(entry != &current->mm->context.direct_list){
193 list_add_tail(&dmmap->list, entry);
194 } else {
195 list_add(&dmmap->list, &current->mm->context.direct_list);
196 }
198 /* and perform the mapping */
199 if(flag == MAP_DISCONT){
200 ret = direct_remap_disc_page_range(addr, phys_addr, tot_pages, prot);
201 } else {
202 ret = direct_remap_page_range(addr, phys_addr, size, prot);
203 }
205 if(ret == 0)
206 ret = addr;
208 out:
209 return ret;
210 }
212 /* most of the checks, refcnt updates, cache stuff have been thrown out as they are not
213 * needed
214 */
215 static inline int direct_zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address,
216 unsigned long size)
217 {
218 unsigned long offset;
219 pte_t * ptep;
220 int freed = 0;
222 if (pmd_none(*pmd))
223 return 0;
224 if (pmd_bad(*pmd)) {
225 pmd_ERROR(*pmd);
226 pmd_clear(pmd);
227 return 0;
228 }
229 ptep = pte_offset(pmd, address);
230 offset = address & ~PMD_MASK;
231 if (offset + size > PMD_SIZE)
232 size = PMD_SIZE - offset;
233 size &= PAGE_MASK;
234 for (offset=0; offset < size; ptep++, offset += PAGE_SIZE) {
235 pte_t pte = *ptep;
236 if (pte_none(pte))
237 continue;
238 freed ++;
239 direct_pte_clear(ptep);
240 }
242 return freed;
243 }
245 static inline int direct_zap_pmd_range(mmu_gather_t *tlb, pgd_t * dir,
246 unsigned long address, unsigned long size)
247 {
248 pmd_t * pmd;
249 unsigned long end;
250 int freed;
252 if (pgd_none(*dir))
253 return 0;
254 if (pgd_bad(*dir)) {
255 pgd_ERROR(*dir);
256 pgd_clear(dir);
257 return 0;
258 }
259 pmd = pmd_offset(dir, address);
260 end = address + size;
261 if (end > ((address + PGDIR_SIZE) & PGDIR_MASK))
262 end = ((address + PGDIR_SIZE) & PGDIR_MASK);
263 freed = 0;
264 do {
265 freed += direct_zap_pte_range(tlb, pmd, address, end - address);
266 address = (address + PMD_SIZE) & PMD_MASK;
267 pmd++;
268 } while (address < end);
269 return freed;
270 }
272 /*
273 * remove user pages in a given range.
274 */
275 void direct_zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
276 {
277 mmu_gather_t *tlb;
278 pgd_t * dir;
279 unsigned long start = address, end = address + size;
280 int freed = 0;
282 dir = pgd_offset(mm, address);
284 /*
285 * This is a long-lived spinlock. That's fine.
286 * There's no contention, because the page table
287 * lock only protects against kswapd anyway, and
288 * even if kswapd happened to be looking at this
289 * process we _want_ it to get stuck.
290 */
291 if (address >= end)
292 BUG();
293 spin_lock(&mm->page_table_lock);
294 flush_cache_range(mm, address, end);
295 tlb = tlb_gather_mmu(mm);
297 do {
298 freed += direct_zap_pmd_range(tlb, dir, address, end - address);
299 address = (address + PGDIR_SIZE) & PGDIR_MASK;
300 dir++;
301 } while (address && (address < end));
303 /* this will flush any remaining tlb entries */
304 tlb_finish_mmu(tlb, start, end);
306 /* decrementing rss removed */
308 spin_unlock(&mm->page_table_lock);
309 }
311 int direct_unmap(unsigned long addr, unsigned long size)
312 {
313 direct_mmap_node_t * node;
314 struct list_head * curr;
315 struct list_head * direct_list = &current->mm->context.direct_list;
317 curr = direct_list->next;
318 while(curr != direct_list){
319 node = list_entry(curr, direct_mmap_node_t, list);
320 if(node->vm_start == addr)
321 break;
322 curr = curr->next;
323 }
325 if(curr == direct_list)
326 return -1;
328 list_del(&node->list);
329 kfree(node);
331 direct_zap_page_range(current->mm, addr, size);
333 return 0;
334 }
336 int direct_disc_unmap(unsigned long from, unsigned long first_pg, int tot_pages)
337 {
338 int count = 0;
339 direct_mmap_node_t * node;
340 struct list_head * curr;
341 struct list_head * direct_list = &current->mm->context.direct_list;
343 printk(KERN_ALERT "bd240 debug: direct_disc_unmap\n");
345 curr = direct_list->next;
346 while(curr != direct_list){
347 node = list_entry(curr, direct_mmap_node_t, list);
349 if(node->vm_start == from)
350 break;
351 curr = curr->next;
352 }
354 if(curr == direct_list)
355 return -1;
357 printk(KERN_ALERT "bd240 debug: direct_disc_unmap, deleted from direct_list\n");
359 list_del(&node->list);
360 kfree(node);
362 printk(KERN_ALERT "bd240 debug: direct_disc_unmap, from %lx, tot_pages %lx\n", from, tot_pages);
364 while(count < tot_pages){
365 direct_zap_page_range(current->mm, from, PAGE_SIZE);
366 from += PAGE_SIZE;
367 count++;
368 }
370 return 0;
371 }