blob: a1a9090254c29fdd07ad9f982a46d54dd29563ee [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 -070058void 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);
Jeff Dike6d536e42007-10-16 01:26:47 -070066 if (err) {
67 if (err == -ENOMEM)
Jeff Dikeba180fd2007-10-16 01:27:00 -070068 printk(KERN_ERR "try increasing the host's "
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 "/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
Jeff Dike23bbd582006-07-10 04:45:06 -070076extern int __syscall_stub_start;
Jeff Diked67b5692005-07-07 17:56:49 -070077
Jeff Dike97a1fcb2007-07-23 18:43:48 -070078void __init setup_physmem(unsigned long start, unsigned long reserve_end,
79 unsigned long len, unsigned long long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080{
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,
Jeff Dike5c8aace2007-10-16 01:26:46 -070090 len - offset, 1, 1, 1);
Jeff Dike6d536e42007-10-16 01:26:47 -070091 if (err < 0) {
Jeff Dike512b6fb2007-10-16 01:27:11 -070092 printf("setup_physmem - mapping %ld bytes of memory at 0x%p "
93 "failed - errno = %d\n", len - offset,
94 (void *) uml_reserved, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 exit(1);
96 }
97
Jeff Dikeba180fd2007-10-16 01:27:00 -070098 /*
99 * Special kludge - This page will be mapped in to userspace processes
Jeff Diked67b5692005-07-07 17:56:49 -0700100 * from physmem_fd, so it needs to be written out there.
101 */
102 os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700103 os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
Jeff Diked67b5692005-07-07 17:56:49 -0700104
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 bootmap_size = init_bootmem(pfn, pfn + delta);
106 free_bootmem(__pa(reserve_end) + bootmap_size,
107 len - bootmap_size - reserve);
108}
109
Jeff Dike0a7675a2007-10-16 01:27:05 -0700110int phys_mapping(unsigned long phys, unsigned long long *offset_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 int fd = -1;
113
Jeff Dike6d536e42007-10-16 01:26:47 -0700114 if (phys < physmem_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 fd = physmem_fd;
116 *offset_out = phys;
117 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700118 else if (phys < __pa(end_iomem)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 struct iomem_region *region = iomem_regions;
120
Jeff Dike6d536e42007-10-16 01:26:47 -0700121 while (region != NULL) {
122 if ((phys >= region->phys) &&
123 (phys < region->phys + region->size)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 fd = region->fd;
125 *offset_out = phys - region->phys;
126 break;
127 }
128 region = region->next;
129 }
130 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700131 else if (phys < __pa(end_iomem) + highmem) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 fd = physmem_fd;
133 *offset_out = phys - iomem_size;
134 }
135
Jeff Dike60678bb2007-02-10 01:44:10 -0800136 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137}
138
139static 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
Jeff Dike94c282d2007-02-10 01:44:09 -0800155extern 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 */
Jeff Dike80e39312008-02-04 22:31:17 -0800167struct iomem_region *iomem_regions;
Jeff Dike94c282d2007-02-10 01:44:09 -0800168
Jeff Dike80e39312008-02-04 22:31:17 -0800169/* Initialized in parse_iomem and unchanged thereafter */
170int iomem_size;
Jeff Dike94c282d2007-02-10 01:44:09 -0800171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172unsigned long find_iomem(char *driver, unsigned long *len_out)
173{
174 struct iomem_region *region = iomem_regions;
175
Jeff Dike6d536e42007-10-16 01:26:47 -0700176 while (region != NULL) {
177 if (!strcmp(region->driver, driver)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 *len_out = region->size;
Jeff Dike60678bb2007-02-10 01:44:10 -0800179 return region->virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 }
Victor V. Vengerovc39e50b2006-05-01 12:15:53 -0700181
182 region = region->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 }
184
Jeff Dike60678bb2007-02-10 01:44:10 -0800185 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186}
187
WANG Cong99764fa2008-07-23 21:28:49 -0700188static int setup_iomem(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
190 struct iomem_region *region = iomem_regions;
191 unsigned long iomem_start = high_physmem + PAGE_SIZE;
192 int err;
193
Jeff Dike6d536e42007-10-16 01:26:47 -0700194 while (region != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 err = os_map_memory((void *) iomem_start, region->fd, 0,
196 region->size, 1, 1, 0);
Jeff Dike6d536e42007-10-16 01:26:47 -0700197 if (err)
Jeff Dikeba180fd2007-10-16 01:27:00 -0700198 printk(KERN_ERR "Mapping iomem region for driver '%s' "
199 "failed, errno = %d\n", region->driver, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 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
Jeff Dike60678bb2007-02-10 01:44:10 -0800209 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210}
211
212__initcall(setup_iomem);