ia64/xen-unstable

view xenolinux-2.4.16-sparse/arch/xeno/drivers/dom0/dom0_memory.c @ 41:9cb55b5e6e2f

bitkeeper revision 1.7.3.20 (3dfeebd8KBvUCyVAwYScXGvSWcM1WA)

testing
author lynx@idefix.cl.cam.ac.uk
date Tue Dec 17 09:18:16 2002 +0000 (2002-12-17)
parents 73d7858fcabb
children 18f514abffe9
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 /*
24 * maps a range of physical memory into the requested pages. the old
25 * mappings are removed. any references to nonexistent pages results
26 * in null mappings (currently treated as "copy-on-access")
27 */
29 /* bd240: functions below perform direct mapping to the real physical pages needed for
30 * mapping various hypervisor specific structures needed in dom0 userspace by various
31 * management applications such as domain builder etc.
32 */
34 #define direct_set_pte(pteptr, pteval) queue_l1_entry_update(__pa(pteptr) | PGREQ_UNCHECKED_UPDATE, (pteval).pte_low)
36 #define direct_pte_clear(pteptr) queue_l1_entry_update(__pa(pteptr) | PGREQ_UNCHECKED_UPDATE, 0)
38 #define __direct_pte(x) ((pte_t) { (x) } )
39 #define __direct_mk_pte(page_nr,pgprot) __direct_pte(((page_nr) << PAGE_SHIFT) | pgprot_val(pgprot))
40 #define direct_mk_pte_phys(physpage, pgprot) __direct_mk_pte((physpage) >> PAGE_SHIFT, pgprot)
42 static inline void forget_pte(pte_t page)
43 {
44 if (!pte_none(page)) {
45 printk("forget_pte: old mapping existed!\n");
46 BUG();
47 }
48 }
50 static inline void direct_remappte_range(pte_t * pte, unsigned long address, unsigned long size,
51 unsigned long phys_addr, pgprot_t prot)
52 {
53 unsigned long end;
55 address &= ~PMD_MASK;
56 end = address + size;
57 if (end > PMD_SIZE)
58 end = PMD_SIZE;
59 do {
60 pte_t oldpage;
61 oldpage = ptep_get_and_clear(pte);
63 printk(KERN_ALERT "bd240 debug: %lx - %lx\n", address, phys_addr);
65 direct_set_pte(pte, direct_mk_pte_phys(phys_addr, prot));
67 forget_pte(oldpage);
68 address += PAGE_SIZE;
69 phys_addr += PAGE_SIZE;
70 pte++;
71 } while (address && (address < end));
73 printk(KERN_ALERT "bd240 debug: exit from direct_remappte_range\n");
74 }
76 static inline int direct_remappmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned long address, unsigned long size,
77 unsigned long phys_addr, pgprot_t prot)
78 {
79 unsigned long end;
81 address &= ~PGDIR_MASK;
82 end = address + size;
83 if (end > PGDIR_SIZE)
84 end = PGDIR_SIZE;
85 phys_addr -= address;
86 do {
87 pte_t * pte = pte_alloc(mm, pmd, address);
88 if (!pte)
89 return -ENOMEM;
90 direct_remappte_range(pte, address, end - address, address + phys_addr, prot);
91 address = (address + PMD_SIZE) & PMD_MASK;
92 pmd++;
93 } while (address && (address < end));
94 return 0;
95 }
97 /* Note: this is only safe if the mm semaphore is held when called. */
98 int direct_remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long size, pgprot_t prot)
99 {
100 int error = 0;
101 pgd_t * dir;
102 unsigned long beg = from;
103 unsigned long end = from + size;
104 struct mm_struct *mm = current->mm;
106 phys_addr -= from;
107 dir = pgd_offset(mm, from);
108 flush_cache_range(mm, beg, end);
109 if (from >= end)
110 BUG();
112 spin_lock(&mm->page_table_lock);
113 do {
114 pmd_t *pmd = pmd_alloc(mm, dir, from);
115 error = -ENOMEM;
116 if (!pmd)
117 break;
118 error = direct_remappmd_range(mm, pmd, from, end - from, phys_addr + from, prot);
119 if (error)
120 break;
121 from = (from + PGDIR_SIZE) & PGDIR_MASK;
122 dir++;
123 } while (from && (from < end));
124 spin_unlock(&mm->page_table_lock);
125 flush_tlb_range(mm, beg, end);
126 return error;
127 }
129 /*
130 * used for remapping discontiguous bits of domain's memory, pages to map are
131 * found from frame table beginning at the given first_pg index
132 */
133 int direct_remap_disc_page_range(unsigned long from, unsigned long first_pg,
134 int tot_pages, pgprot_t prot)
135 {
136 frame_table_t * current_ft;
137 unsigned long current_pfn;
138 unsigned long start = from;
139 int count = 0;
141 current_ft = (frame_table_t *)(frame_table + first_pg);
142 current_pfn = first_pg;
143 while(count < tot_pages){
144 if(direct_remap_page_range(start, current_pfn << PAGE_SHIFT, PAGE_SIZE, prot))
145 goto out;
146 start += PAGE_SIZE;
147 current_pfn = current_ft->next;
148 current_ft = (frame_table_t *)(frame_table + current_pfn);
149 count++;
150 }
152 out:
154 return tot_pages - count;
155 }
157 /* below functions replace standard sys_mmap and sys_munmap which are absolutely useless
158 * for direct memory mapping. direct_zap* functions are minor ammendments to the
159 * original versions in mm/memory.c. the changes are to enable unmapping of real physical
160 * addresses.
161 */
163 unsigned long direct_mmap(unsigned long phys_addr, unsigned long size,
164 pgprot_t prot, int flag, int tot_pages)
165 {
166 direct_mmap_node_t * dmmap;
167 unsigned long addr;
168 int ret = 0;
170 if(!capable(CAP_SYS_ADMIN)){
171 ret = -EPERM;
172 goto out;
173 }
175 /* get unmapped area invokes xen specific arch_get_unmapped_area */
176 addr = get_unmapped_area(NULL, 0, size, 0, 0);
177 if(addr & ~PAGE_MASK){
178 ret = -ENOMEM;
179 goto out;
180 }
182 /* add node on the list of directly mapped areas */
183 dmmap = (direct_mmap_node_t *)kmalloc(GFP_KERNEL, sizeof(direct_mmap_node_t));
184 dmmap->addr = addr;
185 list_add(&dmmap->list, &current->mm->context.direct_list);
187 /* and perform the mapping */
188 if(flag == MAP_DISCONT){
189 ret = direct_remap_disc_page_range(addr, phys_addr, tot_pages, prot);
190 } else {
191 printk(KERN_ALERT "bd240 debug: addr %lx, phys_addr %lx, size %lx\n",
192 addr, phys_addr, size);
193 ret = direct_remap_page_range(addr, phys_addr, size, prot);
194 }
196 if(ret == 0)
197 ret = addr;
199 out:
200 return ret;
201 }
203 /* most of the checks, refcnt updates, cache stuff have been thrown out as they are not
204 * needed
205 */
206 static inline int direct_zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address,
207 unsigned long size)
208 {
209 unsigned long offset;
210 pte_t * ptep;
211 int freed = 0;
213 if (pmd_none(*pmd))
214 return 0;
215 if (pmd_bad(*pmd)) {
216 pmd_ERROR(*pmd);
217 pmd_clear(pmd);
218 return 0;
219 }
220 ptep = pte_offset(pmd, address);
221 offset = address & ~PMD_MASK;
222 if (offset + size > PMD_SIZE)
223 size = PMD_SIZE - offset;
224 size &= PAGE_MASK;
225 for (offset=0; offset < size; ptep++, offset += PAGE_SIZE) {
226 pte_t pte = *ptep;
227 if (pte_none(pte))
228 continue;
229 freed ++;
230 direct_pte_clear(ptep);
231 }
233 return freed;
234 }
236 static inline int direct_zap_pmd_range(mmu_gather_t *tlb, pgd_t * dir,
237 unsigned long address, unsigned long size)
238 {
239 pmd_t * pmd;
240 unsigned long end;
241 int freed;
243 if (pgd_none(*dir))
244 return 0;
245 if (pgd_bad(*dir)) {
246 pgd_ERROR(*dir);
247 pgd_clear(dir);
248 return 0;
249 }
250 pmd = pmd_offset(dir, address);
251 end = address + size;
252 if (end > ((address + PGDIR_SIZE) & PGDIR_MASK))
253 end = ((address + PGDIR_SIZE) & PGDIR_MASK);
254 freed = 0;
255 do {
256 freed += direct_zap_pte_range(tlb, pmd, address, end - address);
257 address = (address + PMD_SIZE) & PMD_MASK;
258 pmd++;
259 } while (address < end);
260 return freed;
261 }
263 /*
264 * remove user pages in a given range.
265 */
266 void direct_zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
267 {
268 mmu_gather_t *tlb;
269 pgd_t * dir;
270 unsigned long start = address, end = address + size;
271 int freed = 0;
273 dir = pgd_offset(mm, address);
275 /*
276 * This is a long-lived spinlock. That's fine.
277 * There's no contention, because the page table
278 * lock only protects against kswapd anyway, and
279 * even if kswapd happened to be looking at this
280 * process we _want_ it to get stuck.
281 */
282 if (address >= end)
283 BUG();
284 spin_lock(&mm->page_table_lock);
285 flush_cache_range(mm, address, end);
286 tlb = tlb_gather_mmu(mm);
288 do {
289 freed += direct_zap_pmd_range(tlb, dir, address, end - address);
290 address = (address + PGDIR_SIZE) & PGDIR_MASK;
291 dir++;
292 } while (address && (address < end));
294 /* this will flush any remaining tlb entries */
295 tlb_finish_mmu(tlb, start, end);
297 /* decrementing rss removed */
299 spin_unlock(&mm->page_table_lock);
300 }
302 int direct_unmap(unsigned long addr, unsigned long size)
303 {
304 direct_mmap_node_t * node;
305 struct list_head * curr;
306 struct list_head * direct_list = &current->mm->context.direct_list;
308 curr = direct_list->next;
309 while(curr != direct_list){
310 node = list_entry(curr, direct_mmap_node_t, list);
311 if(node->addr == addr)
312 break;
313 curr = curr->next;
314 }
316 if(curr == direct_list)
317 return -1;
319 list_del(&node->list);
320 kfree(node);
322 direct_zap_page_range(current->mm, addr, size);
324 return 0;
325 }
327 int direct_disc_unmap(unsigned long from, unsigned long first_pg, int tot_pages)
328 {
329 int count = 0;
330 direct_mmap_node_t * node;
331 struct list_head * curr;
332 struct list_head * direct_list = &current->mm->context.direct_list;
334 curr = direct_list->next;
335 while(curr != direct_list){
336 node = list_entry(curr, direct_mmap_node_t, list);
337 if(node->addr == from)
338 break;
339 curr = curr->next;
340 }
342 if(curr == direct_list)
343 return -1;
345 list_del(&node->list);
346 kfree(node);
348 while(count < tot_pages){
349 direct_zap_page_range(current->mm, from, PAGE_SIZE);
350 from += PAGE_SIZE;
351 count++;
352 }
354 return 0;
355 }