blob: 549ecf3f5857441c95e4344b6aabc99ce4723f7f [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
Honggang Li9e6a57d2014-06-03 13:30:45 +080025void __init mem_total_pages(unsigned long physmem, unsigned long iomem,
Jeff Dike97a1fcb2007-07-23 18:43:48 -070026 unsigned long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -070027{
Honggang Li9e6a57d2014-06-03 13:30:45 +080028 unsigned long phys_pages, highmem_pages;
29 unsigned long iomem_pages, total_pages;
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Honggang Li9e6a57d2014-06-03 13:30:45 +080031 phys_pages = physmem >> PAGE_SHIFT;
32 iomem_pages = iomem >> PAGE_SHIFT;
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 highmem_pages = highmem >> PAGE_SHIFT;
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Honggang Li9e6a57d2014-06-03 13:30:45 +080035 total_pages = phys_pages + iomem_pages + highmem_pages;
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37 max_mapnr = total_pages;
Linus Torvalds1da177e2005-04-16 15:20:36 -070038}
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
41 int r, int w, int x)
42{
43 __u64 offset;
44 int fd, err;
45
46 fd = phys_mapping(phys, &offset);
47 err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
Jeff Dike6d536e42007-10-16 01:26:47 -070048 if (err) {
49 if (err == -ENOMEM)
Jeff Dikeba180fd2007-10-16 01:27:00 -070050 printk(KERN_ERR "try increasing the host's "
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 "/proc/sys/vm/max_map_count to <physical "
52 "memory size>/4096\n");
53 panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
54 "err = %d\n", virt, fd, offset, len, r, w, x, err);
55 }
56}
57
Jeff Dike23bbd5862006-07-10 04:45:06 -070058extern int __syscall_stub_start;
Jeff Diked67b5692005-07-07 17:56:49 -070059
Jeff Dike97a1fcb2007-07-23 18:43:48 -070060void __init setup_physmem(unsigned long start, unsigned long reserve_end,
61 unsigned long len, unsigned long long highmem)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062{
63 unsigned long reserve = reserve_end - start;
64 int pfn = PFN_UP(__pa(reserve_end));
65 int delta = (len - reserve) >> PAGE_SHIFT;
66 int err, offset, bootmap_size;
67
68 physmem_fd = create_mem_file(len + highmem);
69
70 offset = uml_reserved - uml_physmem;
71 err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
Jeff Dike5c8aace2007-10-16 01:26:46 -070072 len - offset, 1, 1, 1);
Jeff Dike6d536e42007-10-16 01:26:47 -070073 if (err < 0) {
Jeff Dike512b6fb2007-10-16 01:27:11 -070074 printf("setup_physmem - mapping %ld bytes of memory at 0x%p "
75 "failed - errno = %d\n", len - offset,
76 (void *) uml_reserved, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 exit(1);
78 }
79
Jeff Dikeba180fd2007-10-16 01:27:00 -070080 /*
81 * Special kludge - This page will be mapped in to userspace processes
Jeff Diked67b5692005-07-07 17:56:49 -070082 * from physmem_fd, so it needs to be written out there.
83 */
84 os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
Jeff Dikea6ea4cc2007-05-06 14:51:43 -070085 os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
Anton Ivanov05651032014-03-07 18:37:47 +000086 os_fsync_file(physmem_fd);
Jeff Diked67b5692005-07-07 17:56:49 -070087
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 bootmap_size = init_bootmem(pfn, pfn + delta);
89 free_bootmem(__pa(reserve_end) + bootmap_size,
90 len - bootmap_size - reserve);
91}
92
Jeff Dike0a7675a2007-10-16 01:27:05 -070093int phys_mapping(unsigned long phys, unsigned long long *offset_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 int fd = -1;
96
Jeff Dike6d536e42007-10-16 01:26:47 -070097 if (phys < physmem_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 fd = physmem_fd;
99 *offset_out = phys;
100 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700101 else if (phys < __pa(end_iomem)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 struct iomem_region *region = iomem_regions;
103
Jeff Dike6d536e42007-10-16 01:26:47 -0700104 while (region != NULL) {
105 if ((phys >= region->phys) &&
106 (phys < region->phys + region->size)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 fd = region->fd;
108 *offset_out = phys - region->phys;
109 break;
110 }
111 region = region->next;
112 }
113 }
Jeff Dike6d536e42007-10-16 01:26:47 -0700114 else if (phys < __pa(end_iomem) + highmem) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 fd = physmem_fd;
116 *offset_out = phys - iomem_size;
117 }
118
Jeff Dike60678bb2007-02-10 01:44:10 -0800119 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120}
121
122static int __init uml_mem_setup(char *line, int *add)
123{
124 char *retptr;
125 physmem_size = memparse(line,&retptr);
126 return 0;
127}
128__uml_setup("mem=", uml_mem_setup,
129"mem=<Amount of desired ram>\n"
130" This controls how much \"physical\" memory the kernel allocates\n"
131" for the system. The size is specified as a number followed by\n"
132" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
133" This is not related to the amount of memory in the host. It can\n"
134" be more, and the excess, if it's ever used, will just be swapped out.\n"
135" Example: mem=64M\n\n"
136);
137
Jeff Dike94c282d2007-02-10 01:44:09 -0800138extern int __init parse_iomem(char *str, int *add);
139
140__uml_setup("iomem=", parse_iomem,
141"iomem=<name>,<file>\n"
142" Configure <file> as an IO memory region named <name>.\n\n"
143);
144
145/*
146 * This list is constructed in parse_iomem and addresses filled in in
147 * setup_iomem, both of which run during early boot. Afterwards, it's
148 * unchanged.
149 */
Jeff Dike80e39312008-02-04 22:31:17 -0800150struct iomem_region *iomem_regions;
Jeff Dike94c282d2007-02-10 01:44:09 -0800151
Jeff Dike80e39312008-02-04 22:31:17 -0800152/* Initialized in parse_iomem and unchanged thereafter */
153int iomem_size;
Jeff Dike94c282d2007-02-10 01:44:09 -0800154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155unsigned long find_iomem(char *driver, unsigned long *len_out)
156{
157 struct iomem_region *region = iomem_regions;
158
Jeff Dike6d536e42007-10-16 01:26:47 -0700159 while (region != NULL) {
160 if (!strcmp(region->driver, driver)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 *len_out = region->size;
Jeff Dike60678bb2007-02-10 01:44:10 -0800162 return region->virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 }
Victor V. Vengerovc39e50b2006-05-01 12:15:53 -0700164
165 region = region->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 }
167
Jeff Dike60678bb2007-02-10 01:44:10 -0800168 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169}
Al Viro73395a02011-08-18 20:14:10 +0100170EXPORT_SYMBOL(find_iomem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
WANG Cong99764fa2008-07-23 21:28:49 -0700172static int setup_iomem(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173{
174 struct iomem_region *region = iomem_regions;
175 unsigned long iomem_start = high_physmem + PAGE_SIZE;
176 int err;
177
Jeff Dike6d536e42007-10-16 01:26:47 -0700178 while (region != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 err = os_map_memory((void *) iomem_start, region->fd, 0,
180 region->size, 1, 1, 0);
Jeff Dike6d536e42007-10-16 01:26:47 -0700181 if (err)
Jeff Dikeba180fd2007-10-16 01:27:00 -0700182 printk(KERN_ERR "Mapping iomem region for driver '%s' "
183 "failed, errno = %d\n", region->driver, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 else {
185 region->virt = iomem_start;
186 region->phys = __pa(region->virt);
187 }
188
189 iomem_start += region->size + PAGE_SIZE;
190 region = region->next;
191 }
192
Jeff Dike60678bb2007-02-10 01:44:10 -0800193 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194}
195
196__initcall(setup_iomem);