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