154936004Sbellard /* 254936004Sbellard * mmap support for qemu 354936004Sbellard * 454936004Sbellard * Copyright (c) 2003 Fabrice Bellard 554936004Sbellard * 654936004Sbellard * This program is free software; you can redistribute it and/or modify 754936004Sbellard * it under the terms of the GNU General Public License as published by 854936004Sbellard * the Free Software Foundation; either version 2 of the License, or 954936004Sbellard * (at your option) any later version. 1054936004Sbellard * 1154936004Sbellard * This program is distributed in the hope that it will be useful, 1254936004Sbellard * but WITHOUT ANY WARRANTY; without even the implied warranty of 1354936004Sbellard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1454936004Sbellard * GNU General Public License for more details. 1554936004Sbellard * 1654936004Sbellard * You should have received a copy of the GNU General Public License 178167ee88SBlue Swirl * along with this program; if not, see <http://www.gnu.org/licenses/>. 1854936004Sbellard */ 1954936004Sbellard #include <stdlib.h> 2054936004Sbellard #include <stdio.h> 2154936004Sbellard #include <stdarg.h> 2254936004Sbellard #include <string.h> 2354936004Sbellard #include <unistd.h> 2454936004Sbellard #include <errno.h> 2554c5a2aeSedgar_igl #include <sys/types.h> 2654c5a2aeSedgar_igl #include <sys/stat.h> 2754936004Sbellard #include <sys/mman.h> 283af72a4dSblueswir1 #include <linux/mman.h> 293af72a4dSblueswir1 #include <linux/unistd.h> 3054936004Sbellard 3154936004Sbellard #include "qemu.h" 3278f5bf1eSblueswir1 #include "qemu-common.h" 331652b974SPaolo Bonzini #include "translate-all.h" 3454936004Sbellard 3554936004Sbellard //#define DEBUG_MMAP 3654936004Sbellard 371e6eec8bSBlue Swirl static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; 38dfd3f85cSJuan Quintela static __thread int mmap_lock_count; 39c8a706feSpbrook 40c8a706feSpbrook void mmap_lock(void) 41c8a706feSpbrook { 42c8a706feSpbrook if (mmap_lock_count++ == 0) { 43c8a706feSpbrook pthread_mutex_lock(&mmap_mutex); 44c8a706feSpbrook } 45c8a706feSpbrook } 46c8a706feSpbrook 47c8a706feSpbrook void mmap_unlock(void) 48c8a706feSpbrook { 49c8a706feSpbrook if (--mmap_lock_count == 0) { 50c8a706feSpbrook pthread_mutex_unlock(&mmap_mutex); 51c8a706feSpbrook } 52c8a706feSpbrook } 53d5975363Spbrook 54d5975363Spbrook /* Grab lock to make sure things are in a consistent state after fork(). */ 55d5975363Spbrook void mmap_fork_start(void) 56d5975363Spbrook { 57d5975363Spbrook if (mmap_lock_count) 58d5975363Spbrook abort(); 59d5975363Spbrook pthread_mutex_lock(&mmap_mutex); 60d5975363Spbrook } 61d5975363Spbrook 62d5975363Spbrook void mmap_fork_end(int child) 63d5975363Spbrook { 64d5975363Spbrook if (child) 65d5975363Spbrook pthread_mutex_init(&mmap_mutex, NULL); 66d5975363Spbrook else 67d5975363Spbrook pthread_mutex_unlock(&mmap_mutex); 68d5975363Spbrook } 69c8a706feSpbrook 7053a5960aSpbrook /* NOTE: all the constants are the HOST ones, but addresses are target. */ 71992f48a0Sblueswir1 int target_mprotect(abi_ulong start, abi_ulong len, int prot) 7254936004Sbellard { 73992f48a0Sblueswir1 abi_ulong end, host_start, host_end, addr; 7454936004Sbellard int prot1, ret; 7554936004Sbellard 7654936004Sbellard #ifdef DEBUG_MMAP 770bf9e31aSBlue Swirl printf("mprotect: start=0x" TARGET_ABI_FMT_lx 780bf9e31aSBlue Swirl "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len, 7954936004Sbellard prot & PROT_READ ? 'r' : '-', 8054936004Sbellard prot & PROT_WRITE ? 'w' : '-', 8154936004Sbellard prot & PROT_EXEC ? 'x' : '-'); 8254936004Sbellard #endif 8354936004Sbellard 8454936004Sbellard if ((start & ~TARGET_PAGE_MASK) != 0) 8554936004Sbellard return -EINVAL; 8654936004Sbellard len = TARGET_PAGE_ALIGN(len); 8754936004Sbellard end = start + len; 8854936004Sbellard if (end < start) 8954936004Sbellard return -EINVAL; 90171cd1cdSbalrog prot &= PROT_READ | PROT_WRITE | PROT_EXEC; 9154936004Sbellard if (len == 0) 9254936004Sbellard return 0; 9354936004Sbellard 94c8a706feSpbrook mmap_lock(); 9583fb7adfSbellard host_start = start & qemu_host_page_mask; 9654936004Sbellard host_end = HOST_PAGE_ALIGN(end); 9754936004Sbellard if (start > host_start) { 9854936004Sbellard /* handle host page containing start */ 9954936004Sbellard prot1 = prot; 10054936004Sbellard for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) { 10154936004Sbellard prot1 |= page_get_flags(addr); 10254936004Sbellard } 10383fb7adfSbellard if (host_end == host_start + qemu_host_page_size) { 104d418c81eSbellard for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { 105d418c81eSbellard prot1 |= page_get_flags(addr); 106d418c81eSbellard } 107d418c81eSbellard end = host_end; 108d418c81eSbellard } 10953a5960aSpbrook ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS); 11054936004Sbellard if (ret != 0) 111c8a706feSpbrook goto error; 11283fb7adfSbellard host_start += qemu_host_page_size; 11354936004Sbellard } 11454936004Sbellard if (end < host_end) { 11554936004Sbellard prot1 = prot; 11654936004Sbellard for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { 11754936004Sbellard prot1 |= page_get_flags(addr); 11854936004Sbellard } 11953a5960aSpbrook ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size, 12054936004Sbellard prot1 & PAGE_BITS); 12154936004Sbellard if (ret != 0) 122c8a706feSpbrook goto error; 12383fb7adfSbellard host_end -= qemu_host_page_size; 12454936004Sbellard } 12554936004Sbellard 12654936004Sbellard /* handle the pages in the middle */ 12754936004Sbellard if (host_start < host_end) { 12853a5960aSpbrook ret = mprotect(g2h(host_start), host_end - host_start, prot); 12954936004Sbellard if (ret != 0) 130c8a706feSpbrook goto error; 13154936004Sbellard } 13254936004Sbellard page_set_flags(start, start + len, prot | PAGE_VALID); 133c8a706feSpbrook mmap_unlock(); 13454936004Sbellard return 0; 135c8a706feSpbrook error: 136c8a706feSpbrook mmap_unlock(); 137c8a706feSpbrook return ret; 13854936004Sbellard } 13954936004Sbellard 14054936004Sbellard /* map an incomplete host page */ 141992f48a0Sblueswir1 static int mmap_frag(abi_ulong real_start, 142992f48a0Sblueswir1 abi_ulong start, abi_ulong end, 143992f48a0Sblueswir1 int prot, int flags, int fd, abi_ulong offset) 14454936004Sbellard { 14580210bcdSths abi_ulong real_end, addr; 14653a5960aSpbrook void *host_start; 14754936004Sbellard int prot1, prot_new; 14854936004Sbellard 14953a5960aSpbrook real_end = real_start + qemu_host_page_size; 15053a5960aSpbrook host_start = g2h(real_start); 15154936004Sbellard 15254936004Sbellard /* get the protection of the target pages outside the mapping */ 15354936004Sbellard prot1 = 0; 15453a5960aSpbrook for(addr = real_start; addr < real_end; addr++) { 15554936004Sbellard if (addr < start || addr >= end) 15654936004Sbellard prot1 |= page_get_flags(addr); 15754936004Sbellard } 15854936004Sbellard 15954936004Sbellard if (prot1 == 0) { 16054936004Sbellard /* no page was there, so we allocate one */ 16180210bcdSths void *p = mmap(host_start, qemu_host_page_size, prot, 16254936004Sbellard flags | MAP_ANONYMOUS, -1, 0); 16380210bcdSths if (p == MAP_FAILED) 16480210bcdSths return -1; 16553a5960aSpbrook prot1 = prot; 16654936004Sbellard } 16754936004Sbellard prot1 &= PAGE_BITS; 16854936004Sbellard 16954936004Sbellard prot_new = prot | prot1; 17054936004Sbellard if (!(flags & MAP_ANONYMOUS)) { 17154936004Sbellard /* msync() won't work here, so we return an error if write is 17254936004Sbellard possible while it is a shared mapping */ 17354936004Sbellard if ((flags & MAP_TYPE) == MAP_SHARED && 17454936004Sbellard (prot & PROT_WRITE)) 175ee636500SJuan Quintela return -1; 17654936004Sbellard 17754936004Sbellard /* adjust protection to be able to read */ 17854936004Sbellard if (!(prot1 & PROT_WRITE)) 17953a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); 18054936004Sbellard 18154936004Sbellard /* read the corresponding file data */ 182fb7e378cSKirill A. Shutemov if (pread(fd, g2h(start), end - start, offset) == -1) 183fb7e378cSKirill A. Shutemov return -1; 18454936004Sbellard 18554936004Sbellard /* put final protection */ 18654936004Sbellard if (prot_new != (prot1 | PROT_WRITE)) 18753a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot_new); 18854936004Sbellard } else { 18954936004Sbellard if (prot_new != prot1) { 19053a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot_new); 19154936004Sbellard } 192e6deac9cSChen Gang if (prot_new & PROT_WRITE) { 193e6deac9cSChen Gang memset(g2h(start), 0, end - start); 194e6deac9cSChen Gang } 19554936004Sbellard } 19654936004Sbellard return 0; 19754936004Sbellard } 19854936004Sbellard 19914f24e14SRichard Henderson #if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64 20014f24e14SRichard Henderson # define TASK_UNMAPPED_BASE (1ul << 38) 20114f24e14SRichard Henderson #elif defined(__CYGWIN__) 202a03e2d42Sbellard /* Cygwin doesn't have a whole lot of address space. */ 20314f24e14SRichard Henderson # define TASK_UNMAPPED_BASE 0x18000000 204a03e2d42Sbellard #else 20514f24e14SRichard Henderson # define TASK_UNMAPPED_BASE 0x40000000 206a03e2d42Sbellard #endif 20759e9d91cSPeter Maydell abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; 208a03e2d42Sbellard 2090776590dSpbrook unsigned long last_brk; 2100776590dSpbrook 21168a1c816SPaul Brook /* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk 21268a1c816SPaul Brook of guest address space. */ 21368a1c816SPaul Brook static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) 21468a1c816SPaul Brook { 21568a1c816SPaul Brook abi_ulong addr; 21659e9d91cSPeter Maydell abi_ulong end_addr; 21768a1c816SPaul Brook int prot; 21868a1c816SPaul Brook int looped = 0; 21968a1c816SPaul Brook 220b76f21a7SLaurent Vivier if (size > reserved_va) { 22168a1c816SPaul Brook return (abi_ulong)-1; 22268a1c816SPaul Brook } 22368a1c816SPaul Brook 22459e9d91cSPeter Maydell size = HOST_PAGE_ALIGN(size); 22559e9d91cSPeter Maydell end_addr = start + size; 226b76f21a7SLaurent Vivier if (end_addr > reserved_va) { 227b76f21a7SLaurent Vivier end_addr = reserved_va; 22859e9d91cSPeter Maydell } 22959e9d91cSPeter Maydell addr = end_addr - qemu_host_page_size; 23059e9d91cSPeter Maydell 23159e9d91cSPeter Maydell while (1) { 23259e9d91cSPeter Maydell if (addr > end_addr) { 23368a1c816SPaul Brook if (looped) { 23468a1c816SPaul Brook return (abi_ulong)-1; 23568a1c816SPaul Brook } 236b76f21a7SLaurent Vivier end_addr = reserved_va; 23759e9d91cSPeter Maydell addr = end_addr - qemu_host_page_size; 23868a1c816SPaul Brook looped = 1; 23968a1c816SPaul Brook continue; 24068a1c816SPaul Brook } 24168a1c816SPaul Brook prot = page_get_flags(addr); 24268a1c816SPaul Brook if (prot) { 24359e9d91cSPeter Maydell end_addr = addr; 24468a1c816SPaul Brook } 24559e9d91cSPeter Maydell if (addr + size == end_addr) { 24659e9d91cSPeter Maydell break; 24768a1c816SPaul Brook } 24859e9d91cSPeter Maydell addr -= qemu_host_page_size; 24959e9d91cSPeter Maydell } 25059e9d91cSPeter Maydell 25159e9d91cSPeter Maydell if (start == mmap_next_start) { 25268a1c816SPaul Brook mmap_next_start = addr; 25359e9d91cSPeter Maydell } 25459e9d91cSPeter Maydell 25559e9d91cSPeter Maydell return addr; 25668a1c816SPaul Brook } 25768a1c816SPaul Brook 258fe3b4152SKirill A. Shutemov /* 259fe3b4152SKirill A. Shutemov * Find and reserve a free memory area of size 'size'. The search 260fe3b4152SKirill A. Shutemov * starts at 'start'. 261fe3b4152SKirill A. Shutemov * It must be called with mmap_lock() held. 262fe3b4152SKirill A. Shutemov * Return -1 if error. 263a03e2d42Sbellard */ 2649ad197d9SRiku Voipio abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) 265a03e2d42Sbellard { 26614f24e14SRichard Henderson void *ptr, *prev; 267fe3b4152SKirill A. Shutemov abi_ulong addr; 26814f24e14SRichard Henderson int wrapped, repeat; 269fe3b4152SKirill A. Shutemov 270fe3b4152SKirill A. Shutemov /* If 'start' == 0, then a default start address is used. */ 27114f24e14SRichard Henderson if (start == 0) { 272fe3b4152SKirill A. Shutemov start = mmap_next_start; 27314f24e14SRichard Henderson } else { 27414f24e14SRichard Henderson start &= qemu_host_page_mask; 27514f24e14SRichard Henderson } 27614f24e14SRichard Henderson 27714f24e14SRichard Henderson size = HOST_PAGE_ALIGN(size); 278fe3b4152SKirill A. Shutemov 279b76f21a7SLaurent Vivier if (reserved_va) { 28068a1c816SPaul Brook return mmap_find_vma_reserved(start, size); 28168a1c816SPaul Brook } 28268a1c816SPaul Brook 283a03e2d42Sbellard addr = start; 28414f24e14SRichard Henderson wrapped = repeat = 0; 28514f24e14SRichard Henderson prev = 0; 286fe3b4152SKirill A. Shutemov 28714f24e14SRichard Henderson for (;; prev = ptr) { 288fe3b4152SKirill A. Shutemov /* 289fe3b4152SKirill A. Shutemov * Reserve needed memory area to avoid a race. 290fe3b4152SKirill A. Shutemov * It should be discarded using: 291fe3b4152SKirill A. Shutemov * - mmap() with MAP_FIXED flag 292fe3b4152SKirill A. Shutemov * - mremap() with MREMAP_FIXED flag 293fe3b4152SKirill A. Shutemov * - shmat() with SHM_REMAP flag 294fe3b4152SKirill A. Shutemov */ 29514f24e14SRichard Henderson ptr = mmap(g2h(addr), size, PROT_NONE, 296fe3b4152SKirill A. Shutemov MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); 297fe3b4152SKirill A. Shutemov 298fe3b4152SKirill A. Shutemov /* ENOMEM, if host address space has no memory */ 29914f24e14SRichard Henderson if (ptr == MAP_FAILED) { 300a03e2d42Sbellard return (abi_ulong)-1; 301a03e2d42Sbellard } 302fe3b4152SKirill A. Shutemov 30314f24e14SRichard Henderson /* Count the number of sequential returns of the same address. 30414f24e14SRichard Henderson This is used to modify the search algorithm below. */ 30514f24e14SRichard Henderson repeat = (ptr == prev ? repeat + 1 : 0); 306fe3b4152SKirill A. Shutemov 30714f24e14SRichard Henderson if (h2g_valid(ptr + size - 1)) { 30814f24e14SRichard Henderson addr = h2g(ptr); 30914f24e14SRichard Henderson 31014f24e14SRichard Henderson if ((addr & ~TARGET_PAGE_MASK) == 0) { 31114f24e14SRichard Henderson /* Success. */ 31214f24e14SRichard Henderson if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) { 31314f24e14SRichard Henderson mmap_next_start = addr + size; 31414f24e14SRichard Henderson } 31514f24e14SRichard Henderson return addr; 31614f24e14SRichard Henderson } 31714f24e14SRichard Henderson 31814f24e14SRichard Henderson /* The address is not properly aligned for the target. */ 31914f24e14SRichard Henderson switch (repeat) { 32014f24e14SRichard Henderson case 0: 32114f24e14SRichard Henderson /* Assume the result that the kernel gave us is the 32214f24e14SRichard Henderson first with enough free space, so start again at the 32314f24e14SRichard Henderson next higher target page. */ 32414f24e14SRichard Henderson addr = TARGET_PAGE_ALIGN(addr); 32514f24e14SRichard Henderson break; 32614f24e14SRichard Henderson case 1: 32714f24e14SRichard Henderson /* Sometimes the kernel decides to perform the allocation 32814f24e14SRichard Henderson at the top end of memory instead. */ 32914f24e14SRichard Henderson addr &= TARGET_PAGE_MASK; 33014f24e14SRichard Henderson break; 33114f24e14SRichard Henderson case 2: 33214f24e14SRichard Henderson /* Start over at low memory. */ 33314f24e14SRichard Henderson addr = 0; 33414f24e14SRichard Henderson break; 33514f24e14SRichard Henderson default: 33614f24e14SRichard Henderson /* Fail. This unaligned block must the last. */ 33714f24e14SRichard Henderson addr = -1; 33814f24e14SRichard Henderson break; 33914f24e14SRichard Henderson } 34014f24e14SRichard Henderson } else { 34114f24e14SRichard Henderson /* Since the result the kernel gave didn't fit, start 34214f24e14SRichard Henderson again at low memory. If any repetition, fail. */ 34314f24e14SRichard Henderson addr = (repeat ? -1 : 0); 34414f24e14SRichard Henderson } 34514f24e14SRichard Henderson 34614f24e14SRichard Henderson /* Unmap and try again. */ 34714f24e14SRichard Henderson munmap(ptr, size); 34814f24e14SRichard Henderson 34914f24e14SRichard Henderson /* ENOMEM if we checked the whole of the target address space. */ 350d0b3e4f5SBlue Swirl if (addr == (abi_ulong)-1) { 35114f24e14SRichard Henderson return (abi_ulong)-1; 35214f24e14SRichard Henderson } else if (addr == 0) { 35314f24e14SRichard Henderson if (wrapped) { 35414f24e14SRichard Henderson return (abi_ulong)-1; 35514f24e14SRichard Henderson } 35614f24e14SRichard Henderson wrapped = 1; 35714f24e14SRichard Henderson /* Don't actually use 0 when wrapping, instead indicate 3588186e783SStefan Weil that we'd truly like an allocation in low memory. */ 35914f24e14SRichard Henderson addr = (mmap_min_addr > TARGET_PAGE_SIZE 36014f24e14SRichard Henderson ? TARGET_PAGE_ALIGN(mmap_min_addr) 36114f24e14SRichard Henderson : TARGET_PAGE_SIZE); 36214f24e14SRichard Henderson } else if (wrapped && addr >= start) { 36314f24e14SRichard Henderson return (abi_ulong)-1; 36414f24e14SRichard Henderson } 36514f24e14SRichard Henderson } 366a03e2d42Sbellard } 367a03e2d42Sbellard 36854936004Sbellard /* NOTE: all the constants are the HOST ones */ 369992f48a0Sblueswir1 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, 370992f48a0Sblueswir1 int flags, int fd, abi_ulong offset) 37154936004Sbellard { 372992f48a0Sblueswir1 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; 37354936004Sbellard 374c8a706feSpbrook mmap_lock(); 37554936004Sbellard #ifdef DEBUG_MMAP 37654936004Sbellard { 3770bf9e31aSBlue Swirl printf("mmap: start=0x" TARGET_ABI_FMT_lx 3780bf9e31aSBlue Swirl " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=", 37954936004Sbellard start, len, 38054936004Sbellard prot & PROT_READ ? 'r' : '-', 38154936004Sbellard prot & PROT_WRITE ? 'w' : '-', 38254936004Sbellard prot & PROT_EXEC ? 'x' : '-'); 38354936004Sbellard if (flags & MAP_FIXED) 38454936004Sbellard printf("MAP_FIXED "); 38554936004Sbellard if (flags & MAP_ANONYMOUS) 38654936004Sbellard printf("MAP_ANON "); 38754936004Sbellard switch(flags & MAP_TYPE) { 38854936004Sbellard case MAP_PRIVATE: 38954936004Sbellard printf("MAP_PRIVATE "); 39054936004Sbellard break; 39154936004Sbellard case MAP_SHARED: 39254936004Sbellard printf("MAP_SHARED "); 39354936004Sbellard break; 39454936004Sbellard default: 39554936004Sbellard printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); 39654936004Sbellard break; 39754936004Sbellard } 3980bf9e31aSBlue Swirl printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset); 39954936004Sbellard } 40054936004Sbellard #endif 40154936004Sbellard 402e89f07d3Spbrook if (offset & ~TARGET_PAGE_MASK) { 403e89f07d3Spbrook errno = EINVAL; 404c8a706feSpbrook goto fail; 405e89f07d3Spbrook } 40654936004Sbellard 40754936004Sbellard len = TARGET_PAGE_ALIGN(len); 40854936004Sbellard if (len == 0) 409c8a706feSpbrook goto the_end; 41053a5960aSpbrook real_start = start & qemu_host_page_mask; 411a5e7ee46SRichard Henderson host_offset = offset & qemu_host_page_mask; 412a5e7ee46SRichard Henderson 413a5e7ee46SRichard Henderson /* If the user is asking for the kernel to find a location, do that 414a5e7ee46SRichard Henderson before we truncate the length for mapping files below. */ 415a5e7ee46SRichard Henderson if (!(flags & MAP_FIXED)) { 416a5e7ee46SRichard Henderson host_len = len + offset - host_offset; 417a5e7ee46SRichard Henderson host_len = HOST_PAGE_ALIGN(host_len); 418a5e7ee46SRichard Henderson start = mmap_find_vma(real_start, host_len); 419a5e7ee46SRichard Henderson if (start == (abi_ulong)-1) { 420a5e7ee46SRichard Henderson errno = ENOMEM; 421a5e7ee46SRichard Henderson goto fail; 422a5e7ee46SRichard Henderson } 423a5e7ee46SRichard Henderson } 42454936004Sbellard 42554c5a2aeSedgar_igl /* When mapping files into a memory area larger than the file, accesses 42654c5a2aeSedgar_igl to pages beyond the file size will cause a SIGBUS. 42754c5a2aeSedgar_igl 42854c5a2aeSedgar_igl For example, if mmaping a file of 100 bytes on a host with 4K pages 42954c5a2aeSedgar_igl emulating a target with 8K pages, the target expects to be able to 43054c5a2aeSedgar_igl access the first 8K. But the host will trap us on any access beyond 43154c5a2aeSedgar_igl 4K. 43254c5a2aeSedgar_igl 43354c5a2aeSedgar_igl When emulating a target with a larger page-size than the hosts, we 43454c5a2aeSedgar_igl may need to truncate file maps at EOF and add extra anonymous pages 43554c5a2aeSedgar_igl up to the targets page boundary. */ 43654c5a2aeSedgar_igl 43754c5a2aeSedgar_igl if ((qemu_real_host_page_size < TARGET_PAGE_SIZE) 43854c5a2aeSedgar_igl && !(flags & MAP_ANONYMOUS)) { 43954c5a2aeSedgar_igl struct stat sb; 44054c5a2aeSedgar_igl 44154c5a2aeSedgar_igl if (fstat (fd, &sb) == -1) 44254c5a2aeSedgar_igl goto fail; 44354c5a2aeSedgar_igl 44454c5a2aeSedgar_igl /* Are we trying to create a map beyond EOF?. */ 44554c5a2aeSedgar_igl if (offset + len > sb.st_size) { 44654c5a2aeSedgar_igl /* If so, truncate the file map at eof aligned with 44754c5a2aeSedgar_igl the hosts real pagesize. Additional anonymous maps 44854c5a2aeSedgar_igl will be created beyond EOF. */ 4490c2d70c4SPaolo Bonzini len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset); 45054c5a2aeSedgar_igl } 45154c5a2aeSedgar_igl } 45254c5a2aeSedgar_igl 45354936004Sbellard if (!(flags & MAP_FIXED)) { 454a5e7ee46SRichard Henderson unsigned long host_start; 455a03e2d42Sbellard void *p; 456a5e7ee46SRichard Henderson 45754936004Sbellard host_len = len + offset - host_offset; 458a03e2d42Sbellard host_len = HOST_PAGE_ALIGN(host_len); 459a5e7ee46SRichard Henderson 460a03e2d42Sbellard /* Note: we prefer to control the mapping address. It is 461a03e2d42Sbellard especially important if qemu_host_page_size > 462a03e2d42Sbellard qemu_real_host_page_size */ 463a5e7ee46SRichard Henderson p = mmap(g2h(start), host_len, prot, 464a5e7ee46SRichard Henderson flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0); 46580210bcdSths if (p == MAP_FAILED) 466c8a706feSpbrook goto fail; 46754936004Sbellard /* update start so that it points to the file position at 'offset' */ 46880210bcdSths host_start = (unsigned long)p; 46954c5a2aeSedgar_igl if (!(flags & MAP_ANONYMOUS)) { 470a5e7ee46SRichard Henderson p = mmap(g2h(start), len, prot, 47154c5a2aeSedgar_igl flags | MAP_FIXED, fd, host_offset); 4728384274eSJürg Billeter if (p == MAP_FAILED) { 4738384274eSJürg Billeter munmap(g2h(start), host_len); 4748384274eSJürg Billeter goto fail; 4758384274eSJürg Billeter } 47653a5960aSpbrook host_start += offset - host_offset; 47754c5a2aeSedgar_igl } 47853a5960aSpbrook start = h2g(host_start); 479a03e2d42Sbellard } else { 480e89f07d3Spbrook if (start & ~TARGET_PAGE_MASK) { 481e89f07d3Spbrook errno = EINVAL; 482c8a706feSpbrook goto fail; 483e89f07d3Spbrook } 48454936004Sbellard end = start + len; 48553a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 48654936004Sbellard 48745bc1f52Saurel32 /* 48845bc1f52Saurel32 * Test if requested memory area fits target address space 48945bc1f52Saurel32 * It can fail only on 64-bit host with 32-bit target. 49045bc1f52Saurel32 * On any other target/host host mmap() handles this error correctly. 49145bc1f52Saurel32 */ 49245bc1f52Saurel32 if ((unsigned long)start + len - 1 > (abi_ulong) -1) { 49345bc1f52Saurel32 errno = EINVAL; 49445bc1f52Saurel32 goto fail; 49545bc1f52Saurel32 } 49645bc1f52Saurel32 49754936004Sbellard /* worst case: we cannot map the file because the offset is not 49854936004Sbellard aligned, so we read it */ 49954936004Sbellard if (!(flags & MAP_ANONYMOUS) && 50083fb7adfSbellard (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { 50154936004Sbellard /* msync() won't work here, so we return an error if write is 50254936004Sbellard possible while it is a shared mapping */ 50354936004Sbellard if ((flags & MAP_TYPE) == MAP_SHARED && 504e89f07d3Spbrook (prot & PROT_WRITE)) { 505e89f07d3Spbrook errno = EINVAL; 506c8a706feSpbrook goto fail; 507e89f07d3Spbrook } 50854936004Sbellard retaddr = target_mmap(start, len, prot | PROT_WRITE, 50954936004Sbellard MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 51054936004Sbellard -1, 0); 51154936004Sbellard if (retaddr == -1) 512c8a706feSpbrook goto fail; 513fb7e378cSKirill A. Shutemov if (pread(fd, g2h(start), len, offset) == -1) 514fb7e378cSKirill A. Shutemov goto fail; 51554936004Sbellard if (!(prot & PROT_WRITE)) { 51654936004Sbellard ret = target_mprotect(start, len, prot); 51786abac06SPaolo Bonzini assert(ret == 0); 51854936004Sbellard } 51954936004Sbellard goto the_end; 52054936004Sbellard } 52154936004Sbellard 52254936004Sbellard /* handle the start of the mapping */ 52353a5960aSpbrook if (start > real_start) { 52453a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 52554936004Sbellard /* one single host page */ 52653a5960aSpbrook ret = mmap_frag(real_start, start, end, 52754936004Sbellard prot, flags, fd, offset); 52854936004Sbellard if (ret == -1) 529c8a706feSpbrook goto fail; 53054936004Sbellard goto the_end1; 53154936004Sbellard } 53253a5960aSpbrook ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, 53354936004Sbellard prot, flags, fd, offset); 53454936004Sbellard if (ret == -1) 535c8a706feSpbrook goto fail; 53653a5960aSpbrook real_start += qemu_host_page_size; 53754936004Sbellard } 53854936004Sbellard /* handle the end of the mapping */ 53953a5960aSpbrook if (end < real_end) { 54053a5960aSpbrook ret = mmap_frag(real_end - qemu_host_page_size, 541*530c0032SChen Gang real_end - qemu_host_page_size, end, 54254936004Sbellard prot, flags, fd, 54353a5960aSpbrook offset + real_end - qemu_host_page_size - start); 54454936004Sbellard if (ret == -1) 545c8a706feSpbrook goto fail; 54653a5960aSpbrook real_end -= qemu_host_page_size; 54754936004Sbellard } 54854936004Sbellard 54954936004Sbellard /* map the middle (easier) */ 55053a5960aSpbrook if (real_start < real_end) { 55180210bcdSths void *p; 5524a585ccbSbellard unsigned long offset1; 5534a585ccbSbellard if (flags & MAP_ANONYMOUS) 5544a585ccbSbellard offset1 = 0; 5554a585ccbSbellard else 55653a5960aSpbrook offset1 = offset + real_start - start; 55780210bcdSths p = mmap(g2h(real_start), real_end - real_start, 5584a585ccbSbellard prot, flags, fd, offset1); 55980210bcdSths if (p == MAP_FAILED) 560c8a706feSpbrook goto fail; 56154936004Sbellard } 562a03e2d42Sbellard } 56354936004Sbellard the_end1: 56454936004Sbellard page_set_flags(start, start + len, prot | PAGE_VALID); 56554936004Sbellard the_end: 56654936004Sbellard #ifdef DEBUG_MMAP 5670bf9e31aSBlue Swirl printf("ret=0x" TARGET_ABI_FMT_lx "\n", start); 56854936004Sbellard page_dump(stdout); 56954936004Sbellard printf("\n"); 57054936004Sbellard #endif 57135865339SPaolo Bonzini tb_invalidate_phys_range(start, start + len); 572c8a706feSpbrook mmap_unlock(); 57354936004Sbellard return start; 574c8a706feSpbrook fail: 575c8a706feSpbrook mmap_unlock(); 576c8a706feSpbrook return -1; 57754936004Sbellard } 57854936004Sbellard 57968a1c816SPaul Brook static void mmap_reserve(abi_ulong start, abi_ulong size) 58068a1c816SPaul Brook { 58168a1c816SPaul Brook abi_ulong real_start; 58268a1c816SPaul Brook abi_ulong real_end; 58368a1c816SPaul Brook abi_ulong addr; 58468a1c816SPaul Brook abi_ulong end; 58568a1c816SPaul Brook int prot; 58668a1c816SPaul Brook 58768a1c816SPaul Brook real_start = start & qemu_host_page_mask; 58868a1c816SPaul Brook real_end = HOST_PAGE_ALIGN(start + size); 58968a1c816SPaul Brook end = start + size; 59068a1c816SPaul Brook if (start > real_start) { 59168a1c816SPaul Brook /* handle host page containing start */ 59268a1c816SPaul Brook prot = 0; 59368a1c816SPaul Brook for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { 59468a1c816SPaul Brook prot |= page_get_flags(addr); 59568a1c816SPaul Brook } 59668a1c816SPaul Brook if (real_end == real_start + qemu_host_page_size) { 59768a1c816SPaul Brook for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 59868a1c816SPaul Brook prot |= page_get_flags(addr); 59968a1c816SPaul Brook } 60068a1c816SPaul Brook end = real_end; 60168a1c816SPaul Brook } 60268a1c816SPaul Brook if (prot != 0) 60368a1c816SPaul Brook real_start += qemu_host_page_size; 60468a1c816SPaul Brook } 60568a1c816SPaul Brook if (end < real_end) { 60668a1c816SPaul Brook prot = 0; 60768a1c816SPaul Brook for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 60868a1c816SPaul Brook prot |= page_get_flags(addr); 60968a1c816SPaul Brook } 61068a1c816SPaul Brook if (prot != 0) 61168a1c816SPaul Brook real_end -= qemu_host_page_size; 61268a1c816SPaul Brook } 61368a1c816SPaul Brook if (real_start != real_end) { 61468a1c816SPaul Brook mmap(g2h(real_start), real_end - real_start, PROT_NONE, 61568a1c816SPaul Brook MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, 61668a1c816SPaul Brook -1, 0); 61768a1c816SPaul Brook } 61868a1c816SPaul Brook } 61968a1c816SPaul Brook 620992f48a0Sblueswir1 int target_munmap(abi_ulong start, abi_ulong len) 62154936004Sbellard { 622992f48a0Sblueswir1 abi_ulong end, real_start, real_end, addr; 62354936004Sbellard int prot, ret; 62454936004Sbellard 62554936004Sbellard #ifdef DEBUG_MMAP 6260bf9e31aSBlue Swirl printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x" 6270bf9e31aSBlue Swirl TARGET_ABI_FMT_lx "\n", 6280bf9e31aSBlue Swirl start, len); 62954936004Sbellard #endif 63054936004Sbellard if (start & ~TARGET_PAGE_MASK) 63154936004Sbellard return -EINVAL; 63254936004Sbellard len = TARGET_PAGE_ALIGN(len); 63354936004Sbellard if (len == 0) 63454936004Sbellard return -EINVAL; 635c8a706feSpbrook mmap_lock(); 63654936004Sbellard end = start + len; 63753a5960aSpbrook real_start = start & qemu_host_page_mask; 63853a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 63954936004Sbellard 64053a5960aSpbrook if (start > real_start) { 64154936004Sbellard /* handle host page containing start */ 64254936004Sbellard prot = 0; 64353a5960aSpbrook for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { 64454936004Sbellard prot |= page_get_flags(addr); 64554936004Sbellard } 64653a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 64753a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 648d418c81eSbellard prot |= page_get_flags(addr); 649d418c81eSbellard } 65053a5960aSpbrook end = real_end; 651d418c81eSbellard } 65254936004Sbellard if (prot != 0) 65353a5960aSpbrook real_start += qemu_host_page_size; 65454936004Sbellard } 65553a5960aSpbrook if (end < real_end) { 65654936004Sbellard prot = 0; 65753a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 65854936004Sbellard prot |= page_get_flags(addr); 65954936004Sbellard } 66054936004Sbellard if (prot != 0) 66153a5960aSpbrook real_end -= qemu_host_page_size; 66254936004Sbellard } 66354936004Sbellard 664c8a706feSpbrook ret = 0; 66554936004Sbellard /* unmap what we can */ 66653a5960aSpbrook if (real_start < real_end) { 667b76f21a7SLaurent Vivier if (reserved_va) { 66868a1c816SPaul Brook mmap_reserve(real_start, real_end - real_start); 66968a1c816SPaul Brook } else { 6704118a970Sj_mayer ret = munmap(g2h(real_start), real_end - real_start); 67154936004Sbellard } 67268a1c816SPaul Brook } 67354936004Sbellard 67477a8f1a5SAlexander Graf if (ret == 0) { 67554936004Sbellard page_set_flags(start, start + len, 0); 67635865339SPaolo Bonzini tb_invalidate_phys_range(start, start + len); 67777a8f1a5SAlexander Graf } 678c8a706feSpbrook mmap_unlock(); 679c8a706feSpbrook return ret; 68054936004Sbellard } 68154936004Sbellard 682992f48a0Sblueswir1 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, 683992f48a0Sblueswir1 abi_ulong new_size, unsigned long flags, 684992f48a0Sblueswir1 abi_ulong new_addr) 68554936004Sbellard { 68654936004Sbellard int prot; 687f19412a2Saurel32 void *host_addr; 68854936004Sbellard 689c8a706feSpbrook mmap_lock(); 690f19412a2Saurel32 69168a1c816SPaul Brook if (flags & MREMAP_FIXED) { 6923af72a4dSblueswir1 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), 6933af72a4dSblueswir1 old_size, new_size, 6943af72a4dSblueswir1 flags, 69568a1c816SPaul Brook g2h(new_addr)); 69668a1c816SPaul Brook 697b76f21a7SLaurent Vivier if (reserved_va && host_addr != MAP_FAILED) { 69868a1c816SPaul Brook /* If new and old addresses overlap then the above mremap will 69968a1c816SPaul Brook already have failed with EINVAL. */ 70068a1c816SPaul Brook mmap_reserve(old_addr, old_size); 70168a1c816SPaul Brook } 70268a1c816SPaul Brook } else if (flags & MREMAP_MAYMOVE) { 703f19412a2Saurel32 abi_ulong mmap_start; 704f19412a2Saurel32 705f19412a2Saurel32 mmap_start = mmap_find_vma(0, new_size); 706f19412a2Saurel32 707f19412a2Saurel32 if (mmap_start == -1) { 708f19412a2Saurel32 errno = ENOMEM; 709f19412a2Saurel32 host_addr = MAP_FAILED; 71068a1c816SPaul Brook } else { 7113af72a4dSblueswir1 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), 7123af72a4dSblueswir1 old_size, new_size, 7133af72a4dSblueswir1 flags | MREMAP_FIXED, 7143af72a4dSblueswir1 g2h(mmap_start)); 715b76f21a7SLaurent Vivier if (reserved_va) { 71668a1c816SPaul Brook mmap_reserve(old_addr, old_size); 71768a1c816SPaul Brook } 718c65ffe6dSamateur } 7193af72a4dSblueswir1 } else { 72068a1c816SPaul Brook int prot = 0; 721b76f21a7SLaurent Vivier if (reserved_va && old_size < new_size) { 72268a1c816SPaul Brook abi_ulong addr; 72368a1c816SPaul Brook for (addr = old_addr + old_size; 72468a1c816SPaul Brook addr < old_addr + new_size; 72568a1c816SPaul Brook addr++) { 72668a1c816SPaul Brook prot |= page_get_flags(addr); 72768a1c816SPaul Brook } 72868a1c816SPaul Brook } 72968a1c816SPaul Brook if (prot == 0) { 730f19412a2Saurel32 host_addr = mremap(g2h(old_addr), old_size, new_size, flags); 731b76f21a7SLaurent Vivier if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) { 73268a1c816SPaul Brook mmap_reserve(old_addr + old_size, new_size - old_size); 73368a1c816SPaul Brook } 73468a1c816SPaul Brook } else { 73568a1c816SPaul Brook errno = ENOMEM; 73668a1c816SPaul Brook host_addr = MAP_FAILED; 73768a1c816SPaul Brook } 738f19412a2Saurel32 /* Check if address fits target address space */ 739f19412a2Saurel32 if ((unsigned long)host_addr + new_size > (abi_ulong)-1) { 740f19412a2Saurel32 /* Revert mremap() changes */ 741f19412a2Saurel32 host_addr = mremap(g2h(old_addr), new_size, old_size, flags); 742f19412a2Saurel32 errno = ENOMEM; 743f19412a2Saurel32 host_addr = MAP_FAILED; 744f19412a2Saurel32 } 745f19412a2Saurel32 } 746f19412a2Saurel32 747f19412a2Saurel32 if (host_addr == MAP_FAILED) { 748c8a706feSpbrook new_addr = -1; 749c8a706feSpbrook } else { 750a5b85f79Sths new_addr = h2g(host_addr); 75154936004Sbellard prot = page_get_flags(old_addr); 75254936004Sbellard page_set_flags(old_addr, old_addr + old_size, 0); 75354936004Sbellard page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID); 754c8a706feSpbrook } 75535865339SPaolo Bonzini tb_invalidate_phys_range(new_addr, new_addr + new_size); 756c8a706feSpbrook mmap_unlock(); 75754936004Sbellard return new_addr; 75854936004Sbellard } 75954936004Sbellard 760992f48a0Sblueswir1 int target_msync(abi_ulong start, abi_ulong len, int flags) 76154936004Sbellard { 762992f48a0Sblueswir1 abi_ulong end; 76354936004Sbellard 76454936004Sbellard if (start & ~TARGET_PAGE_MASK) 76554936004Sbellard return -EINVAL; 76654936004Sbellard len = TARGET_PAGE_ALIGN(len); 76754936004Sbellard end = start + len; 768d418c81eSbellard if (end < start) 769d418c81eSbellard return -EINVAL; 770d418c81eSbellard if (end == start) 771d418c81eSbellard return 0; 77254936004Sbellard 77383fb7adfSbellard start &= qemu_host_page_mask; 77453a5960aSpbrook return msync(g2h(start), end - start, flags); 77554936004Sbellard } 776