ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/vm_alloc.cpp
Revision: 1.9
Committed: 2003-10-03T18:18:12Z (21 years, 1 month ago) by gbeauche
Branch: MAIN
Changes since 1.8: +22 -4 lines
Log Message:
Make sure a 32-bit B2/JIT works reasonnably well on AMD64 too. This implies
to force RAMBaseHost < 0x80000000. This is empirically determined to work on
Linux/x86 and Linux/amd64.

File Contents

# Content
1 /*
2 * vm_alloc.cpp - Wrapper to various virtual memory allocation schemes
3 * (supports mmap, vm_allocate or fallbacks to malloc)
4 *
5 * Basilisk II (C) 1997-2002 Christian Bauer
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 // TODO: Win32 VMs ?
27 #include <stdlib.h>
28 #include <string.h>
29 #include "vm_alloc.h"
30
31 #ifdef HAVE_MACH_VM
32 #ifndef HAVE_MACH_TASK_SELF
33 #ifdef HAVE_TASK_SELF
34 #define mach_task_self task_self
35 #else
36 #error "No task_self(), you lose."
37 #endif
38 #endif
39 #endif
40
41 /* We want MAP_32BIT, if available, for SheepShaver and BasiliskII
42 because the emulated target is 32-bit and this helps to allocate
43 memory so that branches could be resolved more easily (32-bit
44 displacement to code in .text), on AMD64 for example. */
45 #ifndef MAP_32BIT
46 #define MAP_32BIT 0
47 #endif
48
49 #define MAP_EXTRA_FLAGS (MAP_32BIT)
50
51 #ifdef HAVE_MMAP_VM
52 #if defined(__linux__) && defined(__i386__)
53 /* Force a reasonnable address below 0x80000000 on x86 so that we
54 don't get addresses above when the program is run on AMD64.
55 NOTE: this is empirically determined on Linux/x86. */
56 #define MAP_BASE 0x10000000
57 #else
58 #define MAP_BASE 0x00000000
59 #endif
60 static char * next_address = (char *)MAP_BASE;
61 #ifdef HAVE_MMAP_ANON
62 #define map_flags (MAP_PRIVATE | MAP_ANON | MAP_EXTRA_FLAGS)
63 #define zero_fd -1
64 #else
65 #ifdef HAVE_MMAP_ANONYMOUS
66 #define map_flags (MAP_PRIVATE | MAP_ANONYMOUS | MAP_EXTRA_FLAGS)
67 #define zero_fd -1
68 #else
69 #ifdef HAVE_FCNTL_H
70 #include <fcntl.h>
71 #endif
72 #define map_flags (MAP_PRIVATE | MAP_EXTRA_FLAGS)
73 static int zero_fd = -1;
74 #endif
75 #endif
76 #endif
77
78 /* Initialize the VM system. Returns 0 if successful, -1 for errors. */
79
80 int vm_init(void)
81 {
82 #ifdef HAVE_MMAP_VM
83 #ifndef zero_fd
84 zero_fd = open("/dev/zero", O_RDWR);
85 if (zero_fd < 0)
86 return -1;
87 #endif
88 #endif
89 return 0;
90 }
91
92 /* Deallocate all internal data used to wrap virtual memory allocators. */
93
94 void vm_exit(void)
95 {
96 #ifdef HAVE_MMAP_VM
97 #ifndef zero_fd
98 close(zero_fd);
99 #endif
100 #endif
101 }
102
103 /* Allocate zero-filled memory of SIZE bytes. The mapping is private
104 and default protection bits are read / write. The return value
105 is the actual mapping address chosen or VM_MAP_FAILED for errors. */
106
107 void * vm_acquire(size_t size)
108 {
109 void * addr;
110
111 #ifdef HAVE_MACH_VM
112 // vm_allocate() returns a zero-filled memory region
113 if (vm_allocate(mach_task_self(), (vm_address_t *)&addr, size, TRUE) != KERN_SUCCESS)
114 return VM_MAP_FAILED;
115 #else
116 #ifdef HAVE_MMAP_VM
117 if ((addr = mmap((caddr_t)next_address, size, VM_PAGE_DEFAULT, map_flags, zero_fd, 0)) == MAP_FAILED)
118 return VM_MAP_FAILED;
119
120 next_address = (char *)addr + size;
121
122 // Since I don't know the standard behavior of mmap(), zero-fill here
123 if (memset(addr, 0, size) != addr)
124 return VM_MAP_FAILED;
125 #else
126 if ((addr = calloc(size, 1)) == 0)
127 return VM_MAP_FAILED;
128
129 // Omit changes for protections because they are not supported in this mode
130 return addr;
131 #endif
132 #endif
133
134 // Explicitely protect the newly mapped region here because on some systems,
135 // say MacOS X, mmap() doesn't honour the requested protection flags.
136 if (vm_protect(addr, size, VM_PAGE_DEFAULT) != 0)
137 return VM_MAP_FAILED;
138
139 return addr;
140 }
141
142 /* Allocate zero-filled memory at exactly ADDR (which must be page-aligned).
143 Retuns 0 if successful, -1 on errors. */
144
145 int vm_acquire_fixed(void * addr, size_t size)
146 {
147 #ifdef HAVE_MACH_VM
148 // vm_allocate() returns a zero-filled memory region
149 if (vm_allocate(mach_task_self(), (vm_address_t *)&addr, size, 0) != KERN_SUCCESS)
150 return -1;
151 #else
152 #ifdef HAVE_MMAP_VM
153 if (mmap((caddr_t)addr, size, VM_PAGE_DEFAULT, map_flags | MAP_FIXED, zero_fd, 0) == MAP_FAILED)
154 return -1;
155
156 // Since I don't know the standard behavior of mmap(), zero-fill here
157 if (memset(addr, 0, size) != addr)
158 return -1;
159 #else
160 // Unsupported
161 return -1;
162 #endif
163 #endif
164
165 // Explicitely protect the newly mapped region here because on some systems,
166 // say MacOS X, mmap() doesn't honour the requested protection flags.
167 if (vm_protect(addr, size, VM_PAGE_DEFAULT) != 0)
168 return -1;
169
170 return 0;
171 }
172
173 /* Deallocate any mapping for the region starting at ADDR and extending
174 LEN bytes. Returns 0 if successful, -1 on errors. */
175
176 int vm_release(void * addr, size_t size)
177 {
178 // Safety check: don't try to release memory that was not allocated
179 if (addr == VM_MAP_FAILED)
180 return 0;
181
182 #ifdef HAVE_MACH_VM
183 if (vm_deallocate(mach_task_self(), (vm_address_t)addr, size) != KERN_SUCCESS)
184 return -1;
185 #else
186 #ifdef HAVE_MMAP_VM
187 if (munmap((caddr_t)addr, size) != 0)
188 return -1;
189 #else
190 free(addr);
191 #endif
192 #endif
193
194 return 0;
195 }
196
197 /* Change the memory protection of the region starting at ADDR and
198 extending LEN bytes to PROT. Returns 0 if successful, -1 for errors. */
199
200 int vm_protect(void * addr, size_t size, int prot)
201 {
202 #ifdef HAVE_MACH_VM
203 int ret_code = vm_protect(mach_task_self(), (vm_address_t)addr, size, 0, prot);
204 return ret_code == KERN_SUCCESS ? 0 : -1;
205 #else
206 #ifdef HAVE_MMAP_VM
207 int ret_code = mprotect((caddr_t)addr, size, prot);
208 return ret_code == 0 ? 0 : -1;
209 #else
210 // Unsupported
211 return -1;
212 #endif
213 #endif
214 }
215
216 #ifdef CONFIGURE_TEST_VM_MAP
217 /* Tests covered here:
218 - TEST_VM_PROT_* program slices actually succeeds when a crash occurs
219 - TEST_VM_MAP_ANON* program slices succeeds when it could be compiled
220 */
221 int main(void)
222 {
223 vm_init();
224
225 #define page_align(address) ((char *)((unsigned long)(address) & -page_size))
226 unsigned long page_size = getpagesize();
227
228 const int area_size = 6 * page_size;
229 volatile char * area = (volatile char *) vm_acquire(area_size);
230 volatile char * fault_address = area + (page_size * 7) / 2;
231
232 #if defined(TEST_VM_MMAP_ANON) || defined(TEST_VM_MMAP_ANONYMOUS)
233 if (area == VM_MAP_FAILED)
234 return 1;
235
236 if (vm_release((char *)area, area_size) < 0)
237 return 1;
238
239 return 0;
240 #endif
241
242 #if defined(TEST_VM_PROT_NONE_READ) || defined(TEST_VM_PROT_NONE_WRITE)
243 if (area == VM_MAP_FAILED)
244 return 0;
245
246 if (vm_protect(page_align(fault_address), page_size, VM_PAGE_NOACCESS) < 0)
247 return 0;
248 #endif
249
250 #if defined(TEST_VM_PROT_RDWR_WRITE)
251 if (area == VM_MAP_FAILED)
252 return 1;
253
254 if (vm_protect(page_align(fault_address), page_size, VM_PAGE_READ) < 0)
255 return 1;
256
257 if (vm_protect(page_align(fault_address), page_size, VM_PAGE_READ | VM_PAGE_WRITE) < 0)
258 return 1;
259 #endif
260
261 #if defined(TEST_VM_PROT_READ_WRITE)
262 if (vm_protect(page_align(fault_address), page_size, VM_PAGE_READ) < 0)
263 return 0;
264 #endif
265
266 #if defined(TEST_VM_PROT_NONE_READ)
267 // this should cause a core dump
268 char foo = *fault_address;
269 return 0;
270 #endif
271
272 #if defined(TEST_VM_PROT_NONE_WRITE) || defined(TEST_VM_PROT_READ_WRITE)
273 // this should cause a core dump
274 *fault_address = 'z';
275 return 0;
276 #endif
277
278 #if defined(TEST_VM_PROT_RDWR_WRITE)
279 // this should not cause a core dump
280 *fault_address = 'z';
281 return 0;
282 #endif
283 }
284 #endif