1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <stdlib.h>
21: #include <stdio.h>
22: #include <stdarg.h>
23: #include <string.h>
24: #include <unistd.h>
25: #include <errno.h>
26: #include <sys/mman.h>
27:
28: #include "qemu.h"
29:
30:
31:
32:
33: int target_mprotect(abi_ulong start, abi_ulong len, int prot)
34: {
35: abi_ulong end, host_start, host_end, addr;
36: int prot1, ret;
37:
38: #ifdef DEBUG_MMAP
39: printf("mprotect: start=0x" TARGET_FMT_lx
40: "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
41: prot & PROT_READ ? 'r' : '-',
42: prot & PROT_WRITE ? 'w' : '-',
43: prot & PROT_EXEC ? 'x' : '-');
44: #endif
45:
46: if ((start & ~TARGET_PAGE_MASK) != 0)
47: return -EINVAL;
48: len = TARGET_PAGE_ALIGN(len);
49: end = start + len;
50: if (end < start)
51: return -EINVAL;
52: if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
53: return -EINVAL;
54: if (len == 0)
55: return 0;
56:
57: host_start = start & qemu_host_page_mask;
58: host_end = HOST_PAGE_ALIGN(end);
59: if (start > host_start) {
60:
61: prot1 = prot;
62: for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
63: prot1 |= page_get_flags(addr);
64: }
65: if (host_end == host_start + qemu_host_page_size) {
66: for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
67: prot1 |= page_get_flags(addr);
68: }
69: end = host_end;
70: }
71: ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
72: if (ret != 0)
73: return ret;
74: host_start += qemu_host_page_size;
75: }
76: if (end < host_end) {
77: prot1 = prot;
78: for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
79: prot1 |= page_get_flags(addr);
80: }
81: ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
82: prot1 & PAGE_BITS);
83: if (ret != 0)
84: return ret;
85: host_end -= qemu_host_page_size;
86: }
87:
88:
89: if (host_start < host_end) {
90: ret = mprotect(g2h(host_start), host_end - host_start, prot);
91: if (ret != 0)
92: return ret;
93: }
94: page_set_flags(start, start + len, prot | PAGE_VALID);
95: return 0;
96: }
97:
98:
99: static int mmap_frag(abi_ulong real_start,
100: abi_ulong start, abi_ulong end,
101: int prot, int flags, int fd, abi_ulong offset)
102: {
103: abi_ulong real_end, addr;
104: void *host_start;
105: int prot1, prot_new;
106:
107: real_end = real_start + qemu_host_page_size;
108: host_start = g2h(real_start);
109:
110:
111: prot1 = 0;
112: for(addr = real_start; addr < real_end; addr++) {
113: if (addr < start || addr >= end)
114: prot1 |= page_get_flags(addr);
115: }
116:
117: if (prot1 == 0) {
118:
119: void *p = mmap(host_start, qemu_host_page_size, prot,
120: flags | MAP_ANONYMOUS, -1, 0);
121: if (p == MAP_FAILED)
122: return -1;
123: prot1 = prot;
124: }
125: prot1 &= PAGE_BITS;
126:
127: prot_new = prot | prot1;
128: if (!(flags & MAP_ANONYMOUS)) {
129:
130:
131: if ((flags & MAP_TYPE) == MAP_SHARED &&
132: (prot & PROT_WRITE))
133: return -EINVAL;
134:
135:
136: if (!(prot1 & PROT_WRITE))
137: mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
138:
139:
140: pread(fd, g2h(start), end - start, offset);
141:
142:
143: if (prot_new != (prot1 | PROT_WRITE))
144: mprotect(host_start, qemu_host_page_size, prot_new);
145: } else {
146:
147: if (prot_new != prot1) {
148: mprotect(host_start, qemu_host_page_size, prot_new);
149: }
150: }
151: return 0;
152: }
153:
154: #if defined(__CYGWIN__)
155:
156: static abi_ulong mmap_next_start = 0x18000000;
157: #else
158: static abi_ulong mmap_next_start = 0x40000000;
159: #endif
160:
161:
162:
163:
164:
165:
166:
167: static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
168: {
169: abi_ulong addr, addr1, addr_start;
170: int prot;
171:
172: size = HOST_PAGE_ALIGN(size);
173: start = start & qemu_host_page_mask;
174: addr = start;
175: if (addr == 0)
176: addr = mmap_next_start;
177: addr_start = addr;
178: for(;;) {
179: prot = 0;
180: for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
181: prot |= page_get_flags(addr1);
182: }
183: if (prot == 0)
184: break;
185: addr += qemu_host_page_size;
186:
187: if (addr == addr_start)
188: return (abi_ulong)-1;
189: }
190: if (start == 0)
191: mmap_next_start = addr + size;
192: return addr;
193: }
194:
195:
196: abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
197: int flags, int fd, abi_ulong offset)
198: {
199: abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
200: unsigned long host_start;
201:
202: #ifdef DEBUG_MMAP
203: {
204: printf("mmap: start=0x" TARGET_FMT_lx
205: " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
206: start, len,
207: prot & PROT_READ ? 'r' : '-',
208: prot & PROT_WRITE ? 'w' : '-',
209: prot & PROT_EXEC ? 'x' : '-');
210: if (flags & MAP_FIXED)
211: printf("MAP_FIXED ");
212: if (flags & MAP_ANONYMOUS)
213: printf("MAP_ANON ");
214: switch(flags & MAP_TYPE) {
215: case MAP_PRIVATE:
216: printf("MAP_PRIVATE ");
217: break;
218: case MAP_SHARED:
219: printf("MAP_SHARED ");
220: break;
221: default:
222: printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
223: break;
224: }
225: printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
226: }
227: #endif
228:
229: if (offset & ~TARGET_PAGE_MASK) {
230: errno = EINVAL;
231: return -1;
232: }
233:
234: len = TARGET_PAGE_ALIGN(len);
235: if (len == 0)
236: return start;
237: real_start = start & qemu_host_page_mask;
238:
239: if (!(flags & MAP_FIXED)) {
240: abi_ulong mmap_start;
241: void *p;
242: host_offset = offset & qemu_host_page_mask;
243: host_len = len + offset - host_offset;
244: host_len = HOST_PAGE_ALIGN(host_len);
245: mmap_start = mmap_find_vma(real_start, host_len);
246: if (mmap_start == (abi_ulong)-1) {
247: errno = ENOMEM;
248: return -1;
249: }
250:
251:
252:
253: p = mmap(g2h(mmap_start),
254: host_len, prot, flags | MAP_FIXED, fd, host_offset);
255: if (p == MAP_FAILED)
256: return -1;
257:
258: host_start = (unsigned long)p;
259: if (!(flags & MAP_ANONYMOUS))
260: host_start += offset - host_offset;
261: start = h2g(host_start);
262: } else {
263: if (start & ~TARGET_PAGE_MASK) {
264: errno = EINVAL;
265: return -1;
266: }
267: end = start + len;
268: real_end = HOST_PAGE_ALIGN(end);
269:
270:
271:
272: if (!(flags & MAP_ANONYMOUS) &&
273: (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
274:
275:
276: if ((flags & MAP_TYPE) == MAP_SHARED &&
277: (prot & PROT_WRITE)) {
278: errno = EINVAL;
279: return -1;
280: }
281: retaddr = target_mmap(start, len, prot | PROT_WRITE,
282: MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
283: -1, 0);
284: if (retaddr == -1)
285: return -1;
286: pread(fd, g2h(start), len, offset);
287: if (!(prot & PROT_WRITE)) {
288: ret = target_mprotect(start, len, prot);
289: if (ret != 0)
290: return ret;
291: }
292: goto the_end;
293: }
294:
295:
296: if (start > real_start) {
297: if (real_end == real_start + qemu_host_page_size) {
298:
299: ret = mmap_frag(real_start, start, end,
300: prot, flags, fd, offset);
301: if (ret == -1)
302: return ret;
303: goto the_end1;
304: }
305: ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
306: prot, flags, fd, offset);
307: if (ret == -1)
308: return ret;
309: real_start += qemu_host_page_size;
310: }
311:
312: if (end < real_end) {
313: ret = mmap_frag(real_end - qemu_host_page_size,
314: real_end - qemu_host_page_size, real_end,
315: prot, flags, fd,
316: offset + real_end - qemu_host_page_size - start);
317: if (ret == -1)
318: return -1;
319: real_end -= qemu_host_page_size;
320: }
321:
322:
323: if (real_start < real_end) {
324: void *p;
325: unsigned long offset1;
326: if (flags & MAP_ANONYMOUS)
327: offset1 = 0;
328: else
329: offset1 = offset + real_start - start;
330: p = mmap(g2h(real_start), real_end - real_start,
331: prot, flags, fd, offset1);
332: if (p == MAP_FAILED)
333: return -1;
334: }
335: }
336: the_end1:
337: page_set_flags(start, start + len, prot | PAGE_VALID);
338: the_end:
339: #ifdef DEBUG_MMAP
340: printf("ret=0x%llx\n", start);
341: page_dump(stdout);
342: printf("\n");
343: #endif
344: return start;
345: }
346:
347: int target_munmap(abi_ulong start, abi_ulong len)
348: {
349: abi_ulong end, real_start, real_end, addr;
350: int prot, ret;
351:
352: #ifdef DEBUG_MMAP
353: printf("munmap: start=0x%lx len=0x%lx\n", start, len);
354: #endif
355: if (start & ~TARGET_PAGE_MASK)
356: return -EINVAL;
357: len = TARGET_PAGE_ALIGN(len);
358: if (len == 0)
359: return -EINVAL;
360: end = start + len;
361: real_start = start & qemu_host_page_mask;
362: real_end = HOST_PAGE_ALIGN(end);
363:
364: if (start > real_start) {
365:
366: prot = 0;
367: for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
368: prot |= page_get_flags(addr);
369: }
370: if (real_end == real_start + qemu_host_page_size) {
371: for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
372: prot |= page_get_flags(addr);
373: }
374: end = real_end;
375: }
376: if (prot != 0)
377: real_start += qemu_host_page_size;
378: }
379: if (end < real_end) {
380: prot = 0;
381: for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
382: prot |= page_get_flags(addr);
383: }
384: if (prot != 0)
385: real_end -= qemu_host_page_size;
386: }
387:
388:
389: if (real_start < real_end) {
390: ret = munmap(g2h(real_start), real_end - real_start);
391: if (ret != 0)
392: return ret;
393: }
394:
395: page_set_flags(start, start + len, 0);
396: return 0;
397: }
398:
399:
400:
401: abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
402: abi_ulong new_size, unsigned long flags,
403: abi_ulong new_addr)
404: {
405: int prot;
406: unsigned long host_addr;
407:
408:
409: host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags);
410: if (host_addr == -1)
411: return -1;
412: new_addr = h2g(host_addr);
413: prot = page_get_flags(old_addr);
414: page_set_flags(old_addr, old_addr + old_size, 0);
415: page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
416: return new_addr;
417: }
418:
419: int target_msync(abi_ulong start, abi_ulong len, int flags)
420: {
421: abi_ulong end;
422:
423: if (start & ~TARGET_PAGE_MASK)
424: return -EINVAL;
425: len = TARGET_PAGE_ALIGN(len);
426: end = start + len;
427: if (end < start)
428: return -EINVAL;
429: if (end == start)
430: return 0;
431:
432: start &= qemu_host_page_mask;
433: return msync(g2h(start), end - start, flags);
434: }
435: