blob: 544665e04513b33b4106ccd564be392e22d09b8f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
3 * Licensed under the GPL
4 */
5
6#include "linux/mm.h"
7#include "linux/rbtree.h"
8#include "linux/slab.h"
9#include "linux/vmalloc.h"
10#include "linux/bootmem.h"
11#include "linux/module.h"
12#include "asm/types.h"
13#include "asm/pgtable.h"
14#include "kern_util.h"
15#include "user_util.h"
16#include "mode_kern.h"
17#include "mem.h"
18#include "mem_user.h"
19#include "os.h"
20#include "kern.h"
21#include "init.h"
22
23struct phys_desc {
24 struct rb_node rb;
25 int fd;
26 __u64 offset;
27 void *virt;
28 unsigned long phys;
29 struct list_head list;
30};
31
32static struct rb_root phys_mappings = RB_ROOT;
33
34static struct rb_node **find_rb(void *virt)
35{
36 struct rb_node **n = &phys_mappings.rb_node;
37 struct phys_desc *d;
38
39 while(*n != NULL){
40 d = rb_entry(*n, struct phys_desc, rb);
41 if(d->virt == virt)
42 return(n);
43
44 if(d->virt > virt)
45 n = &(*n)->rb_left;
46 else
47 n = &(*n)->rb_right;
48 }
49
50 return(n);
51}
52
53static struct phys_desc *find_phys_mapping(void *virt)
54{
55 struct rb_node **n = find_rb(virt);
56
57 if(*n == NULL)
58 return(NULL);
59
60 return(rb_entry(*n, struct phys_desc, rb));
61}
62
63static void insert_phys_mapping(struct phys_desc *desc)
64{
65 struct rb_node **n = find_rb(desc->virt);
66
67 if(*n != NULL)
68 panic("Physical remapping for %p already present",
69 desc->virt);
70
71 rb_link_node(&desc->rb, (*n)->rb_parent, n);
72 rb_insert_color(&desc->rb, &phys_mappings);
73}
74
75LIST_HEAD(descriptor_mappings);
76
77struct desc_mapping {
78 int fd;
79 struct list_head list;
80 struct list_head pages;
81};
82
83static struct desc_mapping *find_mapping(int fd)
84{
85 struct desc_mapping *desc;
86 struct list_head *ele;
87
88 list_for_each(ele, &descriptor_mappings){
89 desc = list_entry(ele, struct desc_mapping, list);
90 if(desc->fd == fd)
91 return(desc);
92 }
93
94 return(NULL);
95}
96
97static struct desc_mapping *descriptor_mapping(int fd)
98{
99 struct desc_mapping *desc;
100
101 desc = find_mapping(fd);
102 if(desc != NULL)
103 return(desc);
104
105 desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
106 if(desc == NULL)
107 return(NULL);
108
109 *desc = ((struct desc_mapping)
110 { .fd = fd,
111 .list = LIST_HEAD_INIT(desc->list),
112 .pages = LIST_HEAD_INIT(desc->pages) });
113 list_add(&desc->list, &descriptor_mappings);
114
115 return(desc);
116}
117
118int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
119{
120 struct desc_mapping *fd_maps;
121 struct phys_desc *desc;
122 unsigned long phys;
123 int err;
124
125 fd_maps = descriptor_mapping(fd);
126 if(fd_maps == NULL)
127 return(-ENOMEM);
128
129 phys = __pa(virt);
130 desc = find_phys_mapping(virt);
131 if(desc != NULL)
132 panic("Address 0x%p is already substituted\n", virt);
133
134 err = -ENOMEM;
135 desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
136 if(desc == NULL)
137 goto out;
138
139 *desc = ((struct phys_desc)
140 { .fd = fd,
141 .offset = offset,
142 .virt = virt,
143 .phys = __pa(virt),
144 .list = LIST_HEAD_INIT(desc->list) });
145 insert_phys_mapping(desc);
146
147 list_add(&desc->list, &fd_maps->pages);
148
149 virt = (void *) ((unsigned long) virt & PAGE_MASK);
150 err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
151 if(!err)
152 goto out;
153
154 rb_erase(&desc->rb, &phys_mappings);
155 kfree(desc);
156 out:
157 return(err);
158}
159
160static int physmem_fd = -1;
161
162static void remove_mapping(struct phys_desc *desc)
163{
164 void *virt = desc->virt;
165 int err;
166
167 rb_erase(&desc->rb, &phys_mappings);
168 list_del(&desc->list);
169 kfree(desc);
170
171 err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
172 if(err)
173 panic("Failed to unmap block device page from physical memory, "
174 "errno = %d", -err);
175}
176
177int physmem_remove_mapping(void *virt)
178{
179 struct phys_desc *desc;
180
181 virt = (void *) ((unsigned long) virt & PAGE_MASK);
182 desc = find_phys_mapping(virt);
183 if(desc == NULL)
184 return(0);
185
186 remove_mapping(desc);
187 return(1);
188}
189
190void physmem_forget_descriptor(int fd)
191{
192 struct desc_mapping *desc;
193 struct phys_desc *page;
194 struct list_head *ele, *next;
195 __u64 offset;
196 void *addr;
197 int err;
198
199 desc = find_mapping(fd);
200 if(desc == NULL)
201 return;
202
203 list_for_each_safe(ele, next, &desc->pages){
204 page = list_entry(ele, struct phys_desc, list);
205 offset = page->offset;
206 addr = page->virt;
207 remove_mapping(page);
208 err = os_seek_file(fd, offset);
209 if(err)
210 panic("physmem_forget_descriptor - failed to seek "
211 "to %lld in fd %d, error = %d\n",
212 offset, fd, -err);
213 err = os_read_file(fd, addr, PAGE_SIZE);
214 if(err < 0)
215 panic("physmem_forget_descriptor - failed to read "
216 "from fd %d to 0x%p, error = %d\n",
217 fd, addr, -err);
218 }
219
220 list_del(&desc->list);
221 kfree(desc);
222}
223
224EXPORT_SYMBOL(physmem_forget_descriptor);
225EXPORT_SYMBOL(physmem_remove_mapping);
226EXPORT_SYMBOL(physmem_subst_mapping);
227
228void arch_free_page(struct page *page, int order)
229{
230 void *virt;
231 int i;
232
233 for(i = 0; i < (1 << order); i++){
234 virt = __va(page_to_phys(page + i));
235 physmem_remove_mapping(virt);
236 }
237}
238
239int is_remapped(void *virt)
240{
241 struct phys_desc *desc = find_phys_mapping(virt);
242
243 return(desc != NULL);
244}
245
246/* Changed during early boot */
247unsigned long high_physmem;
248
Jeff Dikeae173812005-11-07 00:58:57 -0800249extern unsigned long long physmem_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
252{
253 struct page *p, *map;
254 unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
255 unsigned long iomem_len, iomem_pages, total_len, total_pages;
256 int i;
257
258 phys_pages = physmem >> PAGE_SHIFT;
259 phys_len = phys_pages * sizeof(struct page);
260
261 iomem_pages = iomem >> PAGE_SHIFT;
262 iomem_len = iomem_pages * sizeof(struct page);
263
264 highmem_pages = highmem >> PAGE_SHIFT;
265 highmem_len = highmem_pages * sizeof(struct page);
266
267 total_pages = phys_pages + iomem_pages + highmem_pages;
Paolo 'Blaisorblade' Giarrusso3dfd95b2006-02-01 03:06:26 -0800268 total_len = phys_len + iomem_len + highmem_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
270 if(kmalloc_ok){
271 map = kmalloc(total_len, GFP_KERNEL);
272 if(map == NULL)
273 map = vmalloc(total_len);
274 }
275 else map = alloc_bootmem_low_pages(total_len);
276
277 if(map == NULL)
278 return(-ENOMEM);
279
280 for(i = 0; i < total_pages; i++){
281 p = &map[i];
282 set_page_count(p, 0);
283 SetPageReserved(p);
284 INIT_LIST_HEAD(&p->lru);
285 }
286
287 max_mapnr = total_pages;
288 return(0);
289}
290
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291/* Changed during early boot */
292static unsigned long kmem_top = 0;
293
294unsigned long get_kmem_end(void)
295{
296 if(kmem_top == 0)
297 kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
298 return(kmem_top);
299}
300
301void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
302 int r, int w, int x)
303{
304 __u64 offset;
305 int fd, err;
306
307 fd = phys_mapping(phys, &offset);
308 err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
309 if(err) {
310 if(err == -ENOMEM)
311 printk("try increasing the host's "
312 "/proc/sys/vm/max_map_count to <physical "
313 "memory size>/4096\n");
314 panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
315 "err = %d\n", virt, fd, offset, len, r, w, x, err);
316 }
317}
318
319#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
320
Jeff Diked67b5692005-07-07 17:56:49 -0700321extern int __syscall_stub_start, __binary_start;
322
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323void setup_physmem(unsigned long start, unsigned long reserve_end,
Jeff Dikeae173812005-11-07 00:58:57 -0800324 unsigned long len, unsigned long long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
326 unsigned long reserve = reserve_end - start;
327 int pfn = PFN_UP(__pa(reserve_end));
328 int delta = (len - reserve) >> PAGE_SHIFT;
329 int err, offset, bootmap_size;
330
331 physmem_fd = create_mem_file(len + highmem);
332
333 offset = uml_reserved - uml_physmem;
334 err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
335 len - offset, 1, 1, 0);
336 if(err < 0){
337 os_print_error(err, "Mapping memory");
338 exit(1);
339 }
340
Jeff Diked67b5692005-07-07 17:56:49 -0700341 /* Special kludge - This page will be mapped in to userspace processes
342 * from physmem_fd, so it needs to be written out there.
343 */
344 os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
345 os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 bootmap_size = init_bootmem(pfn, pfn + delta);
348 free_bootmem(__pa(reserve_end) + bootmap_size,
349 len - bootmap_size - reserve);
350}
351
352int phys_mapping(unsigned long phys, __u64 *offset_out)
353{
354 struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
355 int fd = -1;
356
357 if(desc != NULL){
358 fd = desc->fd;
359 *offset_out = desc->offset;
360 }
361 else if(phys < physmem_size){
362 fd = physmem_fd;
363 *offset_out = phys;
364 }
365 else if(phys < __pa(end_iomem)){
366 struct iomem_region *region = iomem_regions;
367
368 while(region != NULL){
369 if((phys >= region->phys) &&
370 (phys < region->phys + region->size)){
371 fd = region->fd;
372 *offset_out = phys - region->phys;
373 break;
374 }
375 region = region->next;
376 }
377 }
378 else if(phys < __pa(end_iomem) + highmem){
379 fd = physmem_fd;
380 *offset_out = phys - iomem_size;
381 }
382
383 return(fd);
384}
385
386static int __init uml_mem_setup(char *line, int *add)
387{
388 char *retptr;
389 physmem_size = memparse(line,&retptr);
390 return 0;
391}
392__uml_setup("mem=", uml_mem_setup,
393"mem=<Amount of desired ram>\n"
394" This controls how much \"physical\" memory the kernel allocates\n"
395" for the system. The size is specified as a number followed by\n"
396" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
397" This is not related to the amount of memory in the host. It can\n"
398" be more, and the excess, if it's ever used, will just be swapped out.\n"
399" Example: mem=64M\n\n"
400);
401
402unsigned long find_iomem(char *driver, unsigned long *len_out)
403{
404 struct iomem_region *region = iomem_regions;
405
406 while(region != NULL){
407 if(!strcmp(region->driver, driver)){
408 *len_out = region->size;
409 return(region->virt);
410 }
411 }
412
413 return(0);
414}
415
416int setup_iomem(void)
417{
418 struct iomem_region *region = iomem_regions;
419 unsigned long iomem_start = high_physmem + PAGE_SIZE;
420 int err;
421
422 while(region != NULL){
423 err = os_map_memory((void *) iomem_start, region->fd, 0,
424 region->size, 1, 1, 0);
425 if(err)
426 printk("Mapping iomem region for driver '%s' failed, "
427 "errno = %d\n", region->driver, -err);
428 else {
429 region->virt = iomem_start;
430 region->phys = __pa(region->virt);
431 }
432
433 iomem_start += region->size + PAGE_SIZE;
434 region = region->next;
435 }
436
437 return(0);
438}
439
440__initcall(setup_iomem);
441
442/*
443 * Overrides for Emacs so that we follow Linus's tabbing style.
444 * Emacs will notice this stuff at the end of the file and automatically
445 * adjust the settings for this buffer only. This must remain at the end
446 * of the file.
447 * ---------------------------------------------------------------------------
448 * Local variables:
449 * c-file-style: "linux"
450 * End:
451 */