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 */ 19d39594e9SPeter Maydell #include "qemu/osdep.h" 2054936004Sbellard 2154936004Sbellard #include "qemu.h" 2278f5bf1eSblueswir1 #include "qemu-common.h" 231652b974SPaolo Bonzini #include "translate-all.h" 2454936004Sbellard 2554936004Sbellard //#define DEBUG_MMAP 2654936004Sbellard 271e6eec8bSBlue Swirl static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; 28dfd3f85cSJuan Quintela static __thread int mmap_lock_count; 29c8a706feSpbrook 30c8a706feSpbrook void mmap_lock(void) 31c8a706feSpbrook { 32c8a706feSpbrook if (mmap_lock_count++ == 0) { 33c8a706feSpbrook pthread_mutex_lock(&mmap_mutex); 34c8a706feSpbrook } 35c8a706feSpbrook } 36c8a706feSpbrook 37c8a706feSpbrook void mmap_unlock(void) 38c8a706feSpbrook { 39c8a706feSpbrook if (--mmap_lock_count == 0) { 40c8a706feSpbrook pthread_mutex_unlock(&mmap_mutex); 41c8a706feSpbrook } 42c8a706feSpbrook } 43d5975363Spbrook 44301e40edSAlex Bennée bool have_mmap_lock(void) 45301e40edSAlex Bennée { 46301e40edSAlex Bennée return mmap_lock_count > 0 ? true : false; 47301e40edSAlex Bennée } 48301e40edSAlex Bennée 49d5975363Spbrook /* Grab lock to make sure things are in a consistent state after fork(). */ 50d5975363Spbrook void mmap_fork_start(void) 51d5975363Spbrook { 52d5975363Spbrook if (mmap_lock_count) 53d5975363Spbrook abort(); 54d5975363Spbrook pthread_mutex_lock(&mmap_mutex); 55d5975363Spbrook } 56d5975363Spbrook 57d5975363Spbrook void mmap_fork_end(int child) 58d5975363Spbrook { 59d5975363Spbrook if (child) 60d5975363Spbrook pthread_mutex_init(&mmap_mutex, NULL); 61d5975363Spbrook else 62d5975363Spbrook pthread_mutex_unlock(&mmap_mutex); 63d5975363Spbrook } 64c8a706feSpbrook 6553a5960aSpbrook /* NOTE: all the constants are the HOST ones, but addresses are target. */ 66992f48a0Sblueswir1 int target_mprotect(abi_ulong start, abi_ulong len, int prot) 6754936004Sbellard { 68992f48a0Sblueswir1 abi_ulong end, host_start, host_end, addr; 6954936004Sbellard int prot1, ret; 7054936004Sbellard 7154936004Sbellard #ifdef DEBUG_MMAP 720bf9e31aSBlue Swirl printf("mprotect: start=0x" TARGET_ABI_FMT_lx 730bf9e31aSBlue Swirl "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len, 7454936004Sbellard prot & PROT_READ ? 'r' : '-', 7554936004Sbellard prot & PROT_WRITE ? 'w' : '-', 7654936004Sbellard prot & PROT_EXEC ? 'x' : '-'); 7754936004Sbellard #endif 7854936004Sbellard 7954936004Sbellard if ((start & ~TARGET_PAGE_MASK) != 0) 8054936004Sbellard return -EINVAL; 8154936004Sbellard len = TARGET_PAGE_ALIGN(len); 8254936004Sbellard end = start + len; 8354936004Sbellard if (end < start) 8454936004Sbellard return -EINVAL; 85171cd1cdSbalrog prot &= PROT_READ | PROT_WRITE | PROT_EXEC; 8654936004Sbellard if (len == 0) 8754936004Sbellard return 0; 8854936004Sbellard 89c8a706feSpbrook mmap_lock(); 9083fb7adfSbellard host_start = start & qemu_host_page_mask; 9154936004Sbellard host_end = HOST_PAGE_ALIGN(end); 9254936004Sbellard if (start > host_start) { 9354936004Sbellard /* handle host page containing start */ 9454936004Sbellard prot1 = prot; 9554936004Sbellard for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) { 9654936004Sbellard prot1 |= page_get_flags(addr); 9754936004Sbellard } 9883fb7adfSbellard if (host_end == host_start + qemu_host_page_size) { 99d418c81eSbellard for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { 100d418c81eSbellard prot1 |= page_get_flags(addr); 101d418c81eSbellard } 102d418c81eSbellard end = host_end; 103d418c81eSbellard } 10453a5960aSpbrook ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS); 10554936004Sbellard if (ret != 0) 106c8a706feSpbrook goto error; 10783fb7adfSbellard host_start += qemu_host_page_size; 10854936004Sbellard } 10954936004Sbellard if (end < host_end) { 11054936004Sbellard prot1 = prot; 11154936004Sbellard for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { 11254936004Sbellard prot1 |= page_get_flags(addr); 11354936004Sbellard } 11453a5960aSpbrook ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size, 11554936004Sbellard prot1 & PAGE_BITS); 11654936004Sbellard if (ret != 0) 117c8a706feSpbrook goto error; 11883fb7adfSbellard host_end -= qemu_host_page_size; 11954936004Sbellard } 12054936004Sbellard 12154936004Sbellard /* handle the pages in the middle */ 12254936004Sbellard if (host_start < host_end) { 12353a5960aSpbrook ret = mprotect(g2h(host_start), host_end - host_start, prot); 12454936004Sbellard if (ret != 0) 125c8a706feSpbrook goto error; 12654936004Sbellard } 12754936004Sbellard page_set_flags(start, start + len, prot | PAGE_VALID); 128c8a706feSpbrook mmap_unlock(); 12954936004Sbellard return 0; 130c8a706feSpbrook error: 131c8a706feSpbrook mmap_unlock(); 132c8a706feSpbrook return ret; 13354936004Sbellard } 13454936004Sbellard 13554936004Sbellard /* map an incomplete host page */ 136992f48a0Sblueswir1 static int mmap_frag(abi_ulong real_start, 137992f48a0Sblueswir1 abi_ulong start, abi_ulong end, 138992f48a0Sblueswir1 int prot, int flags, int fd, abi_ulong offset) 13954936004Sbellard { 14080210bcdSths abi_ulong real_end, addr; 14153a5960aSpbrook void *host_start; 14254936004Sbellard int prot1, prot_new; 14354936004Sbellard 14453a5960aSpbrook real_end = real_start + qemu_host_page_size; 14553a5960aSpbrook host_start = g2h(real_start); 14654936004Sbellard 14754936004Sbellard /* get the protection of the target pages outside the mapping */ 14854936004Sbellard prot1 = 0; 14953a5960aSpbrook for(addr = real_start; addr < real_end; addr++) { 15054936004Sbellard if (addr < start || addr >= end) 15154936004Sbellard prot1 |= page_get_flags(addr); 15254936004Sbellard } 15354936004Sbellard 15454936004Sbellard if (prot1 == 0) { 15554936004Sbellard /* no page was there, so we allocate one */ 15680210bcdSths void *p = mmap(host_start, qemu_host_page_size, prot, 15754936004Sbellard flags | MAP_ANONYMOUS, -1, 0); 15880210bcdSths if (p == MAP_FAILED) 15980210bcdSths return -1; 16053a5960aSpbrook prot1 = prot; 16154936004Sbellard } 16254936004Sbellard prot1 &= PAGE_BITS; 16354936004Sbellard 16454936004Sbellard prot_new = prot | prot1; 16554936004Sbellard if (!(flags & MAP_ANONYMOUS)) { 16654936004Sbellard /* msync() won't work here, so we return an error if write is 16754936004Sbellard possible while it is a shared mapping */ 16854936004Sbellard if ((flags & MAP_TYPE) == MAP_SHARED && 16954936004Sbellard (prot & PROT_WRITE)) 170ee636500SJuan Quintela return -1; 17154936004Sbellard 17254936004Sbellard /* adjust protection to be able to read */ 17354936004Sbellard if (!(prot1 & PROT_WRITE)) 17453a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); 17554936004Sbellard 17654936004Sbellard /* read the corresponding file data */ 177fb7e378cSKirill A. Shutemov if (pread(fd, g2h(start), end - start, offset) == -1) 178fb7e378cSKirill A. Shutemov return -1; 17954936004Sbellard 18054936004Sbellard /* put final protection */ 18154936004Sbellard if (prot_new != (prot1 | PROT_WRITE)) 18253a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot_new); 18354936004Sbellard } else { 18454936004Sbellard if (prot_new != prot1) { 18553a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot_new); 18654936004Sbellard } 187e6deac9cSChen Gang if (prot_new & PROT_WRITE) { 188e6deac9cSChen Gang memset(g2h(start), 0, end - start); 189e6deac9cSChen Gang } 19054936004Sbellard } 19154936004Sbellard return 0; 19254936004Sbellard } 19354936004Sbellard 19414f24e14SRichard Henderson #if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64 19514f24e14SRichard Henderson # define TASK_UNMAPPED_BASE (1ul << 38) 196a03e2d42Sbellard #else 19714f24e14SRichard Henderson # define TASK_UNMAPPED_BASE 0x40000000 198a03e2d42Sbellard #endif 19959e9d91cSPeter Maydell abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; 200a03e2d42Sbellard 2010776590dSpbrook unsigned long last_brk; 2020776590dSpbrook 20368a1c816SPaul Brook /* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk 20468a1c816SPaul Brook of guest address space. */ 20568a1c816SPaul Brook static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) 20668a1c816SPaul Brook { 20768a1c816SPaul Brook abi_ulong addr; 20859e9d91cSPeter Maydell abi_ulong end_addr; 20968a1c816SPaul Brook int prot; 21068a1c816SPaul Brook int looped = 0; 21168a1c816SPaul Brook 212b76f21a7SLaurent Vivier if (size > reserved_va) { 21368a1c816SPaul Brook return (abi_ulong)-1; 21468a1c816SPaul Brook } 21568a1c816SPaul Brook 21659e9d91cSPeter Maydell size = HOST_PAGE_ALIGN(size); 21759e9d91cSPeter Maydell end_addr = start + size; 218b76f21a7SLaurent Vivier if (end_addr > reserved_va) { 219b76f21a7SLaurent Vivier end_addr = reserved_va; 22059e9d91cSPeter Maydell } 22159e9d91cSPeter Maydell addr = end_addr - qemu_host_page_size; 22259e9d91cSPeter Maydell 22359e9d91cSPeter Maydell while (1) { 22459e9d91cSPeter Maydell if (addr > end_addr) { 22568a1c816SPaul Brook if (looped) { 22668a1c816SPaul Brook return (abi_ulong)-1; 22768a1c816SPaul Brook } 228b76f21a7SLaurent Vivier end_addr = reserved_va; 22959e9d91cSPeter Maydell addr = end_addr - qemu_host_page_size; 23068a1c816SPaul Brook looped = 1; 23168a1c816SPaul Brook continue; 23268a1c816SPaul Brook } 23368a1c816SPaul Brook prot = page_get_flags(addr); 23468a1c816SPaul Brook if (prot) { 23559e9d91cSPeter Maydell end_addr = addr; 23668a1c816SPaul Brook } 23759e9d91cSPeter Maydell if (addr + size == end_addr) { 23859e9d91cSPeter Maydell break; 23968a1c816SPaul Brook } 24059e9d91cSPeter Maydell addr -= qemu_host_page_size; 24159e9d91cSPeter Maydell } 24259e9d91cSPeter Maydell 24359e9d91cSPeter Maydell if (start == mmap_next_start) { 24468a1c816SPaul Brook mmap_next_start = addr; 24559e9d91cSPeter Maydell } 24659e9d91cSPeter Maydell 24759e9d91cSPeter Maydell return addr; 24868a1c816SPaul Brook } 24968a1c816SPaul Brook 250fe3b4152SKirill A. Shutemov /* 251fe3b4152SKirill A. Shutemov * Find and reserve a free memory area of size 'size'. The search 252fe3b4152SKirill A. Shutemov * starts at 'start'. 253fe3b4152SKirill A. Shutemov * It must be called with mmap_lock() held. 254fe3b4152SKirill A. Shutemov * Return -1 if error. 255a03e2d42Sbellard */ 2569ad197d9SRiku Voipio abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) 257a03e2d42Sbellard { 25814f24e14SRichard Henderson void *ptr, *prev; 259fe3b4152SKirill A. Shutemov abi_ulong addr; 26014f24e14SRichard Henderson int wrapped, repeat; 261fe3b4152SKirill A. Shutemov 262fe3b4152SKirill A. Shutemov /* If 'start' == 0, then a default start address is used. */ 26314f24e14SRichard Henderson if (start == 0) { 264fe3b4152SKirill A. Shutemov start = mmap_next_start; 26514f24e14SRichard Henderson } else { 26614f24e14SRichard Henderson start &= qemu_host_page_mask; 26714f24e14SRichard Henderson } 26814f24e14SRichard Henderson 26914f24e14SRichard Henderson size = HOST_PAGE_ALIGN(size); 270fe3b4152SKirill A. Shutemov 271b76f21a7SLaurent Vivier if (reserved_va) { 27268a1c816SPaul Brook return mmap_find_vma_reserved(start, size); 27368a1c816SPaul Brook } 27468a1c816SPaul Brook 275a03e2d42Sbellard addr = start; 27614f24e14SRichard Henderson wrapped = repeat = 0; 27714f24e14SRichard Henderson prev = 0; 278fe3b4152SKirill A. Shutemov 27914f24e14SRichard Henderson for (;; prev = ptr) { 280fe3b4152SKirill A. Shutemov /* 281fe3b4152SKirill A. Shutemov * Reserve needed memory area to avoid a race. 282fe3b4152SKirill A. Shutemov * It should be discarded using: 283fe3b4152SKirill A. Shutemov * - mmap() with MAP_FIXED flag 284fe3b4152SKirill A. Shutemov * - mremap() with MREMAP_FIXED flag 285fe3b4152SKirill A. Shutemov * - shmat() with SHM_REMAP flag 286fe3b4152SKirill A. Shutemov */ 28714f24e14SRichard Henderson ptr = mmap(g2h(addr), size, PROT_NONE, 288fe3b4152SKirill A. Shutemov MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); 289fe3b4152SKirill A. Shutemov 290fe3b4152SKirill A. Shutemov /* ENOMEM, if host address space has no memory */ 29114f24e14SRichard Henderson if (ptr == MAP_FAILED) { 292a03e2d42Sbellard return (abi_ulong)-1; 293a03e2d42Sbellard } 294fe3b4152SKirill A. Shutemov 29514f24e14SRichard Henderson /* Count the number of sequential returns of the same address. 29614f24e14SRichard Henderson This is used to modify the search algorithm below. */ 29714f24e14SRichard Henderson repeat = (ptr == prev ? repeat + 1 : 0); 298fe3b4152SKirill A. Shutemov 29914f24e14SRichard Henderson if (h2g_valid(ptr + size - 1)) { 30014f24e14SRichard Henderson addr = h2g(ptr); 30114f24e14SRichard Henderson 30214f24e14SRichard Henderson if ((addr & ~TARGET_PAGE_MASK) == 0) { 30314f24e14SRichard Henderson /* Success. */ 30414f24e14SRichard Henderson if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) { 30514f24e14SRichard Henderson mmap_next_start = addr + size; 30614f24e14SRichard Henderson } 30714f24e14SRichard Henderson return addr; 30814f24e14SRichard Henderson } 30914f24e14SRichard Henderson 31014f24e14SRichard Henderson /* The address is not properly aligned for the target. */ 31114f24e14SRichard Henderson switch (repeat) { 31214f24e14SRichard Henderson case 0: 31314f24e14SRichard Henderson /* Assume the result that the kernel gave us is the 31414f24e14SRichard Henderson first with enough free space, so start again at the 31514f24e14SRichard Henderson next higher target page. */ 31614f24e14SRichard Henderson addr = TARGET_PAGE_ALIGN(addr); 31714f24e14SRichard Henderson break; 31814f24e14SRichard Henderson case 1: 31914f24e14SRichard Henderson /* Sometimes the kernel decides to perform the allocation 32014f24e14SRichard Henderson at the top end of memory instead. */ 32114f24e14SRichard Henderson addr &= TARGET_PAGE_MASK; 32214f24e14SRichard Henderson break; 32314f24e14SRichard Henderson case 2: 32414f24e14SRichard Henderson /* Start over at low memory. */ 32514f24e14SRichard Henderson addr = 0; 32614f24e14SRichard Henderson break; 32714f24e14SRichard Henderson default: 32814f24e14SRichard Henderson /* Fail. This unaligned block must the last. */ 32914f24e14SRichard Henderson addr = -1; 33014f24e14SRichard Henderson break; 33114f24e14SRichard Henderson } 33214f24e14SRichard Henderson } else { 33314f24e14SRichard Henderson /* Since the result the kernel gave didn't fit, start 33414f24e14SRichard Henderson again at low memory. If any repetition, fail. */ 33514f24e14SRichard Henderson addr = (repeat ? -1 : 0); 33614f24e14SRichard Henderson } 33714f24e14SRichard Henderson 33814f24e14SRichard Henderson /* Unmap and try again. */ 33914f24e14SRichard Henderson munmap(ptr, size); 34014f24e14SRichard Henderson 34114f24e14SRichard Henderson /* ENOMEM if we checked the whole of the target address space. */ 342d0b3e4f5SBlue Swirl if (addr == (abi_ulong)-1) { 34314f24e14SRichard Henderson return (abi_ulong)-1; 34414f24e14SRichard Henderson } else if (addr == 0) { 34514f24e14SRichard Henderson if (wrapped) { 34614f24e14SRichard Henderson return (abi_ulong)-1; 34714f24e14SRichard Henderson } 34814f24e14SRichard Henderson wrapped = 1; 34914f24e14SRichard Henderson /* Don't actually use 0 when wrapping, instead indicate 3508186e783SStefan Weil that we'd truly like an allocation in low memory. */ 35114f24e14SRichard Henderson addr = (mmap_min_addr > TARGET_PAGE_SIZE 35214f24e14SRichard Henderson ? TARGET_PAGE_ALIGN(mmap_min_addr) 35314f24e14SRichard Henderson : TARGET_PAGE_SIZE); 35414f24e14SRichard Henderson } else if (wrapped && addr >= start) { 35514f24e14SRichard Henderson return (abi_ulong)-1; 35614f24e14SRichard Henderson } 35714f24e14SRichard Henderson } 358a03e2d42Sbellard } 359a03e2d42Sbellard 36054936004Sbellard /* NOTE: all the constants are the HOST ones */ 361992f48a0Sblueswir1 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, 362992f48a0Sblueswir1 int flags, int fd, abi_ulong offset) 36354936004Sbellard { 364992f48a0Sblueswir1 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; 36554936004Sbellard 366c8a706feSpbrook mmap_lock(); 36754936004Sbellard #ifdef DEBUG_MMAP 36854936004Sbellard { 3690bf9e31aSBlue Swirl printf("mmap: start=0x" TARGET_ABI_FMT_lx 3700bf9e31aSBlue Swirl " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=", 37154936004Sbellard start, len, 37254936004Sbellard prot & PROT_READ ? 'r' : '-', 37354936004Sbellard prot & PROT_WRITE ? 'w' : '-', 37454936004Sbellard prot & PROT_EXEC ? 'x' : '-'); 37554936004Sbellard if (flags & MAP_FIXED) 37654936004Sbellard printf("MAP_FIXED "); 37754936004Sbellard if (flags & MAP_ANONYMOUS) 37854936004Sbellard printf("MAP_ANON "); 37954936004Sbellard switch(flags & MAP_TYPE) { 38054936004Sbellard case MAP_PRIVATE: 38154936004Sbellard printf("MAP_PRIVATE "); 38254936004Sbellard break; 38354936004Sbellard case MAP_SHARED: 38454936004Sbellard printf("MAP_SHARED "); 38554936004Sbellard break; 38654936004Sbellard default: 38754936004Sbellard printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); 38854936004Sbellard break; 38954936004Sbellard } 3900bf9e31aSBlue Swirl printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset); 39154936004Sbellard } 39254936004Sbellard #endif 39354936004Sbellard 394e89f07d3Spbrook if (offset & ~TARGET_PAGE_MASK) { 395e89f07d3Spbrook errno = EINVAL; 396c8a706feSpbrook goto fail; 397e89f07d3Spbrook } 39854936004Sbellard 39954936004Sbellard len = TARGET_PAGE_ALIGN(len); 40054936004Sbellard if (len == 0) 401c8a706feSpbrook goto the_end; 40253a5960aSpbrook real_start = start & qemu_host_page_mask; 403a5e7ee46SRichard Henderson host_offset = offset & qemu_host_page_mask; 404a5e7ee46SRichard Henderson 405a5e7ee46SRichard Henderson /* If the user is asking for the kernel to find a location, do that 406a5e7ee46SRichard Henderson before we truncate the length for mapping files below. */ 407a5e7ee46SRichard Henderson if (!(flags & MAP_FIXED)) { 408a5e7ee46SRichard Henderson host_len = len + offset - host_offset; 409a5e7ee46SRichard Henderson host_len = HOST_PAGE_ALIGN(host_len); 410a5e7ee46SRichard Henderson start = mmap_find_vma(real_start, host_len); 411a5e7ee46SRichard Henderson if (start == (abi_ulong)-1) { 412a5e7ee46SRichard Henderson errno = ENOMEM; 413a5e7ee46SRichard Henderson goto fail; 414a5e7ee46SRichard Henderson } 415a5e7ee46SRichard Henderson } 41654936004Sbellard 41754c5a2aeSedgar_igl /* When mapping files into a memory area larger than the file, accesses 41854c5a2aeSedgar_igl to pages beyond the file size will cause a SIGBUS. 41954c5a2aeSedgar_igl 42054c5a2aeSedgar_igl For example, if mmaping a file of 100 bytes on a host with 4K pages 42154c5a2aeSedgar_igl emulating a target with 8K pages, the target expects to be able to 42254c5a2aeSedgar_igl access the first 8K. But the host will trap us on any access beyond 42354c5a2aeSedgar_igl 4K. 42454c5a2aeSedgar_igl 42554c5a2aeSedgar_igl When emulating a target with a larger page-size than the hosts, we 42654c5a2aeSedgar_igl may need to truncate file maps at EOF and add extra anonymous pages 42754c5a2aeSedgar_igl up to the targets page boundary. */ 42854c5a2aeSedgar_igl 429*35f2fd04SMarc-André Lureau if ((qemu_real_host_page_size < qemu_host_page_size) && 430*35f2fd04SMarc-André Lureau !(flags & MAP_ANONYMOUS)) { 43154c5a2aeSedgar_igl struct stat sb; 43254c5a2aeSedgar_igl 43354c5a2aeSedgar_igl if (fstat (fd, &sb) == -1) 43454c5a2aeSedgar_igl goto fail; 43554c5a2aeSedgar_igl 43654c5a2aeSedgar_igl /* Are we trying to create a map beyond EOF?. */ 43754c5a2aeSedgar_igl if (offset + len > sb.st_size) { 43854c5a2aeSedgar_igl /* If so, truncate the file map at eof aligned with 43954c5a2aeSedgar_igl the hosts real pagesize. Additional anonymous maps 44054c5a2aeSedgar_igl will be created beyond EOF. */ 4410c2d70c4SPaolo Bonzini len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset); 44254c5a2aeSedgar_igl } 44354c5a2aeSedgar_igl } 44454c5a2aeSedgar_igl 44554936004Sbellard if (!(flags & MAP_FIXED)) { 446a5e7ee46SRichard Henderson unsigned long host_start; 447a03e2d42Sbellard void *p; 448a5e7ee46SRichard Henderson 44954936004Sbellard host_len = len + offset - host_offset; 450a03e2d42Sbellard host_len = HOST_PAGE_ALIGN(host_len); 451a5e7ee46SRichard Henderson 452a03e2d42Sbellard /* Note: we prefer to control the mapping address. It is 453a03e2d42Sbellard especially important if qemu_host_page_size > 454a03e2d42Sbellard qemu_real_host_page_size */ 455a5e7ee46SRichard Henderson p = mmap(g2h(start), host_len, prot, 456a5e7ee46SRichard Henderson flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0); 45780210bcdSths if (p == MAP_FAILED) 458c8a706feSpbrook goto fail; 45954936004Sbellard /* update start so that it points to the file position at 'offset' */ 46080210bcdSths host_start = (unsigned long)p; 46154c5a2aeSedgar_igl if (!(flags & MAP_ANONYMOUS)) { 462a5e7ee46SRichard Henderson p = mmap(g2h(start), len, prot, 46354c5a2aeSedgar_igl flags | MAP_FIXED, fd, host_offset); 4648384274eSJürg Billeter if (p == MAP_FAILED) { 4658384274eSJürg Billeter munmap(g2h(start), host_len); 4668384274eSJürg Billeter goto fail; 4678384274eSJürg Billeter } 46853a5960aSpbrook host_start += offset - host_offset; 46954c5a2aeSedgar_igl } 47053a5960aSpbrook start = h2g(host_start); 471a03e2d42Sbellard } else { 472e89f07d3Spbrook if (start & ~TARGET_PAGE_MASK) { 473e89f07d3Spbrook errno = EINVAL; 474c8a706feSpbrook goto fail; 475e89f07d3Spbrook } 47654936004Sbellard end = start + len; 47753a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 47854936004Sbellard 47945bc1f52Saurel32 /* 48045bc1f52Saurel32 * Test if requested memory area fits target address space 48145bc1f52Saurel32 * It can fail only on 64-bit host with 32-bit target. 48245bc1f52Saurel32 * On any other target/host host mmap() handles this error correctly. 48345bc1f52Saurel32 */ 48445bc1f52Saurel32 if ((unsigned long)start + len - 1 > (abi_ulong) -1) { 48545bc1f52Saurel32 errno = EINVAL; 48645bc1f52Saurel32 goto fail; 48745bc1f52Saurel32 } 48845bc1f52Saurel32 48954936004Sbellard /* worst case: we cannot map the file because the offset is not 49054936004Sbellard aligned, so we read it */ 49154936004Sbellard if (!(flags & MAP_ANONYMOUS) && 49283fb7adfSbellard (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { 49354936004Sbellard /* msync() won't work here, so we return an error if write is 49454936004Sbellard possible while it is a shared mapping */ 49554936004Sbellard if ((flags & MAP_TYPE) == MAP_SHARED && 496e89f07d3Spbrook (prot & PROT_WRITE)) { 497e89f07d3Spbrook errno = EINVAL; 498c8a706feSpbrook goto fail; 499e89f07d3Spbrook } 50054936004Sbellard retaddr = target_mmap(start, len, prot | PROT_WRITE, 50154936004Sbellard MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 50254936004Sbellard -1, 0); 50354936004Sbellard if (retaddr == -1) 504c8a706feSpbrook goto fail; 505fb7e378cSKirill A. Shutemov if (pread(fd, g2h(start), len, offset) == -1) 506fb7e378cSKirill A. Shutemov goto fail; 50754936004Sbellard if (!(prot & PROT_WRITE)) { 50854936004Sbellard ret = target_mprotect(start, len, prot); 50986abac06SPaolo Bonzini assert(ret == 0); 51054936004Sbellard } 51154936004Sbellard goto the_end; 51254936004Sbellard } 51354936004Sbellard 51454936004Sbellard /* handle the start of the mapping */ 51553a5960aSpbrook if (start > real_start) { 51653a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 51754936004Sbellard /* one single host page */ 51853a5960aSpbrook ret = mmap_frag(real_start, start, end, 51954936004Sbellard prot, flags, fd, offset); 52054936004Sbellard if (ret == -1) 521c8a706feSpbrook goto fail; 52254936004Sbellard goto the_end1; 52354936004Sbellard } 52453a5960aSpbrook ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, 52554936004Sbellard prot, flags, fd, offset); 52654936004Sbellard if (ret == -1) 527c8a706feSpbrook goto fail; 52853a5960aSpbrook real_start += qemu_host_page_size; 52954936004Sbellard } 53054936004Sbellard /* handle the end of the mapping */ 53153a5960aSpbrook if (end < real_end) { 53253a5960aSpbrook ret = mmap_frag(real_end - qemu_host_page_size, 533530c0032SChen Gang real_end - qemu_host_page_size, end, 53454936004Sbellard prot, flags, fd, 53553a5960aSpbrook offset + real_end - qemu_host_page_size - start); 53654936004Sbellard if (ret == -1) 537c8a706feSpbrook goto fail; 53853a5960aSpbrook real_end -= qemu_host_page_size; 53954936004Sbellard } 54054936004Sbellard 54154936004Sbellard /* map the middle (easier) */ 54253a5960aSpbrook if (real_start < real_end) { 54380210bcdSths void *p; 5444a585ccbSbellard unsigned long offset1; 5454a585ccbSbellard if (flags & MAP_ANONYMOUS) 5464a585ccbSbellard offset1 = 0; 5474a585ccbSbellard else 54853a5960aSpbrook offset1 = offset + real_start - start; 54980210bcdSths p = mmap(g2h(real_start), real_end - real_start, 5504a585ccbSbellard prot, flags, fd, offset1); 55180210bcdSths if (p == MAP_FAILED) 552c8a706feSpbrook goto fail; 55354936004Sbellard } 554a03e2d42Sbellard } 55554936004Sbellard the_end1: 55654936004Sbellard page_set_flags(start, start + len, prot | PAGE_VALID); 55754936004Sbellard the_end: 55854936004Sbellard #ifdef DEBUG_MMAP 5590bf9e31aSBlue Swirl printf("ret=0x" TARGET_ABI_FMT_lx "\n", start); 56054936004Sbellard page_dump(stdout); 56154936004Sbellard printf("\n"); 56254936004Sbellard #endif 56335865339SPaolo Bonzini tb_invalidate_phys_range(start, start + len); 564c8a706feSpbrook mmap_unlock(); 56554936004Sbellard return start; 566c8a706feSpbrook fail: 567c8a706feSpbrook mmap_unlock(); 568c8a706feSpbrook return -1; 56954936004Sbellard } 57054936004Sbellard 57168a1c816SPaul Brook static void mmap_reserve(abi_ulong start, abi_ulong size) 57268a1c816SPaul Brook { 57368a1c816SPaul Brook abi_ulong real_start; 57468a1c816SPaul Brook abi_ulong real_end; 57568a1c816SPaul Brook abi_ulong addr; 57668a1c816SPaul Brook abi_ulong end; 57768a1c816SPaul Brook int prot; 57868a1c816SPaul Brook 57968a1c816SPaul Brook real_start = start & qemu_host_page_mask; 58068a1c816SPaul Brook real_end = HOST_PAGE_ALIGN(start + size); 58168a1c816SPaul Brook end = start + size; 58268a1c816SPaul Brook if (start > real_start) { 58368a1c816SPaul Brook /* handle host page containing start */ 58468a1c816SPaul Brook prot = 0; 58568a1c816SPaul Brook for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { 58668a1c816SPaul Brook prot |= page_get_flags(addr); 58768a1c816SPaul Brook } 58868a1c816SPaul Brook if (real_end == real_start + qemu_host_page_size) { 58968a1c816SPaul Brook for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 59068a1c816SPaul Brook prot |= page_get_flags(addr); 59168a1c816SPaul Brook } 59268a1c816SPaul Brook end = real_end; 59368a1c816SPaul Brook } 59468a1c816SPaul Brook if (prot != 0) 59568a1c816SPaul Brook real_start += qemu_host_page_size; 59668a1c816SPaul Brook } 59768a1c816SPaul Brook if (end < real_end) { 59868a1c816SPaul Brook prot = 0; 59968a1c816SPaul Brook for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 60068a1c816SPaul Brook prot |= page_get_flags(addr); 60168a1c816SPaul Brook } 60268a1c816SPaul Brook if (prot != 0) 60368a1c816SPaul Brook real_end -= qemu_host_page_size; 60468a1c816SPaul Brook } 60568a1c816SPaul Brook if (real_start != real_end) { 60668a1c816SPaul Brook mmap(g2h(real_start), real_end - real_start, PROT_NONE, 60768a1c816SPaul Brook MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, 60868a1c816SPaul Brook -1, 0); 60968a1c816SPaul Brook } 61068a1c816SPaul Brook } 61168a1c816SPaul Brook 612992f48a0Sblueswir1 int target_munmap(abi_ulong start, abi_ulong len) 61354936004Sbellard { 614992f48a0Sblueswir1 abi_ulong end, real_start, real_end, addr; 61554936004Sbellard int prot, ret; 61654936004Sbellard 61754936004Sbellard #ifdef DEBUG_MMAP 6180bf9e31aSBlue Swirl printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x" 6190bf9e31aSBlue Swirl TARGET_ABI_FMT_lx "\n", 6200bf9e31aSBlue Swirl start, len); 62154936004Sbellard #endif 62254936004Sbellard if (start & ~TARGET_PAGE_MASK) 62354936004Sbellard return -EINVAL; 62454936004Sbellard len = TARGET_PAGE_ALIGN(len); 62554936004Sbellard if (len == 0) 62654936004Sbellard return -EINVAL; 627c8a706feSpbrook mmap_lock(); 62854936004Sbellard end = start + len; 62953a5960aSpbrook real_start = start & qemu_host_page_mask; 63053a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 63154936004Sbellard 63253a5960aSpbrook if (start > real_start) { 63354936004Sbellard /* handle host page containing start */ 63454936004Sbellard prot = 0; 63553a5960aSpbrook for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { 63654936004Sbellard prot |= page_get_flags(addr); 63754936004Sbellard } 63853a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 63953a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 640d418c81eSbellard prot |= page_get_flags(addr); 641d418c81eSbellard } 64253a5960aSpbrook end = real_end; 643d418c81eSbellard } 64454936004Sbellard if (prot != 0) 64553a5960aSpbrook real_start += qemu_host_page_size; 64654936004Sbellard } 64753a5960aSpbrook if (end < real_end) { 64854936004Sbellard prot = 0; 64953a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 65054936004Sbellard prot |= page_get_flags(addr); 65154936004Sbellard } 65254936004Sbellard if (prot != 0) 65353a5960aSpbrook real_end -= qemu_host_page_size; 65454936004Sbellard } 65554936004Sbellard 656c8a706feSpbrook ret = 0; 65754936004Sbellard /* unmap what we can */ 65853a5960aSpbrook if (real_start < real_end) { 659b76f21a7SLaurent Vivier if (reserved_va) { 66068a1c816SPaul Brook mmap_reserve(real_start, real_end - real_start); 66168a1c816SPaul Brook } else { 6624118a970Sj_mayer ret = munmap(g2h(real_start), real_end - real_start); 66354936004Sbellard } 66468a1c816SPaul Brook } 66554936004Sbellard 66677a8f1a5SAlexander Graf if (ret == 0) { 66754936004Sbellard page_set_flags(start, start + len, 0); 66835865339SPaolo Bonzini tb_invalidate_phys_range(start, start + len); 66977a8f1a5SAlexander Graf } 670c8a706feSpbrook mmap_unlock(); 671c8a706feSpbrook return ret; 67254936004Sbellard } 67354936004Sbellard 674992f48a0Sblueswir1 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, 675992f48a0Sblueswir1 abi_ulong new_size, unsigned long flags, 676992f48a0Sblueswir1 abi_ulong new_addr) 67754936004Sbellard { 67854936004Sbellard int prot; 679f19412a2Saurel32 void *host_addr; 68054936004Sbellard 681c8a706feSpbrook mmap_lock(); 682f19412a2Saurel32 68368a1c816SPaul Brook if (flags & MREMAP_FIXED) { 68452956a9bSFelix Janda host_addr = mremap(g2h(old_addr), old_size, new_size, 68552956a9bSFelix Janda flags, g2h(new_addr)); 68668a1c816SPaul Brook 687b76f21a7SLaurent Vivier if (reserved_va && host_addr != MAP_FAILED) { 68868a1c816SPaul Brook /* If new and old addresses overlap then the above mremap will 68968a1c816SPaul Brook already have failed with EINVAL. */ 69068a1c816SPaul Brook mmap_reserve(old_addr, old_size); 69168a1c816SPaul Brook } 69268a1c816SPaul Brook } else if (flags & MREMAP_MAYMOVE) { 693f19412a2Saurel32 abi_ulong mmap_start; 694f19412a2Saurel32 695f19412a2Saurel32 mmap_start = mmap_find_vma(0, new_size); 696f19412a2Saurel32 697f19412a2Saurel32 if (mmap_start == -1) { 698f19412a2Saurel32 errno = ENOMEM; 699f19412a2Saurel32 host_addr = MAP_FAILED; 70068a1c816SPaul Brook } else { 70152956a9bSFelix Janda host_addr = mremap(g2h(old_addr), old_size, new_size, 70252956a9bSFelix Janda flags | MREMAP_FIXED, g2h(mmap_start)); 703b76f21a7SLaurent Vivier if (reserved_va) { 70468a1c816SPaul Brook mmap_reserve(old_addr, old_size); 70568a1c816SPaul Brook } 706c65ffe6dSamateur } 7073af72a4dSblueswir1 } else { 70868a1c816SPaul Brook int prot = 0; 709b76f21a7SLaurent Vivier if (reserved_va && old_size < new_size) { 71068a1c816SPaul Brook abi_ulong addr; 71168a1c816SPaul Brook for (addr = old_addr + old_size; 71268a1c816SPaul Brook addr < old_addr + new_size; 71368a1c816SPaul Brook addr++) { 71468a1c816SPaul Brook prot |= page_get_flags(addr); 71568a1c816SPaul Brook } 71668a1c816SPaul Brook } 71768a1c816SPaul Brook if (prot == 0) { 718f19412a2Saurel32 host_addr = mremap(g2h(old_addr), old_size, new_size, flags); 719b76f21a7SLaurent Vivier if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) { 72068a1c816SPaul Brook mmap_reserve(old_addr + old_size, new_size - old_size); 72168a1c816SPaul Brook } 72268a1c816SPaul Brook } else { 72368a1c816SPaul Brook errno = ENOMEM; 72468a1c816SPaul Brook host_addr = MAP_FAILED; 72568a1c816SPaul Brook } 726f19412a2Saurel32 /* Check if address fits target address space */ 727f19412a2Saurel32 if ((unsigned long)host_addr + new_size > (abi_ulong)-1) { 728f19412a2Saurel32 /* Revert mremap() changes */ 729f19412a2Saurel32 host_addr = mremap(g2h(old_addr), new_size, old_size, flags); 730f19412a2Saurel32 errno = ENOMEM; 731f19412a2Saurel32 host_addr = MAP_FAILED; 732f19412a2Saurel32 } 733f19412a2Saurel32 } 734f19412a2Saurel32 735f19412a2Saurel32 if (host_addr == MAP_FAILED) { 736c8a706feSpbrook new_addr = -1; 737c8a706feSpbrook } else { 738a5b85f79Sths new_addr = h2g(host_addr); 73954936004Sbellard prot = page_get_flags(old_addr); 74054936004Sbellard page_set_flags(old_addr, old_addr + old_size, 0); 74154936004Sbellard page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID); 742c8a706feSpbrook } 74335865339SPaolo Bonzini tb_invalidate_phys_range(new_addr, new_addr + new_size); 744c8a706feSpbrook mmap_unlock(); 74554936004Sbellard return new_addr; 74654936004Sbellard } 74754936004Sbellard 748992f48a0Sblueswir1 int target_msync(abi_ulong start, abi_ulong len, int flags) 74954936004Sbellard { 750992f48a0Sblueswir1 abi_ulong end; 75154936004Sbellard 75254936004Sbellard if (start & ~TARGET_PAGE_MASK) 75354936004Sbellard return -EINVAL; 75454936004Sbellard len = TARGET_PAGE_ALIGN(len); 75554936004Sbellard end = start + len; 756d418c81eSbellard if (end < start) 757d418c81eSbellard return -EINVAL; 758d418c81eSbellard if (end == start) 759d418c81eSbellard return 0; 76054936004Sbellard 76183fb7adfSbellard start &= qemu_host_page_mask; 76253a5960aSpbrook return msync(g2h(start), end - start, flags); 76354936004Sbellard } 764