]>
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 | ||
73395a00 | 6 | #include <linux/module.h> |
ddf63983 | 7 | #include <linux/memblock.h> |
73395a00 AV |
8 | #include <linux/mm.h> |
9 | #include <linux/pfn.h> | |
10 | #include <asm/page.h> | |
d5f20be7 | 11 | #include <asm/sections.h> |
73395a00 AV |
12 | #include <as-layout.h> |
13 | #include <init.h> | |
14 | #include <kern.h> | |
15 | #include <mem_user.h> | |
16 | #include <os.h> | |
1da177e4 | 17 | |
1da177e4 LT |
18 | static int physmem_fd = -1; |
19 | ||
1da177e4 LT |
20 | /* Changed during early boot */ |
21 | unsigned long high_physmem; | |
73395a00 | 22 | EXPORT_SYMBOL(high_physmem); |
1da177e4 | 23 | |
ae173816 | 24 | extern unsigned long long physmem_size; |
1da177e4 | 25 | |
9e6a57d2 | 26 | void __init mem_total_pages(unsigned long physmem, unsigned long iomem, |
97a1fcbb | 27 | unsigned long highmem) |
1da177e4 | 28 | { |
9e6a57d2 HL |
29 | unsigned long phys_pages, highmem_pages; |
30 | unsigned long iomem_pages, total_pages; | |
1da177e4 | 31 | |
9e6a57d2 HL |
32 | phys_pages = physmem >> PAGE_SHIFT; |
33 | iomem_pages = iomem >> PAGE_SHIFT; | |
1da177e4 | 34 | highmem_pages = highmem >> PAGE_SHIFT; |
1da177e4 | 35 | |
9e6a57d2 | 36 | total_pages = phys_pages + iomem_pages + highmem_pages; |
1da177e4 LT |
37 | |
38 | max_mapnr = total_pages; | |
1da177e4 LT |
39 | } |
40 | ||
1da177e4 LT |
41 | void map_memory(unsigned long virt, unsigned long phys, unsigned long len, |
42 | int r, int w, int x) | |
43 | { | |
44 | __u64 offset; | |
45 | int fd, err; | |
46 | ||
47 | fd = phys_mapping(phys, &offset); | |
48 | err = os_map_memory((void *) virt, fd, offset, len, r, w, x); | |
6d536e4b JD |
49 | if (err) { |
50 | if (err == -ENOMEM) | |
ba180fd4 | 51 | printk(KERN_ERR "try increasing the host's " |
1da177e4 LT |
52 | "/proc/sys/vm/max_map_count to <physical " |
53 | "memory size>/4096\n"); | |
54 | panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, " | |
55 | "err = %d\n", virt, fd, offset, len, r, w, x, err); | |
56 | } | |
57 | } | |
58 | ||
fe205bdd TM |
59 | /** |
60 | * setup_physmem() - Setup physical memory for UML | |
61 | * @start: Start address of the physical kernel memory, | |
62 | * i.e start address of the executable image. | |
63 | * @reserve_end: end address of the physical kernel memory. | |
64 | * @len: Length of total physical memory that should be mapped/made | |
65 | * available, in bytes. | |
66 | * @highmem: Number of highmem bytes that should be mapped/made available. | |
67 | * | |
68 | * Creates an unlinked temporary file of size (len + highmem) and memory maps | |
69 | * it on the last executable image address (uml_reserved). | |
70 | * | |
71 | * The offset is needed as the length of the total physical memory | |
72 | * (len + highmem) includes the size of the memory used be the executable image, | |
73 | * but the mapped-to address is the last address of the executable image | |
74 | * (uml_reserved == end address of executable image). | |
75 | * | |
76 | * The memory mapped memory of the temporary file is used as backing memory | |
77 | * of all user space processes/kernel tasks. | |
78 | */ | |
97a1fcbb JD |
79 | void __init setup_physmem(unsigned long start, unsigned long reserve_end, |
80 | unsigned long len, unsigned long long highmem) | |
1da177e4 LT |
81 | { |
82 | unsigned long reserve = reserve_end - start; | |
ddf63983 | 83 | long map_size = len - reserve; |
fe205bdd TM |
84 | int err; |
85 | ||
fe205bdd | 86 | if(map_size <= 0) { |
0936d4f3 | 87 | os_warn("Too few physical memory! Needed=%lu, given=%lu\n", |
ddf63983 | 88 | reserve, len); |
fe205bdd TM |
89 | exit(1); |
90 | } | |
1da177e4 LT |
91 | |
92 | physmem_fd = create_mem_file(len + highmem); | |
93 | ||
ddf63983 | 94 | err = os_map_memory((void *) reserve_end, physmem_fd, reserve, |
fe205bdd | 95 | map_size, 1, 1, 1); |
6d536e4b | 96 | if (err < 0) { |
0936d4f3 MH |
97 | os_warn("setup_physmem - mapping %ld bytes of memory at 0x%p " |
98 | "failed - errno = %d\n", map_size, | |
be6ec5b1 | 99 | (void *) reserve_end, err); |
1da177e4 LT |
100 | exit(1); |
101 | } | |
102 | ||
ba180fd4 JD |
103 | /* |
104 | * Special kludge - This page will be mapped in to userspace processes | |
d67b569f JD |
105 | * from physmem_fd, so it needs to be written out there. |
106 | */ | |
05eacfd0 NI |
107 | os_seek_file(physmem_fd, __pa(__syscall_stub_start)); |
108 | os_write_file(physmem_fd, __syscall_stub_start, PAGE_SIZE); | |
0565103d | 109 | os_fsync_file(physmem_fd); |
d67b569f | 110 | |
ddf63983 MR |
111 | memblock_add(__pa(start), len + highmem); |
112 | memblock_reserve(__pa(start), reserve); | |
113 | ||
114 | min_low_pfn = PFN_UP(__pa(reserve_end)); | |
115 | max_low_pfn = min_low_pfn + (map_size >> PAGE_SHIFT); | |
1da177e4 LT |
116 | } |
117 | ||
0a7675aa | 118 | int phys_mapping(unsigned long phys, unsigned long long *offset_out) |
1da177e4 | 119 | { |
1da177e4 LT |
120 | int fd = -1; |
121 | ||
6d536e4b | 122 | if (phys < physmem_size) { |
1da177e4 LT |
123 | fd = physmem_fd; |
124 | *offset_out = phys; | |
125 | } | |
6d536e4b | 126 | else if (phys < __pa(end_iomem)) { |
1da177e4 LT |
127 | struct iomem_region *region = iomem_regions; |
128 | ||
6d536e4b JD |
129 | while (region != NULL) { |
130 | if ((phys >= region->phys) && | |
131 | (phys < region->phys + region->size)) { | |
1da177e4 LT |
132 | fd = region->fd; |
133 | *offset_out = phys - region->phys; | |
134 | break; | |
135 | } | |
136 | region = region->next; | |
137 | } | |
138 | } | |
6d536e4b | 139 | else if (phys < __pa(end_iomem) + highmem) { |
1da177e4 LT |
140 | fd = physmem_fd; |
141 | *offset_out = phys - iomem_size; | |
142 | } | |
143 | ||
60678bbc | 144 | return fd; |
1da177e4 LT |
145 | } |
146 | ||
147 | static int __init uml_mem_setup(char *line, int *add) | |
148 | { | |
149 | char *retptr; | |
150 | physmem_size = memparse(line,&retptr); | |
151 | return 0; | |
152 | } | |
153 | __uml_setup("mem=", uml_mem_setup, | |
154 | "mem=<Amount of desired ram>\n" | |
155 | " This controls how much \"physical\" memory the kernel allocates\n" | |
156 | " for the system. The size is specified as a number followed by\n" | |
157 | " one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" | |
158 | " This is not related to the amount of memory in the host. It can\n" | |
159 | " be more, and the excess, if it's ever used, will just be swapped out.\n" | |
160 | " Example: mem=64M\n\n" | |
161 | ); | |
162 | ||
94c282d7 JD |
163 | extern int __init parse_iomem(char *str, int *add); |
164 | ||
165 | __uml_setup("iomem=", parse_iomem, | |
166 | "iomem=<name>,<file>\n" | |
167 | " Configure <file> as an IO memory region named <name>.\n\n" | |
168 | ); | |
169 | ||
170 | /* | |
171 | * This list is constructed in parse_iomem and addresses filled in in | |
172 | * setup_iomem, both of which run during early boot. Afterwards, it's | |
173 | * unchanged. | |
174 | */ | |
80e39311 | 175 | struct iomem_region *iomem_regions; |
94c282d7 | 176 | |
80e39311 JD |
177 | /* Initialized in parse_iomem and unchanged thereafter */ |
178 | int iomem_size; | |
94c282d7 | 179 | |
1da177e4 LT |
180 | unsigned long find_iomem(char *driver, unsigned long *len_out) |
181 | { | |
182 | struct iomem_region *region = iomem_regions; | |
183 | ||
6d536e4b JD |
184 | while (region != NULL) { |
185 | if (!strcmp(region->driver, driver)) { | |
1da177e4 | 186 | *len_out = region->size; |
60678bbc | 187 | return region->virt; |
1da177e4 | 188 | } |
c39e50b4 VV |
189 | |
190 | region = region->next; | |
1da177e4 LT |
191 | } |
192 | ||
60678bbc | 193 | return 0; |
1da177e4 | 194 | } |
73395a00 | 195 | EXPORT_SYMBOL(find_iomem); |
1da177e4 | 196 | |
99764fa4 | 197 | static int setup_iomem(void) |
1da177e4 LT |
198 | { |
199 | struct iomem_region *region = iomem_regions; | |
200 | unsigned long iomem_start = high_physmem + PAGE_SIZE; | |
201 | int err; | |
202 | ||
6d536e4b | 203 | while (region != NULL) { |
1da177e4 LT |
204 | err = os_map_memory((void *) iomem_start, region->fd, 0, |
205 | region->size, 1, 1, 0); | |
6d536e4b | 206 | if (err) |
ba180fd4 JD |
207 | printk(KERN_ERR "Mapping iomem region for driver '%s' " |
208 | "failed, errno = %d\n", region->driver, -err); | |
1da177e4 LT |
209 | else { |
210 | region->virt = iomem_start; | |
211 | region->phys = __pa(region->virt); | |
212 | } | |
213 | ||
214 | iomem_start += region->size + PAGE_SIZE; | |
215 | region = region->next; | |
216 | } | |
217 | ||
60678bbc | 218 | return 0; |
1da177e4 LT |
219 | } |
220 | ||
221 | __initcall(setup_iomem); |