Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
6d536e4b | 2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
1da177e4 LT |
3 | * Licensed under the GPL |
4 | */ | |
5 | ||
1da177e4 | 6 | #include "linux/bootmem.h" |
6d536e4b | 7 | #include "linux/mm.h" |
22a9835c | 8 | #include "linux/pfn.h" |
6d536e4b | 9 | #include "asm/page.h" |
4ff83ce1 | 10 | #include "as-layout.h" |
6d536e4b JD |
11 | #include "init.h" |
12 | #include "kern.h" | |
77bf4400 | 13 | #include "mem_user.h" |
1da177e4 | 14 | #include "os.h" |
1da177e4 | 15 | |
1da177e4 LT |
16 | static int physmem_fd = -1; |
17 | ||
1da177e4 LT |
18 | /* Changed during early boot */ |
19 | unsigned long high_physmem; | |
20 | ||
ae173816 | 21 | extern unsigned long long physmem_size; |
1da177e4 | 22 | |
97a1fcbb JD |
23 | int __init init_maps(unsigned long physmem, unsigned long iomem, |
24 | unsigned long highmem) | |
1da177e4 LT |
25 | { |
26 | struct page *p, *map; | |
27 | unsigned long phys_len, phys_pages, highmem_len, highmem_pages; | |
28 | unsigned long iomem_len, iomem_pages, total_len, total_pages; | |
29 | int i; | |
30 | ||
31 | phys_pages = physmem >> PAGE_SHIFT; | |
32 | phys_len = phys_pages * sizeof(struct page); | |
33 | ||
34 | iomem_pages = iomem >> PAGE_SHIFT; | |
35 | iomem_len = iomem_pages * sizeof(struct page); | |
36 | ||
37 | highmem_pages = highmem >> PAGE_SHIFT; | |
38 | highmem_len = highmem_pages * sizeof(struct page); | |
39 | ||
40 | total_pages = phys_pages + iomem_pages + highmem_pages; | |
3dfd95b3 | 41 | total_len = phys_len + iomem_len + highmem_len; |
1da177e4 | 42 | |
97a1fcbb | 43 | map = alloc_bootmem_low_pages(total_len); |
6d536e4b | 44 | if (map == NULL) |
60678bbc | 45 | return -ENOMEM; |
1da177e4 | 46 | |
6d536e4b | 47 | for (i = 0; i < total_pages; i++) { |
1da177e4 | 48 | p = &map[i]; |
70dc991d | 49 | memset(p, 0, sizeof(struct page)); |
1da177e4 LT |
50 | SetPageReserved(p); |
51 | INIT_LIST_HEAD(&p->lru); | |
52 | } | |
53 | ||
54 | max_mapnr = total_pages; | |
60678bbc | 55 | return 0; |
1da177e4 LT |
56 | } |
57 | ||
1da177e4 LT |
58 | void map_memory(unsigned long virt, unsigned long phys, unsigned long len, |
59 | int r, int w, int x) | |
60 | { | |
61 | __u64 offset; | |
62 | int fd, err; | |
63 | ||
64 | fd = phys_mapping(phys, &offset); | |
65 | err = os_map_memory((void *) virt, fd, offset, len, r, w, x); | |
6d536e4b JD |
66 | if (err) { |
67 | if (err == -ENOMEM) | |
ba180fd4 | 68 | printk(KERN_ERR "try increasing the host's " |
1da177e4 LT |
69 | "/proc/sys/vm/max_map_count to <physical " |
70 | "memory size>/4096\n"); | |
71 | panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, " | |
72 | "err = %d\n", virt, fd, offset, len, r, w, x, err); | |
73 | } | |
74 | } | |
75 | ||
23bbd586 | 76 | extern int __syscall_stub_start; |
d67b569f | 77 | |
97a1fcbb JD |
78 | void __init setup_physmem(unsigned long start, unsigned long reserve_end, |
79 | unsigned long len, unsigned long long highmem) | |
1da177e4 LT |
80 | { |
81 | unsigned long reserve = reserve_end - start; | |
82 | int pfn = PFN_UP(__pa(reserve_end)); | |
83 | int delta = (len - reserve) >> PAGE_SHIFT; | |
84 | int err, offset, bootmap_size; | |
85 | ||
86 | physmem_fd = create_mem_file(len + highmem); | |
87 | ||
88 | offset = uml_reserved - uml_physmem; | |
89 | err = os_map_memory((void *) uml_reserved, physmem_fd, offset, | |
5c8aacea | 90 | len - offset, 1, 1, 1); |
6d536e4b | 91 | if (err < 0) { |
512b6fb1 JD |
92 | printf("setup_physmem - mapping %ld bytes of memory at 0x%p " |
93 | "failed - errno = %d\n", len - offset, | |
94 | (void *) uml_reserved, err); | |
1da177e4 LT |
95 | exit(1); |
96 | } | |
97 | ||
ba180fd4 JD |
98 | /* |
99 | * Special kludge - This page will be mapped in to userspace processes | |
d67b569f JD |
100 | * from physmem_fd, so it needs to be written out there. |
101 | */ | |
102 | os_seek_file(physmem_fd, __pa(&__syscall_stub_start)); | |
a6ea4cce | 103 | os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE); |
d67b569f | 104 | |
1da177e4 LT |
105 | bootmap_size = init_bootmem(pfn, pfn + delta); |
106 | free_bootmem(__pa(reserve_end) + bootmap_size, | |
107 | len - bootmap_size - reserve); | |
108 | } | |
109 | ||
0a7675aa | 110 | int phys_mapping(unsigned long phys, unsigned long long *offset_out) |
1da177e4 | 111 | { |
1da177e4 LT |
112 | int fd = -1; |
113 | ||
6d536e4b | 114 | if (phys < physmem_size) { |
1da177e4 LT |
115 | fd = physmem_fd; |
116 | *offset_out = phys; | |
117 | } | |
6d536e4b | 118 | else if (phys < __pa(end_iomem)) { |
1da177e4 LT |
119 | struct iomem_region *region = iomem_regions; |
120 | ||
6d536e4b JD |
121 | while (region != NULL) { |
122 | if ((phys >= region->phys) && | |
123 | (phys < region->phys + region->size)) { | |
1da177e4 LT |
124 | fd = region->fd; |
125 | *offset_out = phys - region->phys; | |
126 | break; | |
127 | } | |
128 | region = region->next; | |
129 | } | |
130 | } | |
6d536e4b | 131 | else if (phys < __pa(end_iomem) + highmem) { |
1da177e4 LT |
132 | fd = physmem_fd; |
133 | *offset_out = phys - iomem_size; | |
134 | } | |
135 | ||
60678bbc | 136 | return fd; |
1da177e4 LT |
137 | } |
138 | ||
139 | static int __init uml_mem_setup(char *line, int *add) | |
140 | { | |
141 | char *retptr; | |
142 | physmem_size = memparse(line,&retptr); | |
143 | return 0; | |
144 | } | |
145 | __uml_setup("mem=", uml_mem_setup, | |
146 | "mem=<Amount of desired ram>\n" | |
147 | " This controls how much \"physical\" memory the kernel allocates\n" | |
148 | " for the system. The size is specified as a number followed by\n" | |
149 | " one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" | |
150 | " This is not related to the amount of memory in the host. It can\n" | |
151 | " be more, and the excess, if it's ever used, will just be swapped out.\n" | |
152 | " Example: mem=64M\n\n" | |
153 | ); | |
154 | ||
94c282d7 JD |
155 | extern int __init parse_iomem(char *str, int *add); |
156 | ||
157 | __uml_setup("iomem=", parse_iomem, | |
158 | "iomem=<name>,<file>\n" | |
159 | " Configure <file> as an IO memory region named <name>.\n\n" | |
160 | ); | |
161 | ||
162 | /* | |
163 | * This list is constructed in parse_iomem and addresses filled in in | |
164 | * setup_iomem, both of which run during early boot. Afterwards, it's | |
165 | * unchanged. | |
166 | */ | |
167 | struct iomem_region *iomem_regions = NULL; | |
168 | ||
169 | /* Initialized in parse_iomem */ | |
170 | int iomem_size = 0; | |
171 | ||
1da177e4 LT |
172 | unsigned long find_iomem(char *driver, unsigned long *len_out) |
173 | { | |
174 | struct iomem_region *region = iomem_regions; | |
175 | ||
6d536e4b JD |
176 | while (region != NULL) { |
177 | if (!strcmp(region->driver, driver)) { | |
1da177e4 | 178 | *len_out = region->size; |
60678bbc | 179 | return region->virt; |
1da177e4 | 180 | } |
c39e50b4 VV |
181 | |
182 | region = region->next; | |
1da177e4 LT |
183 | } |
184 | ||
60678bbc | 185 | return 0; |
1da177e4 LT |
186 | } |
187 | ||
188 | int setup_iomem(void) | |
189 | { | |
190 | struct iomem_region *region = iomem_regions; | |
191 | unsigned long iomem_start = high_physmem + PAGE_SIZE; | |
192 | int err; | |
193 | ||
6d536e4b | 194 | while (region != NULL) { |
1da177e4 LT |
195 | err = os_map_memory((void *) iomem_start, region->fd, 0, |
196 | region->size, 1, 1, 0); | |
6d536e4b | 197 | if (err) |
ba180fd4 JD |
198 | printk(KERN_ERR "Mapping iomem region for driver '%s' " |
199 | "failed, errno = %d\n", region->driver, -err); | |
1da177e4 LT |
200 | else { |
201 | region->virt = iomem_start; | |
202 | region->phys = __pa(region->virt); | |
203 | } | |
204 | ||
205 | iomem_start += region->size + PAGE_SIZE; | |
206 | region = region->next; | |
207 | } | |
208 | ||
60678bbc | 209 | return 0; |
1da177e4 LT |
210 | } |
211 | ||
212 | __initcall(setup_iomem); |