blob: a55d221d8a4c337c9311d16ad46e9d7e7e8678a4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Jeff Dike6d536e42007-10-16 01:26:47 -07002 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Licensed under the GPL
4 */
5
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include "linux/bootmem.h"
Jeff Dike6d536e42007-10-16 01:26:47 -07007#include "linux/mm.h"
Dave Hansen22a98352006-03-27 01:16:04 -08008#include "linux/pfn.h"
Jeff Dike6d536e42007-10-16 01:26:47 -07009#include "asm/page.h"
Jeff Dike4ff83ce2007-05-06 14:51:08 -070010#include "as-layout.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include "init.h"
Jeff Dike6d536e42007-10-16 01:26:47 -070012#include "kern.h"
Jeff Dike77bf4402007-10-16 01:26:58 -070013#include "mem_user.h"
Jeff Dike6d536e42007-10-16 01:26:47 -070014#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070015
Linus Torvalds1da177e2005-04-16 15:20:36 -070016static int physmem_fd = -1;
17
Linus Torvalds1da177e2005-04-16 15:20:36 -070018/* Changed during early boot */
19unsigned long high_physmem;
20
Jeff Dikeae173812005-11-07 00:58:57 -080021extern unsigned long long physmem_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Jeff Dike97a1fcb2007-07-23 18:43:48 -070023int __init init_maps(unsigned long physmem, unsigned long iomem,
24 unsigned long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -070025{
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;
Paolo 'Blaisorblade' Giarrusso3dfd95b2006-02-01 03:06:26 -080041 total_len = phys_len + iomem_len + highmem_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Jeff Dike97a1fcb2007-07-23 18:43:48 -070043 map = alloc_bootmem_low_pages(total_len);
Jeff Dike6d536e42007-10-16 01:26:47 -070044 if (map == NULL)
Jeff Dike60678bb2007-02-10 01:44:10 -080045 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
Jeff Dike6d536e42007-10-16 01:26:47 -070047 for (i = 0; i < total_pages; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 p = &map[i];
Nick Piggin70dc9912006-03-22 00:08:35 -080049 memset(p, 0, sizeof(struct page));
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 SetPageReserved(p);
51 INIT_LIST_HEAD(&p->lru);
52 }
53
54 max_mapnr = total_pages;
Jeff Dike60678bb2007-02-10 01:44:10 -080055 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056}
57
Linus Torvalds1da177e2005-04-16 15:20:36 -070058/* Changed during early boot */
59static unsigned long kmem_top = 0;
60
61unsigned long get_kmem_end(void)
62{
Jeff Dike6d536e42007-10-16 01:26:47 -070063 if (kmem_top == 0)
Jeff Dike77bf4402007-10-16 01:26:58 -070064 kmem_top = host_task_size - 1024 * 1024;
Jeff Dike60678bb2007-02-10 01:44:10 -080065 return kmem_top;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066}
67
68void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
69 int r, int w, int x)
70{
71 __u64 offset;
72 int fd, err;
73
74 fd = phys_mapping(phys, &offset);
75 err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
Jeff Dike6d536e42007-10-16 01:26:47 -070076 if (err) {
77 if (err == -ENOMEM)
Jeff Dikeba180fd2007-10-16 01:27:00 -070078 printk(KERN_ERR "try increasing the host's "
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 "/proc/sys/vm/max_map_count to <physical "
80 "memory size>/4096\n");
81 panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
82 "err = %d\n", virt, fd, offset, len, r, w, x, err);
83 }
84}
85
Jeff Dike23bbd582006-07-10 04:45:06 -070086extern int __syscall_stub_start;
Jeff Diked67b5692005-07-07 17:56:49 -070087
Jeff Dike97a1fcb2007-07-23 18:43:48 -070088void __init setup_physmem(unsigned long start, unsigned long reserve_end,
89 unsigned long len, unsigned long long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
91 unsigned long reserve = reserve_end - start;
92 int pfn = PFN_UP(__pa(reserve_end));
93 int delta = (len - reserve) >> PAGE_SHIFT;
94 int err, offset, bootmap_size;
95
96 physmem_fd = create_mem_file(len + highmem);
97
98 offset = uml_reserved - uml_physmem;
99 err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
Jeff Dike5c8aace2007-10-16 01:26:46 -0700100 len - offset, 1, 1, 1);
Jeff Dike6d536e42007-10-16 01:26:47 -0700101 if (err < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 os_print_error(err, "Mapping memory");
103 exit(1);
104 }
105
Jeff Dikeba180fd2007-10-16 01:27:00 -0700106 /*
107 * Special kludge - This page will be mapped in to userspace processes
Jeff Diked67b5692005-07-07 17:56:49 -0700108 * from physmem_fd, so it needs to be written out there.
109 */
110 os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700111 os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
Jeff Diked67b5692005-07-07 17:56:49 -0700112
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 bootmap_size = init_bootmem(pfn, pfn + delta);
114 free_bootmem(__pa(reserve_end) + bootmap_size,
115 len - bootmap_size - reserve);
116}
117
118int phys_mapping(unsigned long phys, __u64 *offset_out)
119{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 int fd = -1;
121
Jeff Dike6d536e42007-10-16 01:26:47 -0700122 if (phys < physmem_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 fd = physmem_fd;
124 *offset_out = phys;
125 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700126 else if (phys < __pa(end_iomem)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 struct iomem_region *region = iomem_regions;
128
Jeff Dike6d536e42007-10-16 01:26:47 -0700129 while (region != NULL) {
130 if ((phys >= region->phys) &&
131 (phys < region->phys + region->size)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 fd = region->fd;
133 *offset_out = phys - region->phys;
134 break;
135 }
136 region = region->next;
137 }
138 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700139 else if (phys < __pa(end_iomem) + highmem) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 fd = physmem_fd;
141 *offset_out = phys - iomem_size;
142 }
143
Jeff Dike60678bb2007-02-10 01:44:10 -0800144 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145}
146
147static 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
Jeff Dike94c282d2007-02-10 01:44:09 -0800163extern 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 */
175struct iomem_region *iomem_regions = NULL;
176
177/* Initialized in parse_iomem */
178int iomem_size = 0;
179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180unsigned long find_iomem(char *driver, unsigned long *len_out)
181{
182 struct iomem_region *region = iomem_regions;
183
Jeff Dike6d536e42007-10-16 01:26:47 -0700184 while (region != NULL) {
185 if (!strcmp(region->driver, driver)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 *len_out = region->size;
Jeff Dike60678bb2007-02-10 01:44:10 -0800187 return region->virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 }
Victor V. Vengerovc39e50b2006-05-01 12:15:53 -0700189
190 region = region->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
192
Jeff Dike60678bb2007-02-10 01:44:10 -0800193 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194}
195
196int setup_iomem(void)
197{
198 struct iomem_region *region = iomem_regions;
199 unsigned long iomem_start = high_physmem + PAGE_SIZE;
200 int err;
201
Jeff Dike6d536e42007-10-16 01:26:47 -0700202 while (region != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 err = os_map_memory((void *) iomem_start, region->fd, 0,
204 region->size, 1, 1, 0);
Jeff Dike6d536e42007-10-16 01:26:47 -0700205 if (err)
Jeff Dikeba180fd2007-10-16 01:27:00 -0700206 printk(KERN_ERR "Mapping iomem region for driver '%s' "
207 "failed, errno = %d\n", region->driver, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 else {
209 region->virt = iomem_start;
210 region->phys = __pa(region->virt);
211 }
212
213 iomem_start += region->size + PAGE_SIZE;
214 region = region->next;
215 }
216
Jeff Dike60678bb2007-02-10 01:44:10 -0800217 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218}
219
220__initcall(setup_iomem);