ia64/xen-unstable

view xenolinux-2.4.16-sparse/arch/xeno/drivers/dom0/dom0_memory.c @ 46:ae8ce588d9bd

bitkeeper revision 1.7.3.25 (3dff11a5AJGEQ-jI9ocfpJBgS5N5iQ)

test
author lynx@idefix.cl.cam.ac.uk
date Tue Dec 17 11:59:33 2002 +0000 (2002-12-17)
parents 6bc0f33acd56
children dbf7fc972188
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 direct_set_pte(pte, direct_mk_pte_phys(phys_addr, prot));
65 forget_pte(oldpage);
66 address += PAGE_SIZE;
67 phys_addr += PAGE_SIZE;
68 pte++;
69 } while (address && (address < end));
71 printk(KERN_ALERT "bd240 debug: exit from direct_remappte_range\n");
72 }
74 static inline int direct_remappmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned long address, unsigned long size,
75 unsigned long phys_addr, pgprot_t prot)
76 {
77 unsigned long end;
79 address &= ~PGDIR_MASK;
80 end = address + size;
81 if (end > PGDIR_SIZE)
82 end = PGDIR_SIZE;
83 phys_addr -= address;
84 do {
85 pte_t * pte = pte_alloc(mm, pmd, address);
86 if (!pte)
87 return -ENOMEM;
88 direct_remappte_range(pte, address, end - address, address + phys_addr, prot);
89 address = (address + PMD_SIZE) & PMD_MASK;
90 pmd++;
91 } while (address && (address < end));
92 return 0;
93 }
95 /* Note: this is only safe if the mm semaphore is held when called. */
96 int direct_remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long size, pgprot_t prot)
97 {
98 int error = 0;
99 pgd_t * dir;
100 unsigned long beg = from;
101 unsigned long end = from + size;
102 struct mm_struct *mm = current->mm;
104 phys_addr -= from;
105 dir = pgd_offset(mm, from);
106 flush_cache_range(mm, beg, end);
107 if (from >= end)
108 BUG();
110 spin_lock(&mm->page_table_lock);
111 do {
112 pmd_t *pmd = pmd_alloc(mm, dir, from);
113 error = -ENOMEM;
114 if (!pmd)
115 break;
116 error = direct_remappmd_range(mm, pmd, from, end - from, phys_addr + from, prot);
117 if (error)
118 break;
119 from = (from + PGDIR_SIZE) & PGDIR_MASK;
120 dir++;
121 } while (from && (from < end));
122 spin_unlock(&mm->page_table_lock);
123 flush_tlb_range(mm, beg, end);
124 return error;
125 }
127 /*
128 * used for remapping discontiguous bits of domain's memory, pages to map are
129 * found from frame table beginning at the given first_pg index
130 */
131 int direct_remap_disc_page_range(unsigned long from, unsigned long first_pg,
132 int tot_pages, pgprot_t prot)
133 {
134 frame_table_t * current_ft;
135 unsigned long current_pfn;
136 unsigned long start = from;
137 int count = 0;
139 current_ft = (frame_table_t *)(frame_table + first_pg);
140 current_pfn = first_pg;
141 while(count < tot_pages){
142 if(direct_remap_page_range(start, current_pfn << PAGE_SHIFT, PAGE_SIZE, prot))
143 goto out;
144 start += PAGE_SIZE;
145 current_pfn = current_ft->next;
146 current_ft = (frame_table_t *)(frame_table + current_pfn);
147 count++;
148 }
150 out:
152 return tot_pages - count;
153 }
155 /* below functions replace standard sys_mmap and sys_munmap which are absolutely useless
156 * for direct memory mapping. direct_zap* functions are minor ammendments to the
157 * original versions in mm/memory.c. the changes are to enable unmapping of real physical
158 * addresses.
159 */
161 unsigned long direct_mmap(unsigned long phys_addr, unsigned long size,
162 pgprot_t prot, int flag, int tot_pages)
163 {
164 direct_mmap_node_t * dmmap;
165 unsigned long addr;
166 int ret = 0;
168 if(!capable(CAP_SYS_ADMIN)){
169 ret = -EPERM;
170 goto out;
171 }
173 /* get unmapped area invokes xen specific arch_get_unmapped_area */
174 addr = get_unmapped_area(NULL, 0, size, 0, 0);
175 if(addr & ~PAGE_MASK){
176 ret = -ENOMEM;
177 goto out;
178 }
180 /* add node on the list of directly mapped areas */
181 dmmap = (direct_mmap_node_t *)kmalloc(GFP_KERNEL, sizeof(direct_mmap_node_t));
182 dmmap->addr = addr;
183 list_add(&dmmap->list, &current->mm->context.direct_list);
185 /* and perform the mapping */
186 if(flag == MAP_DISCONT){
187 ret = direct_remap_disc_page_range(addr, phys_addr, tot_pages, prot);
188 } else {
189 printk(KERN_ALERT "bd240 debug: addr %lx, phys_addr %lx, size %lx\n",
190 addr, phys_addr, size);
191 ret = direct_remap_page_range(addr, phys_addr, size, prot);
192 }
194 if(ret == 0)
195 ret = addr;
197 out:
198 return ret;
199 }
201 /* most of the checks, refcnt updates, cache stuff have been thrown out as they are not
202 * needed
203 */
204 static inline int direct_zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address,
205 unsigned long size)
206 {
207 unsigned long offset;
208 pte_t * ptep;
209 int freed = 0;
211 if (pmd_none(*pmd))
212 return 0;
213 if (pmd_bad(*pmd)) {
214 pmd_ERROR(*pmd);
215 pmd_clear(pmd);
216 return 0;
217 }
218 ptep = pte_offset(pmd, address);
219 offset = address & ~PMD_MASK;
220 if (offset + size > PMD_SIZE)
221 size = PMD_SIZE - offset;
222 size &= PAGE_MASK;
223 for (offset=0; offset < size; ptep++, offset += PAGE_SIZE) {
224 pte_t pte = *ptep;
225 if (pte_none(pte))
226 continue;
227 freed ++;
228 printk(KERN_ALERT "bd240 debug: clearing ptr %lx\n", __pa(ptep) + start_info.phys_base);
229 direct_pte_clear(ptep);
230 }
232 return freed;
233 }
235 static inline int direct_zap_pmd_range(mmu_gather_t *tlb, pgd_t * dir,
236 unsigned long address, unsigned long size)
237 {
238 pmd_t * pmd;
239 unsigned long end;
240 int freed;
242 if (pgd_none(*dir))
243 return 0;
244 if (pgd_bad(*dir)) {
245 pgd_ERROR(*dir);
246 pgd_clear(dir);
247 return 0;
248 }
249 pmd = pmd_offset(dir, address);
250 end = address + size;
251 if (end > ((address + PGDIR_SIZE) & PGDIR_MASK))
252 end = ((address + PGDIR_SIZE) & PGDIR_MASK);
253 freed = 0;
254 do {
255 freed += direct_zap_pte_range(tlb, pmd, address, end - address);
256 address = (address + PMD_SIZE) & PMD_MASK;
257 pmd++;
258 } while (address < end);
259 return freed;
260 }
262 /*
263 * remove user pages in a given range.
264 */
265 void direct_zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
266 {
267 mmu_gather_t *tlb;
268 pgd_t * dir;
269 unsigned long start = address, end = address + size;
270 int freed = 0;
272 dir = pgd_offset(mm, address);
274 /*
275 * This is a long-lived spinlock. That's fine.
276 * There's no contention, because the page table
277 * lock only protects against kswapd anyway, and
278 * even if kswapd happened to be looking at this
279 * process we _want_ it to get stuck.
280 */
281 if (address >= end)
282 BUG();
283 spin_lock(&mm->page_table_lock);
284 flush_cache_range(mm, address, end);
285 tlb = tlb_gather_mmu(mm);
287 do {
288 freed += direct_zap_pmd_range(tlb, dir, address, end - address);
289 address = (address + PGDIR_SIZE) & PGDIR_MASK;
290 dir++;
291 } while (address && (address < end));
293 /* this will flush any remaining tlb entries */
294 tlb_finish_mmu(tlb, start, end);
296 /* decrementing rss removed */
298 spin_unlock(&mm->page_table_lock);
299 }
301 int direct_unmap(unsigned long addr, unsigned long size)
302 {
303 direct_mmap_node_t * node;
304 struct list_head * curr;
305 struct list_head * direct_list = &current->mm->context.direct_list;
307 curr = direct_list->next;
308 while(curr != direct_list){
309 node = list_entry(curr, direct_mmap_node_t, list);
310 if(node->addr == addr)
311 break;
312 curr = curr->next;
313 }
315 if(curr == direct_list)
316 return -1;
318 list_del(&node->list);
319 kfree(node);
321 direct_zap_page_range(current->mm, addr, size);
323 return 0;
324 }
326 int direct_disc_unmap(unsigned long from, unsigned long first_pg, int tot_pages)
327 {
328 int count = 0;
329 direct_mmap_node_t * node;
330 struct list_head * curr;
331 struct list_head * direct_list = &current->mm->context.direct_list;
333 curr = direct_list->next;
334 while(curr != direct_list){
335 node = list_entry(curr, direct_mmap_node_t, list);
336 if(node->addr == from)
337 break;
338 curr = curr->next;
339 }
341 if(curr == direct_list)
342 return -1;
344 list_del(&node->list);
345 kfree(node);
347 while(count < tot_pages){
348 direct_zap_page_range(current->mm, from, PAGE_SIZE);
349 from += PAGE_SIZE;
350 count++;
351 }
353 return 0;
354 }