PageRenderTime 63ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/qemu/darwin-user/mmap.c

https://github.com/jehc/MondocosmOS
C | 409 lines | 324 code | 34 blank | 51 comment | 107 complexity | 9b546e77f2dbd1bf9c5080d9b0b93a18 MD5 | raw file
  1. /*
  2. * mmap support for qemu
  3. *
  4. * Copyright (c) 2003 Fabrice Bellard
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <stdarg.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include <errno.h>
  25. #include <sys/mman.h>
  26. #include "qemu.h"
  27. //#define DEBUG_MMAP
  28. /* NOTE: all the constants are the HOST ones */
  29. int target_mprotect(unsigned long start, unsigned long len, int prot)
  30. {
  31. unsigned long end, host_start, host_end, addr;
  32. int prot1, ret;
  33. #ifdef DEBUG_MMAP
  34. printf("mprotect: start=0x%lx len=0x%lx prot=%c%c%c\n", start, len,
  35. prot & PROT_READ ? 'r' : '-',
  36. prot & PROT_WRITE ? 'w' : '-',
  37. prot & PROT_EXEC ? 'x' : '-');
  38. #endif
  39. if ((start & ~TARGET_PAGE_MASK) != 0)
  40. return -EINVAL;
  41. len = TARGET_PAGE_ALIGN(len);
  42. end = start + len;
  43. if (end < start)
  44. return -EINVAL;
  45. if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
  46. return -EINVAL;
  47. if (len == 0)
  48. return 0;
  49. host_start = start & qemu_host_page_mask;
  50. host_end = HOST_PAGE_ALIGN(end);
  51. if (start > host_start) {
  52. /* handle host page containing start */
  53. prot1 = prot;
  54. for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
  55. prot1 |= page_get_flags(addr);
  56. }
  57. if (host_end == host_start + qemu_host_page_size) {
  58. for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
  59. prot1 |= page_get_flags(addr);
  60. }
  61. end = host_end;
  62. }
  63. ret = mprotect((void *)host_start, qemu_host_page_size, prot1 & PAGE_BITS);
  64. if (ret != 0)
  65. return ret;
  66. host_start += qemu_host_page_size;
  67. }
  68. if (end < host_end) {
  69. prot1 = prot;
  70. for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
  71. prot1 |= page_get_flags(addr);
  72. }
  73. ret = mprotect((void *)(host_end - qemu_host_page_size), qemu_host_page_size,
  74. prot1 & PAGE_BITS);
  75. if (ret != 0)
  76. return ret;
  77. host_end -= qemu_host_page_size;
  78. }
  79. /* handle the pages in the middle */
  80. if (host_start < host_end) {
  81. ret = mprotect((void *)host_start, host_end - host_start, prot);
  82. if (ret != 0)
  83. return ret;
  84. }
  85. page_set_flags(start, start + len, prot | PAGE_VALID);
  86. return 0;
  87. }
  88. /* map an incomplete host page */
  89. int mmap_frag(unsigned long host_start,
  90. unsigned long start, unsigned long end,
  91. int prot, int flags, int fd, unsigned long offset)
  92. {
  93. unsigned long host_end, ret, addr;
  94. int prot1, prot_new;
  95. host_end = host_start + qemu_host_page_size;
  96. /* get the protection of the target pages outside the mapping */
  97. prot1 = 0;
  98. for(addr = host_start; addr < host_end; addr++) {
  99. if (addr < start || addr >= end)
  100. prot1 |= page_get_flags(addr);
  101. }
  102. if (prot1 == 0) {
  103. /* no page was there, so we allocate one */
  104. ret = (long)mmap((void *)host_start, qemu_host_page_size, prot,
  105. flags | MAP_ANONYMOUS, -1, 0);
  106. if (ret == -1)
  107. return ret;
  108. }
  109. prot1 &= PAGE_BITS;
  110. prot_new = prot | prot1;
  111. if (!(flags & MAP_ANONYMOUS)) {
  112. /* msync() won't work here, so we return an error if write is
  113. possible while it is a shared mapping */
  114. #ifndef __APPLE__
  115. if ((flags & MAP_TYPE) == MAP_SHARED &&
  116. #else
  117. if ((flags & MAP_SHARED) &&
  118. #endif
  119. (prot & PROT_WRITE))
  120. return -1;
  121. /* adjust protection to be able to read */
  122. if (!(prot1 & PROT_WRITE))
  123. mprotect((void *)host_start, qemu_host_page_size, prot1 | PROT_WRITE);
  124. /* read the corresponding file data */
  125. pread(fd, (void *)start, end - start, offset);
  126. /* put final protection */
  127. if (prot_new != (prot1 | PROT_WRITE))
  128. mprotect((void *)host_start, qemu_host_page_size, prot_new);
  129. } else {
  130. /* just update the protection */
  131. if (prot_new != prot1) {
  132. mprotect((void *)host_start, qemu_host_page_size, prot_new);
  133. }
  134. }
  135. return 0;
  136. }
  137. /* NOTE: all the constants are the HOST ones */
  138. long target_mmap(unsigned long start, unsigned long len, int prot,
  139. int flags, int fd, unsigned long offset)
  140. {
  141. unsigned long ret, end, host_start, host_end, retaddr, host_offset, host_len;
  142. #if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
  143. static unsigned long last_start = 0x40000000;
  144. #endif
  145. #ifdef DEBUG_MMAP
  146. {
  147. printf("mmap: start=0x%lx len=0x%lx prot=%c%c%c flags=",
  148. start, len,
  149. prot & PROT_READ ? 'r' : '-',
  150. prot & PROT_WRITE ? 'w' : '-',
  151. prot & PROT_EXEC ? 'x' : '-');
  152. if (flags & MAP_FIXED)
  153. printf("MAP_FIXED ");
  154. if (flags & MAP_ANONYMOUS)
  155. printf("MAP_ANON ");
  156. #ifndef MAP_TYPE
  157. # define MAP_TYPE 0x3
  158. #endif
  159. switch(flags & MAP_TYPE) {
  160. case MAP_PRIVATE:
  161. printf("MAP_PRIVATE ");
  162. break;
  163. case MAP_SHARED:
  164. printf("MAP_SHARED ");
  165. break;
  166. default:
  167. printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
  168. break;
  169. }
  170. printf("fd=%d offset=%lx\n", fd, offset);
  171. }
  172. #endif
  173. if (offset & ~TARGET_PAGE_MASK)
  174. return -EINVAL;
  175. len = TARGET_PAGE_ALIGN(len);
  176. if (len == 0)
  177. return start;
  178. host_start = start & qemu_host_page_mask;
  179. if (!(flags & MAP_FIXED)) {
  180. #if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
  181. /* tell the kernel to search at the same place as i386 */
  182. if (host_start == 0) {
  183. host_start = last_start;
  184. last_start += HOST_PAGE_ALIGN(len);
  185. }
  186. #endif
  187. if (qemu_host_page_size != qemu_real_host_page_size) {
  188. /* NOTE: this code is only for debugging with '-p' option */
  189. /* reserve a memory area */
  190. host_len = HOST_PAGE_ALIGN(len) + qemu_host_page_size - TARGET_PAGE_SIZE;
  191. host_start = (long)mmap((void *)host_start, host_len, PROT_NONE,
  192. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  193. if (host_start == -1)
  194. return host_start;
  195. host_end = host_start + host_len;
  196. start = HOST_PAGE_ALIGN(host_start);
  197. end = start + HOST_PAGE_ALIGN(len);
  198. if (start > host_start)
  199. munmap((void *)host_start, start - host_start);
  200. if (end < host_end)
  201. munmap((void *)end, host_end - end);
  202. /* use it as a fixed mapping */
  203. flags |= MAP_FIXED;
  204. } else {
  205. /* if not fixed, no need to do anything */
  206. host_offset = offset & qemu_host_page_mask;
  207. host_len = len + offset - host_offset;
  208. start = (long)mmap((void *)host_start, host_len,
  209. prot, flags, fd, host_offset);
  210. if (start == -1)
  211. return start;
  212. /* update start so that it points to the file position at 'offset' */
  213. if (!(flags & MAP_ANONYMOUS))
  214. start += offset - host_offset;
  215. goto the_end1;
  216. }
  217. }
  218. if (start & ~TARGET_PAGE_MASK)
  219. return -EINVAL;
  220. end = start + len;
  221. host_end = HOST_PAGE_ALIGN(end);
  222. /* worst case: we cannot map the file because the offset is not
  223. aligned, so we read it */
  224. if (!(flags & MAP_ANONYMOUS) &&
  225. (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
  226. /* msync() won't work here, so we return an error if write is
  227. possible while it is a shared mapping */
  228. #ifndef __APPLE__
  229. if ((flags & MAP_TYPE) == MAP_SHARED &&
  230. #else
  231. if ((flags & MAP_SHARED) &&
  232. #endif
  233. (prot & PROT_WRITE))
  234. return -EINVAL;
  235. retaddr = target_mmap(start, len, prot | PROT_WRITE,
  236. MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
  237. -1, 0);
  238. if (retaddr == -1)
  239. return retaddr;
  240. pread(fd, (void *)start, len, offset);
  241. if (!(prot & PROT_WRITE)) {
  242. ret = target_mprotect(start, len, prot);
  243. if (ret != 0)
  244. return ret;
  245. }
  246. goto the_end;
  247. }
  248. /* handle the start of the mapping */
  249. if (start > host_start) {
  250. if (host_end == host_start + qemu_host_page_size) {
  251. /* one single host page */
  252. ret = mmap_frag(host_start, start, end,
  253. prot, flags, fd, offset);
  254. if (ret == -1)
  255. return ret;
  256. goto the_end1;
  257. }
  258. ret = mmap_frag(host_start, start, host_start + qemu_host_page_size,
  259. prot, flags, fd, offset);
  260. if (ret == -1)
  261. return ret;
  262. host_start += qemu_host_page_size;
  263. }
  264. /* handle the end of the mapping */
  265. if (end < host_end) {
  266. ret = mmap_frag(host_end - qemu_host_page_size,
  267. host_end - qemu_host_page_size, host_end,
  268. prot, flags, fd,
  269. offset + host_end - qemu_host_page_size - start);
  270. if (ret == -1)
  271. return ret;
  272. host_end -= qemu_host_page_size;
  273. }
  274. /* map the middle (easier) */
  275. if (host_start < host_end) {
  276. unsigned long offset1;
  277. if (flags & MAP_ANONYMOUS)
  278. offset1 = 0;
  279. else
  280. offset1 = offset + host_start - start;
  281. ret = (long)mmap((void *)host_start, host_end - host_start,
  282. prot, flags, fd, offset1);
  283. if (ret == -1)
  284. return ret;
  285. }
  286. the_end1:
  287. page_set_flags(start, start + len, prot | PAGE_VALID);
  288. the_end:
  289. #ifdef DEBUG_MMAP
  290. printf("target_mmap: ret=0x%lx\n", (long)start);
  291. page_dump(stdout);
  292. printf("\n");
  293. #endif
  294. return start;
  295. }
  296. int target_munmap(unsigned long start, unsigned long len)
  297. {
  298. unsigned long end, host_start, host_end, addr;
  299. int prot, ret;
  300. #ifdef DEBUG_MMAP
  301. printf("munmap: start=0x%lx len=0x%lx\n", start, len);
  302. #endif
  303. if (start & ~TARGET_PAGE_MASK)
  304. return -EINVAL;
  305. len = TARGET_PAGE_ALIGN(len);
  306. if (len == 0)
  307. return -EINVAL;
  308. end = start + len;
  309. host_start = start & qemu_host_page_mask;
  310. host_end = HOST_PAGE_ALIGN(end);
  311. if (start > host_start) {
  312. /* handle host page containing start */
  313. prot = 0;
  314. for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
  315. prot |= page_get_flags(addr);
  316. }
  317. if (host_end == host_start + qemu_host_page_size) {
  318. for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
  319. prot |= page_get_flags(addr);
  320. }
  321. end = host_end;
  322. }
  323. if (prot != 0)
  324. host_start += qemu_host_page_size;
  325. }
  326. if (end < host_end) {
  327. prot = 0;
  328. for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
  329. prot |= page_get_flags(addr);
  330. }
  331. if (prot != 0)
  332. host_end -= qemu_host_page_size;
  333. }
  334. /* unmap what we can */
  335. if (host_start < host_end) {
  336. ret = munmap((void *)host_start, host_end - host_start);
  337. if (ret != 0)
  338. return ret;
  339. }
  340. page_set_flags(start, start + len, 0);
  341. return 0;
  342. }
  343. /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
  344. blocks which have been allocated starting on a host page */
  345. long target_mremap(unsigned long old_addr, unsigned long old_size,
  346. unsigned long new_size, unsigned long flags,
  347. unsigned long new_addr)
  348. {
  349. #ifndef __APPLE__
  350. /* XXX: use 5 args syscall */
  351. new_addr = (long)mremap((void *)old_addr, old_size, new_size, flags);
  352. if (new_addr == -1)
  353. return new_addr;
  354. prot = page_get_flags(old_addr);
  355. page_set_flags(old_addr, old_addr + old_size, 0);
  356. page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
  357. return new_addr;
  358. #else
  359. qerror("target_mremap: unsupported\n");
  360. #endif
  361. }
  362. int target_msync(unsigned long start, unsigned long len, int flags)
  363. {
  364. unsigned long end;
  365. if (start & ~TARGET_PAGE_MASK)
  366. return -EINVAL;
  367. len = TARGET_PAGE_ALIGN(len);
  368. end = start + len;
  369. if (end < start)
  370. return -EINVAL;
  371. if (end == start)
  372. return 0;
  373. start &= qemu_host_page_mask;
  374. return msync((void *)start, end - start, flags);
  375. }