blob: 30fdd5d0067b26c91fb8c831da5a1d4008c79e22 [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
Al Viro73395a02011-08-18 20:14:10 +01006#include <linux/module.h>
7#include <linux/bootmem.h>
8#include <linux/mm.h>
9#include <linux/pfn.h>
10#include <asm/page.h>
11#include <as-layout.h>
12#include <init.h>
13#include <kern.h>
14#include <mem_user.h>
15#include <os.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
Linus Torvalds1da177e2005-04-16 15:20:36 -070017static int physmem_fd = -1;
18
Linus Torvalds1da177e2005-04-16 15:20:36 -070019/* Changed during early boot */
20unsigned long high_physmem;
Al Viro73395a02011-08-18 20:14:10 +010021EXPORT_SYMBOL(high_physmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Jeff Dikeae173812005-11-07 00:58:57 -080023extern unsigned long long physmem_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Jeff Dike97a1fcb2007-07-23 18:43:48 -070025int __init init_maps(unsigned long physmem, unsigned long iomem,
26 unsigned long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -070027{
28 struct page *p, *map;
29 unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
30 unsigned long iomem_len, iomem_pages, total_len, total_pages;
31 int i;
32
33 phys_pages = physmem >> PAGE_SHIFT;
34 phys_len = phys_pages * sizeof(struct page);
35
36 iomem_pages = iomem >> PAGE_SHIFT;
37 iomem_len = iomem_pages * sizeof(struct page);
38
39 highmem_pages = highmem >> PAGE_SHIFT;
40 highmem_len = highmem_pages * sizeof(struct page);
41
42 total_pages = phys_pages + iomem_pages + highmem_pages;
Paolo 'Blaisorblade' Giarrusso3dfd95b2006-02-01 03:06:26 -080043 total_len = phys_len + iomem_len + highmem_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Jeff Dike97a1fcb2007-07-23 18:43:48 -070045 map = alloc_bootmem_low_pages(total_len);
Jeff Dike6d536e42007-10-16 01:26:47 -070046 if (map == NULL)
Jeff Dike60678bb2007-02-10 01:44:10 -080047 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Jeff Dike6d536e42007-10-16 01:26:47 -070049 for (i = 0; i < total_pages; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 p = &map[i];
Nick Piggin70dc9912006-03-22 00:08:35 -080051 memset(p, 0, sizeof(struct page));
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 SetPageReserved(p);
53 INIT_LIST_HEAD(&p->lru);
54 }
55
56 max_mapnr = total_pages;
Jeff Dike60678bb2007-02-10 01:44:10 -080057 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058}
59
Linus Torvalds1da177e2005-04-16 15:20:36 -070060void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
61 int r, int w, int x)
62{
63 __u64 offset;
64 int fd, err;
65
66 fd = phys_mapping(phys, &offset);
67 err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
Jeff Dike6d536e42007-10-16 01:26:47 -070068 if (err) {
69 if (err == -ENOMEM)
Jeff Dikeba180fd2007-10-16 01:27:00 -070070 printk(KERN_ERR "try increasing the host's "
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 "/proc/sys/vm/max_map_count to <physical "
72 "memory size>/4096\n");
73 panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
74 "err = %d\n", virt, fd, offset, len, r, w, x, err);
75 }
76}
77
Jeff Dike23bbd5862006-07-10 04:45:06 -070078extern int __syscall_stub_start;
Jeff Diked67b5692005-07-07 17:56:49 -070079
Jeff Dike97a1fcb2007-07-23 18:43:48 -070080void __init setup_physmem(unsigned long start, unsigned long reserve_end,
81 unsigned long len, unsigned long long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
83 unsigned long reserve = reserve_end - start;
84 int pfn = PFN_UP(__pa(reserve_end));
85 int delta = (len - reserve) >> PAGE_SHIFT;
86 int err, offset, bootmap_size;
87
88 physmem_fd = create_mem_file(len + highmem);
89
90 offset = uml_reserved - uml_physmem;
91 err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
Jeff Dike5c8aace2007-10-16 01:26:46 -070092 len - offset, 1, 1, 1);
Jeff Dike6d536e42007-10-16 01:26:47 -070093 if (err < 0) {
Jeff Dike512b6fb2007-10-16 01:27:11 -070094 printf("setup_physmem - mapping %ld bytes of memory at 0x%p "
95 "failed - errno = %d\n", len - offset,
96 (void *) uml_reserved, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 exit(1);
98 }
99
Jeff Dikeba180fd2007-10-16 01:27:00 -0700100 /*
101 * Special kludge - This page will be mapped in to userspace processes
Jeff Diked67b5692005-07-07 17:56:49 -0700102 * from physmem_fd, so it needs to be written out there.
103 */
104 os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700105 os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
Anton Ivanov05651032014-03-07 18:37:47 +0000106 os_fsync_file(physmem_fd);
Jeff Diked67b5692005-07-07 17:56:49 -0700107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 bootmap_size = init_bootmem(pfn, pfn + delta);
109 free_bootmem(__pa(reserve_end) + bootmap_size,
110 len - bootmap_size - reserve);
111}
112
Jeff Dike0a7675a2007-10-16 01:27:05 -0700113int phys_mapping(unsigned long phys, unsigned long long *offset_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 int fd = -1;
116
Jeff Dike6d536e42007-10-16 01:26:47 -0700117 if (phys < physmem_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 fd = physmem_fd;
119 *offset_out = phys;
120 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700121 else if (phys < __pa(end_iomem)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 struct iomem_region *region = iomem_regions;
123
Jeff Dike6d536e42007-10-16 01:26:47 -0700124 while (region != NULL) {
125 if ((phys >= region->phys) &&
126 (phys < region->phys + region->size)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 fd = region->fd;
128 *offset_out = phys - region->phys;
129 break;
130 }
131 region = region->next;
132 }
133 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700134 else if (phys < __pa(end_iomem) + highmem) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 fd = physmem_fd;
136 *offset_out = phys - iomem_size;
137 }
138
Jeff Dike60678bb2007-02-10 01:44:10 -0800139 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}
141
142static int __init uml_mem_setup(char *line, int *add)
143{
144 char *retptr;
145 physmem_size = memparse(line,&retptr);
146 return 0;
147}
148__uml_setup("mem=", uml_mem_setup,
149"mem=<Amount of desired ram>\n"
150" This controls how much \"physical\" memory the kernel allocates\n"
151" for the system. The size is specified as a number followed by\n"
152" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
153" This is not related to the amount of memory in the host. It can\n"
154" be more, and the excess, if it's ever used, will just be swapped out.\n"
155" Example: mem=64M\n\n"
156);
157
Jeff Dike94c282d2007-02-10 01:44:09 -0800158extern int __init parse_iomem(char *str, int *add);
159
160__uml_setup("iomem=", parse_iomem,
161"iomem=<name>,<file>\n"
162" Configure <file> as an IO memory region named <name>.\n\n"
163);
164
165/*
166 * This list is constructed in parse_iomem and addresses filled in in
167 * setup_iomem, both of which run during early boot. Afterwards, it's
168 * unchanged.
169 */
Jeff Dike80e39312008-02-04 22:31:17 -0800170struct iomem_region *iomem_regions;
Jeff Dike94c282d2007-02-10 01:44:09 -0800171
Jeff Dike80e39312008-02-04 22:31:17 -0800172/* Initialized in parse_iomem and unchanged thereafter */
173int iomem_size;
Jeff Dike94c282d2007-02-10 01:44:09 -0800174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175unsigned long find_iomem(char *driver, unsigned long *len_out)
176{
177 struct iomem_region *region = iomem_regions;
178
Jeff Dike6d536e42007-10-16 01:26:47 -0700179 while (region != NULL) {
180 if (!strcmp(region->driver, driver)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 *len_out = region->size;
Jeff Dike60678bb2007-02-10 01:44:10 -0800182 return region->virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 }
Victor V. Vengerovc39e50b2006-05-01 12:15:53 -0700184
185 region = region->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 }
187
Jeff Dike60678bb2007-02-10 01:44:10 -0800188 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189}
Al Viro73395a02011-08-18 20:14:10 +0100190EXPORT_SYMBOL(find_iomem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
WANG Cong99764fa2008-07-23 21:28:49 -0700192static int setup_iomem(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193{
194 struct iomem_region *region = iomem_regions;
195 unsigned long iomem_start = high_physmem + PAGE_SIZE;
196 int err;
197
Jeff Dike6d536e42007-10-16 01:26:47 -0700198 while (region != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 err = os_map_memory((void *) iomem_start, region->fd, 0,
200 region->size, 1, 1, 0);
Jeff Dike6d536e42007-10-16 01:26:47 -0700201 if (err)
Jeff Dikeba180fd2007-10-16 01:27:00 -0700202 printk(KERN_ERR "Mapping iomem region for driver '%s' "
203 "failed, errno = %d\n", region->driver, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 else {
205 region->virt = iomem_start;
206 region->phys = __pa(region->virt);
207 }
208
209 iomem_start += region->size + PAGE_SIZE;
210 region = region->next;
211 }
212
Jeff Dike60678bb2007-02-10 01:44:10 -0800213 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214}
215
216__initcall(setup_iomem);