blob: 3ba6e4c841da66454b4e50625fb4ee65f5d0af8f [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"
Dave Hansen22a98352006-03-27 01:16:04 -080012#include "linux/pfn.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include "asm/types.h"
14#include "asm/pgtable.h"
15#include "kern_util.h"
Jeff Dike4ff83ce2007-05-06 14:51:08 -070016#include "as-layout.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include "mode_kern.h"
18#include "mem.h"
19#include "mem_user.h"
20#include "os.h"
21#include "kern.h"
22#include "init.h"
23
Linus Torvalds1da177e2005-04-16 15:20:36 -070024static int physmem_fd = -1;
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026/* Changed during early boot */
27unsigned long high_physmem;
28
Jeff Dikeae173812005-11-07 00:58:57 -080029extern unsigned long long physmem_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Linus Torvalds1da177e2005-04-16 15:20:36 -070031int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
32{
33 struct page *p, *map;
34 unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
35 unsigned long iomem_len, iomem_pages, total_len, total_pages;
36 int i;
37
38 phys_pages = physmem >> PAGE_SHIFT;
39 phys_len = phys_pages * sizeof(struct page);
40
41 iomem_pages = iomem >> PAGE_SHIFT;
42 iomem_len = iomem_pages * sizeof(struct page);
43
44 highmem_pages = highmem >> PAGE_SHIFT;
45 highmem_len = highmem_pages * sizeof(struct page);
46
47 total_pages = phys_pages + iomem_pages + highmem_pages;
Paolo 'Blaisorblade' Giarrusso3dfd95b2006-02-01 03:06:26 -080048 total_len = phys_len + iomem_len + highmem_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50 if(kmalloc_ok){
51 map = kmalloc(total_len, GFP_KERNEL);
52 if(map == NULL)
53 map = vmalloc(total_len);
54 }
55 else map = alloc_bootmem_low_pages(total_len);
56
57 if(map == NULL)
Jeff Dike60678bb2007-02-10 01:44:10 -080058 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60 for(i = 0; i < total_pages; i++){
61 p = &map[i];
Nick Piggin70dc9912006-03-22 00:08:35 -080062 memset(p, 0, sizeof(struct page));
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 SetPageReserved(p);
64 INIT_LIST_HEAD(&p->lru);
65 }
66
67 max_mapnr = total_pages;
Jeff Dike60678bb2007-02-10 01:44:10 -080068 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070069}
70
Linus Torvalds1da177e2005-04-16 15:20:36 -070071/* Changed during early boot */
72static unsigned long kmem_top = 0;
73
74unsigned long get_kmem_end(void)
75{
76 if(kmem_top == 0)
77 kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
Jeff Dike60678bb2007-02-10 01:44:10 -080078 return kmem_top;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079}
80
81void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
82 int r, int w, int x)
83{
84 __u64 offset;
85 int fd, err;
86
87 fd = phys_mapping(phys, &offset);
88 err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
89 if(err) {
90 if(err == -ENOMEM)
91 printk("try increasing the host's "
92 "/proc/sys/vm/max_map_count to <physical "
93 "memory size>/4096\n");
94 panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
95 "err = %d\n", virt, fd, offset, len, r, w, x, err);
96 }
97}
98
Jeff Dike23bbd582006-07-10 04:45:06 -070099extern int __syscall_stub_start;
Jeff Diked67b5692005-07-07 17:56:49 -0700100
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101void setup_physmem(unsigned long start, unsigned long reserve_end,
Jeff Dikeae173812005-11-07 00:58:57 -0800102 unsigned long len, unsigned long long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
104 unsigned long reserve = reserve_end - start;
105 int pfn = PFN_UP(__pa(reserve_end));
106 int delta = (len - reserve) >> PAGE_SHIFT;
107 int err, offset, bootmap_size;
108
109 physmem_fd = create_mem_file(len + highmem);
110
111 offset = uml_reserved - uml_physmem;
112 err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
113 len - offset, 1, 1, 0);
114 if(err < 0){
115 os_print_error(err, "Mapping memory");
116 exit(1);
117 }
118
Jeff Diked67b5692005-07-07 17:56:49 -0700119 /* Special kludge - This page will be mapped in to userspace processes
120 * from physmem_fd, so it needs to be written out there.
121 */
122 os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700123 os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
Jeff Diked67b5692005-07-07 17:56:49 -0700124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 bootmap_size = init_bootmem(pfn, pfn + delta);
126 free_bootmem(__pa(reserve_end) + bootmap_size,
127 len - bootmap_size - reserve);
128}
129
130int phys_mapping(unsigned long phys, __u64 *offset_out)
131{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 int fd = -1;
133
Jeff Dike16dd07b2007-05-06 14:51:48 -0700134 if(phys < physmem_size){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 fd = physmem_fd;
136 *offset_out = phys;
137 }
138 else if(phys < __pa(end_iomem)){
139 struct iomem_region *region = iomem_regions;
140
141 while(region != NULL){
142 if((phys >= region->phys) &&
143 (phys < region->phys + region->size)){
144 fd = region->fd;
145 *offset_out = phys - region->phys;
146 break;
147 }
148 region = region->next;
149 }
150 }
151 else if(phys < __pa(end_iomem) + highmem){
152 fd = physmem_fd;
153 *offset_out = phys - iomem_size;
154 }
155
Jeff Dike60678bb2007-02-10 01:44:10 -0800156 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157}
158
159static int __init uml_mem_setup(char *line, int *add)
160{
161 char *retptr;
162 physmem_size = memparse(line,&retptr);
163 return 0;
164}
165__uml_setup("mem=", uml_mem_setup,
166"mem=<Amount of desired ram>\n"
167" This controls how much \"physical\" memory the kernel allocates\n"
168" for the system. The size is specified as a number followed by\n"
169" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
170" This is not related to the amount of memory in the host. It can\n"
171" be more, and the excess, if it's ever used, will just be swapped out.\n"
172" Example: mem=64M\n\n"
173);
174
Jeff Dike94c282d2007-02-10 01:44:09 -0800175extern int __init parse_iomem(char *str, int *add);
176
177__uml_setup("iomem=", parse_iomem,
178"iomem=<name>,<file>\n"
179" Configure <file> as an IO memory region named <name>.\n\n"
180);
181
182/*
183 * This list is constructed in parse_iomem and addresses filled in in
184 * setup_iomem, both of which run during early boot. Afterwards, it's
185 * unchanged.
186 */
187struct iomem_region *iomem_regions = NULL;
188
189/* Initialized in parse_iomem */
190int iomem_size = 0;
191
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192unsigned long find_iomem(char *driver, unsigned long *len_out)
193{
194 struct iomem_region *region = iomem_regions;
195
196 while(region != NULL){
197 if(!strcmp(region->driver, driver)){
198 *len_out = region->size;
Jeff Dike60678bb2007-02-10 01:44:10 -0800199 return region->virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 }
Victor V. Vengerovc39e50b2006-05-01 12:15:53 -0700201
202 region = region->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 }
204
Jeff Dike60678bb2007-02-10 01:44:10 -0800205 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206}
207
208int setup_iomem(void)
209{
210 struct iomem_region *region = iomem_regions;
211 unsigned long iomem_start = high_physmem + PAGE_SIZE;
212 int err;
213
214 while(region != NULL){
215 err = os_map_memory((void *) iomem_start, region->fd, 0,
216 region->size, 1, 1, 0);
217 if(err)
218 printk("Mapping iomem region for driver '%s' failed, "
219 "errno = %d\n", region->driver, -err);
220 else {
221 region->virt = iomem_start;
222 region->phys = __pa(region->virt);
223 }
224
225 iomem_start += region->size + PAGE_SIZE;
226 region = region->next;
227 }
228
Jeff Dike60678bb2007-02-10 01:44:10 -0800229 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
231
232__initcall(setup_iomem);