xref: /linux/arch/um/kernel/physmem.c (revision cbb8e65e234e0139c0c516bb6b9110d210eecd3f)
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 
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 
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  */
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 	os_fsync_file(physmem_fd);
105 
106 	memblock_add(__pa(start), len);
107 	memblock_reserve(__pa(start), reserve);
108 
109 	min_low_pfn = PFN_UP(__pa(reserve_end));
110 	max_low_pfn = min_low_pfn + (map_size >> PAGE_SHIFT);
111 }
112 
113 int phys_mapping(unsigned long phys, unsigned long long *offset_out)
114 {
115 	int fd = -1;
116 
117 	if (phys < physmem_size) {
118 		fd = physmem_fd;
119 		*offset_out = phys;
120 	}
121 	else if (phys < __pa(end_iomem)) {
122 		struct iomem_region *region = iomem_regions;
123 
124 		while (region != NULL) {
125 			if ((phys >= region->phys) &&
126 			    (phys < region->phys + region->size)) {
127 				fd = region->fd;
128 				*offset_out = phys - region->phys;
129 				break;
130 			}
131 			region = region->next;
132 		}
133 	}
134 
135 	return fd;
136 }
137 EXPORT_SYMBOL(phys_mapping);
138 
139 static int __init uml_mem_setup(char *line, int *add)
140 {
141 	char *retptr;
142 	physmem_size = memparse(line,&retptr);
143 	return 0;
144 }
145 __uml_setup("mem=", uml_mem_setup,
146 "mem=<Amount of desired ram>\n"
147 "    This controls how much \"physical\" memory the kernel allocates\n"
148 "    for the system. The size is specified as a number followed by\n"
149 "    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
150 "    This is not related to the amount of memory in the host.  It can\n"
151 "    be more, and the excess, if it's ever used, will just be swapped out.\n"
152 "	Example: mem=64M\n\n"
153 );
154 
155 __uml_setup("iomem=", parse_iomem,
156 "iomem=<name>,<file>\n"
157 "    Configure <file> as an IO memory region named <name>.\n\n"
158 );
159 
160 /*
161  * This list is constructed in parse_iomem and addresses filled in
162  * setup_iomem, both of which run during early boot.  Afterwards, it's
163  * unchanged.
164  */
165 struct iomem_region *iomem_regions;
166 
167 /* Initialized in parse_iomem and unchanged thereafter */
168 int iomem_size;
169 
170 unsigned long find_iomem(char *driver, unsigned long *len_out)
171 {
172 	struct iomem_region *region = iomem_regions;
173 
174 	while (region != NULL) {
175 		if (!strcmp(region->driver, driver)) {
176 			*len_out = region->size;
177 			return region->virt;
178 		}
179 
180 		region = region->next;
181 	}
182 
183 	return 0;
184 }
185 EXPORT_SYMBOL(find_iomem);
186 
187 static int setup_iomem(void)
188 {
189 	struct iomem_region *region = iomem_regions;
190 	unsigned long iomem_start = high_physmem + PAGE_SIZE;
191 	int err;
192 
193 	while (region != NULL) {
194 		err = os_map_memory((void *) iomem_start, region->fd, 0,
195 				    region->size, 1, 1, 0);
196 		if (err)
197 			printk(KERN_ERR "Mapping iomem region for driver '%s' "
198 			       "failed, errno = %d\n", region->driver, -err);
199 		else {
200 			region->virt = iomem_start;
201 			region->phys = __pa(region->virt);
202 		}
203 
204 		iomem_start += region->size + PAGE_SIZE;
205 		region = region->next;
206 	}
207 
208 	return 0;
209 }
210 
211 __initcall(setup_iomem);
212