1/*
2 * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
3 * Licensed under the GPL
4 */
5
6#include "linux/mm.h"
7#include "linux/rbtree.h"
8#include "linux/slab.h"
9#include "linux/vmalloc.h"
10#include "linux/bootmem.h"
11#include "linux/module.h"
12#include "linux/pfn.h"
13#include "asm/types.h"
14#include "asm/pgtable.h"
15#include "kern_util.h"
16#include "as-layout.h"
17#include "mode_kern.h"
18#include "mem.h"
19#include "mem_user.h"
20#include "os.h"
21#include "kern.h"
22#include "init.h"
23
24static int physmem_fd = -1;
25
26/* Changed during early boot */
27unsigned long high_physmem;
28
29extern unsigned long long physmem_size;
30
31int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
32{
33	struct page *p, *map;
34	unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
35	unsigned long iomem_len, iomem_pages, total_len, total_pages;
36	int i;
37
38	phys_pages = physmem >> PAGE_SHIFT;
39	phys_len = phys_pages * sizeof(struct page);
40
41	iomem_pages = iomem >> PAGE_SHIFT;
42	iomem_len = iomem_pages * sizeof(struct page);
43
44	highmem_pages = highmem >> PAGE_SHIFT;
45	highmem_len = highmem_pages * sizeof(struct page);
46
47	total_pages = phys_pages + iomem_pages + highmem_pages;
48	total_len = phys_len + iomem_len + highmem_len;
49
50	if(kmalloc_ok){
51		map = kmalloc(total_len, GFP_KERNEL);
52		if(map == NULL)
53			map = vmalloc(total_len);
54	}
55	else map = alloc_bootmem_low_pages(total_len);
56
57	if(map == NULL)
58		return -ENOMEM;
59
60	for(i = 0; i < total_pages; i++){
61		p = &map[i];
62		memset(p, 0, sizeof(struct page));
63		SetPageReserved(p);
64		INIT_LIST_HEAD(&p->lru);
65	}
66
67	max_mapnr = total_pages;
68	return 0;
69}
70
71/* Changed during early boot */
72static unsigned long kmem_top = 0;
73
74unsigned long get_kmem_end(void)
75{
76	if(kmem_top == 0)
77		kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
78	return kmem_top;
79}
80
81void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
82		int r, int w, int x)
83{
84	__u64 offset;
85	int fd, err;
86
87	fd = phys_mapping(phys, &offset);
88	err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
89	if(err) {
90		if(err == -ENOMEM)
91			printk("try increasing the host's "
92			       "/proc/sys/vm/max_map_count to <physical "
93			       "memory size>/4096\n");
94		panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
95		      "err = %d\n", virt, fd, offset, len, r, w, x, err);
96	}
97}
98
99extern int __syscall_stub_start;
100
101void setup_physmem(unsigned long start, unsigned long reserve_end,
102		   unsigned long len, unsigned long long highmem)
103{
104	unsigned long reserve = reserve_end - start;
105	int pfn = PFN_UP(__pa(reserve_end));
106	int delta = (len - reserve) >> PAGE_SHIFT;
107	int err, offset, bootmap_size;
108
109	physmem_fd = create_mem_file(len + highmem);
110
111	offset = uml_reserved - uml_physmem;
112	err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
113			    len - offset, 1, 1, 0);
114	if(err < 0){
115		os_print_error(err, "Mapping memory");
116		exit(1);
117	}
118
119	/* Special kludge - This page will be mapped in to userspace processes
120	 * from physmem_fd, so it needs to be written out there.
121	 */
122	os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
123	os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
124
125	bootmap_size = init_bootmem(pfn, pfn + delta);
126	free_bootmem(__pa(reserve_end) + bootmap_size,
127		     len - bootmap_size - reserve);
128}
129
130int phys_mapping(unsigned long phys, __u64 *offset_out)
131{
132	int fd = -1;
133
134	if(phys < physmem_size){
135		fd = physmem_fd;
136		*offset_out = phys;
137	}
138	else if(phys < __pa(end_iomem)){
139		struct iomem_region *region = iomem_regions;
140
141		while(region != NULL){
142			if((phys >= region->phys) &&
143			   (phys < region->phys + region->size)){
144				fd = region->fd;
145				*offset_out = phys - region->phys;
146				break;
147			}
148			region = region->next;
149		}
150	}
151	else if(phys < __pa(end_iomem) + highmem){
152		fd = physmem_fd;
153		*offset_out = phys - iomem_size;
154	}
155
156	return fd;
157}
158
159static int __init uml_mem_setup(char *line, int *add)
160{
161	char *retptr;
162	physmem_size = memparse(line,&retptr);
163	return 0;
164}
165__uml_setup("mem=", uml_mem_setup,
166"mem=<Amount of desired ram>\n"
167"    This controls how much \"physical\" memory the kernel allocates\n"
168"    for the system. The size is specified as a number followed by\n"
169"    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
170"    This is not related to the amount of memory in the host.  It can\n"
171"    be more, and the excess, if it's ever used, will just be swapped out.\n"
172"	Example: mem=64M\n\n"
173);
174
175extern int __init parse_iomem(char *str, int *add);
176
177__uml_setup("iomem=", parse_iomem,
178"iomem=<name>,<file>\n"
179"    Configure <file> as an IO memory region named <name>.\n\n"
180);
181
182/*
183 * This list is constructed in parse_iomem and addresses filled in in
184 * setup_iomem, both of which run during early boot.  Afterwards, it's
185 * unchanged.
186 */
187struct iomem_region *iomem_regions = NULL;
188
189/* Initialized in parse_iomem */
190int iomem_size = 0;
191
192unsigned long find_iomem(char *driver, unsigned long *len_out)
193{
194	struct iomem_region *region = iomem_regions;
195
196	while(region != NULL){
197		if(!strcmp(region->driver, driver)){
198			*len_out = region->size;
199			return region->virt;
200		}
201
202		region = region->next;
203	}
204
205	return 0;
206}
207
208int setup_iomem(void)
209{
210	struct iomem_region *region = iomem_regions;
211	unsigned long iomem_start = high_physmem + PAGE_SIZE;
212	int err;
213
214	while(region != NULL){
215		err = os_map_memory((void *) iomem_start, region->fd, 0,
216				    region->size, 1, 1, 0);
217		if(err)
218			printk("Mapping iomem region for driver '%s' failed, "
219			       "errno = %d\n", region->driver, -err);
220		else {
221			region->virt = iomem_start;
222			region->phys = __pa(region->virt);
223		}
224
225		iomem_start += region->size + PAGE_SIZE;
226		region = region->next;
227	}
228
229	return 0;
230}
231
232__initcall(setup_iomem);
233