1/*
2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3 * Licensed under the GPL
4 */
5
6#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>
16
17static int physmem_fd = -1;
18
19/* Changed during early boot */
20unsigned long high_physmem;
21EXPORT_SYMBOL(high_physmem);
22
23extern unsigned long long physmem_size;
24
25void __init mem_total_pages(unsigned long physmem, unsigned long iomem,
26		     unsigned long highmem)
27{
28	unsigned long phys_pages, highmem_pages;
29	unsigned long iomem_pages, total_pages;
30
31	phys_pages    = physmem >> PAGE_SHIFT;
32	iomem_pages   = iomem   >> PAGE_SHIFT;
33	highmem_pages = highmem >> PAGE_SHIFT;
34
35	total_pages   = phys_pages + iomem_pages + highmem_pages;
36
37	max_mapnr = total_pages;
38}
39
40void 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);
48	if (err) {
49		if (err == -ENOMEM)
50			printk(KERN_ERR "try increasing the host's "
51			       "/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
58extern int __syscall_stub_start;
59
60/**
61 * setup_physmem() - Setup physical memory for UML
62 * @start:	Start address of the physical kernel memory,
63 *		i.e start address of the executable image.
64 * @reserve_end:	end address of the physical kernel memory.
65 * @len:	Length of total physical memory that should be mapped/made
66 *		available, in bytes.
67 * @highmem:	Number of highmem bytes that should be mapped/made available.
68 *
69 * Creates an unlinked temporary file of size (len + highmem) and memory maps
70 * it on the last executable image address (uml_reserved).
71 *
72 * The offset is needed as the length of the total physical memory
73 * (len + highmem) includes the size of the memory used be the executable image,
74 * but the mapped-to address is the last address of the executable image
75 * (uml_reserved == end address of executable image).
76 *
77 * The memory mapped memory of the temporary file is used as backing memory
78 * of all user space processes/kernel tasks.
79 */
80void __init setup_physmem(unsigned long start, unsigned long reserve_end,
81			  unsigned long len, unsigned long long highmem)
82{
83	unsigned long reserve = reserve_end - start;
84	unsigned long pfn = PFN_UP(__pa(reserve_end));
85	unsigned long delta = (len - reserve) >> PAGE_SHIFT;
86	unsigned long offset, bootmap_size;
87	long map_size;
88	int err;
89
90	offset = uml_reserved - uml_physmem;
91	map_size = len - offset;
92	if(map_size <= 0) {
93		printf("Too few physical memory! Needed=%d, given=%d\n",
94		       offset, len);
95		exit(1);
96	}
97
98	physmem_fd = create_mem_file(len + highmem);
99
100	err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
101			    map_size, 1, 1, 1);
102	if (err < 0) {
103		printf("setup_physmem - mapping %ld bytes of memory at 0x%p "
104		       "failed - errno = %d\n", map_size,
105		       (void *) uml_reserved, err);
106		exit(1);
107	}
108
109	/*
110	 * Special kludge - This page will be mapped in to userspace processes
111	 * from physmem_fd, so it needs to be written out there.
112	 */
113	os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
114	os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
115	os_fsync_file(physmem_fd);
116
117	bootmap_size = init_bootmem(pfn, pfn + delta);
118	free_bootmem(__pa(reserve_end) + bootmap_size,
119		     len - bootmap_size - reserve);
120}
121
122int phys_mapping(unsigned long phys, unsigned long long *offset_out)
123{
124	int fd = -1;
125
126	if (phys < physmem_size) {
127		fd = physmem_fd;
128		*offset_out = phys;
129	}
130	else if (phys < __pa(end_iomem)) {
131		struct iomem_region *region = iomem_regions;
132
133		while (region != NULL) {
134			if ((phys >= region->phys) &&
135			    (phys < region->phys + region->size)) {
136				fd = region->fd;
137				*offset_out = phys - region->phys;
138				break;
139			}
140			region = region->next;
141		}
142	}
143	else if (phys < __pa(end_iomem) + highmem) {
144		fd = physmem_fd;
145		*offset_out = phys - iomem_size;
146	}
147
148	return fd;
149}
150
151static int __init uml_mem_setup(char *line, int *add)
152{
153	char *retptr;
154	physmem_size = memparse(line,&retptr);
155	return 0;
156}
157__uml_setup("mem=", uml_mem_setup,
158"mem=<Amount of desired ram>\n"
159"    This controls how much \"physical\" memory the kernel allocates\n"
160"    for the system. The size is specified as a number followed by\n"
161"    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
162"    This is not related to the amount of memory in the host.  It can\n"
163"    be more, and the excess, if it's ever used, will just be swapped out.\n"
164"	Example: mem=64M\n\n"
165);
166
167extern int __init parse_iomem(char *str, int *add);
168
169__uml_setup("iomem=", parse_iomem,
170"iomem=<name>,<file>\n"
171"    Configure <file> as an IO memory region named <name>.\n\n"
172);
173
174/*
175 * This list is constructed in parse_iomem and addresses filled in in
176 * setup_iomem, both of which run during early boot.  Afterwards, it's
177 * unchanged.
178 */
179struct iomem_region *iomem_regions;
180
181/* Initialized in parse_iomem and unchanged thereafter */
182int iomem_size;
183
184unsigned long find_iomem(char *driver, unsigned long *len_out)
185{
186	struct iomem_region *region = iomem_regions;
187
188	while (region != NULL) {
189		if (!strcmp(region->driver, driver)) {
190			*len_out = region->size;
191			return region->virt;
192		}
193
194		region = region->next;
195	}
196
197	return 0;
198}
199EXPORT_SYMBOL(find_iomem);
200
201static int setup_iomem(void)
202{
203	struct iomem_region *region = iomem_regions;
204	unsigned long iomem_start = high_physmem + PAGE_SIZE;
205	int err;
206
207	while (region != NULL) {
208		err = os_map_memory((void *) iomem_start, region->fd, 0,
209				    region->size, 1, 1, 0);
210		if (err)
211			printk(KERN_ERR "Mapping iomem region for driver '%s' "
212			       "failed, errno = %d\n", region->driver, -err);
213		else {
214			region->virt = iomem_start;
215			region->phys = __pa(region->virt);
216		}
217
218		iomem_start += region->size + PAGE_SIZE;
219		region = region->next;
220	}
221
222	return 0;
223}
224
225__initcall(setup_iomem);
226