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 /* just update the protection */ 19054936004Sbellard if (prot_new != prot1) { 19153a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot_new); 19254936004Sbellard } 19354936004Sbellard } 19454936004Sbellard return 0; 19554936004Sbellard } 19654936004Sbellard 19714f24e14SRichard Henderson #if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64 19814f24e14SRichard Henderson # define TASK_UNMAPPED_BASE (1ul << 38) 19914f24e14SRichard Henderson #elif defined(__CYGWIN__) 200a03e2d42Sbellard /* Cygwin doesn't have a whole lot of address space. */ 20114f24e14SRichard Henderson # define TASK_UNMAPPED_BASE 0x18000000 202a03e2d42Sbellard #else 20314f24e14SRichard Henderson # define TASK_UNMAPPED_BASE 0x40000000 204a03e2d42Sbellard #endif 20559e9d91cSPeter Maydell abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; 206a03e2d42Sbellard 2070776590dSpbrook unsigned long last_brk; 2080776590dSpbrook 2094e655712SPeter Maydell #ifdef CONFIG_USE_GUEST_BASE 21068a1c816SPaul Brook /* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk 21168a1c816SPaul Brook of guest address space. */ 21268a1c816SPaul Brook static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) 21368a1c816SPaul Brook { 21468a1c816SPaul Brook abi_ulong addr; 21559e9d91cSPeter Maydell abi_ulong end_addr; 21668a1c816SPaul Brook int prot; 21768a1c816SPaul Brook int looped = 0; 21868a1c816SPaul Brook 21918e9ea8aSAurelien Jarno if (size > RESERVED_VA) { 22068a1c816SPaul Brook return (abi_ulong)-1; 22168a1c816SPaul Brook } 22268a1c816SPaul Brook 22359e9d91cSPeter Maydell size = HOST_PAGE_ALIGN(size); 22459e9d91cSPeter Maydell end_addr = start + size; 22559e9d91cSPeter Maydell if (end_addr > RESERVED_VA) { 22659e9d91cSPeter Maydell end_addr = RESERVED_VA; 22759e9d91cSPeter Maydell } 22859e9d91cSPeter Maydell addr = end_addr - qemu_host_page_size; 22959e9d91cSPeter Maydell 23059e9d91cSPeter Maydell while (1) { 23159e9d91cSPeter Maydell if (addr > end_addr) { 23268a1c816SPaul Brook if (looped) { 23368a1c816SPaul Brook return (abi_ulong)-1; 23468a1c816SPaul Brook } 23559e9d91cSPeter Maydell end_addr = RESERVED_VA; 23659e9d91cSPeter Maydell addr = end_addr - qemu_host_page_size; 23768a1c816SPaul Brook looped = 1; 23868a1c816SPaul Brook continue; 23968a1c816SPaul Brook } 24068a1c816SPaul Brook prot = page_get_flags(addr); 24168a1c816SPaul Brook if (prot) { 24259e9d91cSPeter Maydell end_addr = addr; 24368a1c816SPaul Brook } 24459e9d91cSPeter Maydell if (addr + size == end_addr) { 24559e9d91cSPeter Maydell break; 24668a1c816SPaul Brook } 24759e9d91cSPeter Maydell addr -= qemu_host_page_size; 24859e9d91cSPeter Maydell } 24959e9d91cSPeter Maydell 25059e9d91cSPeter Maydell if (start == mmap_next_start) { 25168a1c816SPaul Brook mmap_next_start = addr; 25259e9d91cSPeter Maydell } 25359e9d91cSPeter Maydell 25459e9d91cSPeter Maydell return addr; 25568a1c816SPaul Brook } 2564e655712SPeter Maydell #endif 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 2794e655712SPeter Maydell #ifdef CONFIG_USE_GUEST_BASE 28018e9ea8aSAurelien Jarno if (RESERVED_VA) { 28168a1c816SPaul Brook return mmap_find_vma_reserved(start, size); 28268a1c816SPaul Brook } 2834e655712SPeter Maydell #endif 28468a1c816SPaul Brook 285a03e2d42Sbellard addr = start; 28614f24e14SRichard Henderson wrapped = repeat = 0; 28714f24e14SRichard Henderson prev = 0; 288fe3b4152SKirill A. Shutemov 28914f24e14SRichard Henderson for (;; prev = ptr) { 290fe3b4152SKirill A. Shutemov /* 291fe3b4152SKirill A. Shutemov * Reserve needed memory area to avoid a race. 292fe3b4152SKirill A. Shutemov * It should be discarded using: 293fe3b4152SKirill A. Shutemov * - mmap() with MAP_FIXED flag 294fe3b4152SKirill A. Shutemov * - mremap() with MREMAP_FIXED flag 295fe3b4152SKirill A. Shutemov * - shmat() with SHM_REMAP flag 296fe3b4152SKirill A. Shutemov */ 29714f24e14SRichard Henderson ptr = mmap(g2h(addr), size, PROT_NONE, 298fe3b4152SKirill A. Shutemov MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); 299fe3b4152SKirill A. Shutemov 300fe3b4152SKirill A. Shutemov /* ENOMEM, if host address space has no memory */ 30114f24e14SRichard Henderson if (ptr == MAP_FAILED) { 302a03e2d42Sbellard return (abi_ulong)-1; 303a03e2d42Sbellard } 304fe3b4152SKirill A. Shutemov 30514f24e14SRichard Henderson /* Count the number of sequential returns of the same address. 30614f24e14SRichard Henderson This is used to modify the search algorithm below. */ 30714f24e14SRichard Henderson repeat = (ptr == prev ? repeat + 1 : 0); 308fe3b4152SKirill A. Shutemov 30914f24e14SRichard Henderson if (h2g_valid(ptr + size - 1)) { 31014f24e14SRichard Henderson addr = h2g(ptr); 31114f24e14SRichard Henderson 31214f24e14SRichard Henderson if ((addr & ~TARGET_PAGE_MASK) == 0) { 31314f24e14SRichard Henderson /* Success. */ 31414f24e14SRichard Henderson if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) { 31514f24e14SRichard Henderson mmap_next_start = addr + size; 31614f24e14SRichard Henderson } 31714f24e14SRichard Henderson return addr; 31814f24e14SRichard Henderson } 31914f24e14SRichard Henderson 32014f24e14SRichard Henderson /* The address is not properly aligned for the target. */ 32114f24e14SRichard Henderson switch (repeat) { 32214f24e14SRichard Henderson case 0: 32314f24e14SRichard Henderson /* Assume the result that the kernel gave us is the 32414f24e14SRichard Henderson first with enough free space, so start again at the 32514f24e14SRichard Henderson next higher target page. */ 32614f24e14SRichard Henderson addr = TARGET_PAGE_ALIGN(addr); 32714f24e14SRichard Henderson break; 32814f24e14SRichard Henderson case 1: 32914f24e14SRichard Henderson /* Sometimes the kernel decides to perform the allocation 33014f24e14SRichard Henderson at the top end of memory instead. */ 33114f24e14SRichard Henderson addr &= TARGET_PAGE_MASK; 33214f24e14SRichard Henderson break; 33314f24e14SRichard Henderson case 2: 33414f24e14SRichard Henderson /* Start over at low memory. */ 33514f24e14SRichard Henderson addr = 0; 33614f24e14SRichard Henderson break; 33714f24e14SRichard Henderson default: 33814f24e14SRichard Henderson /* Fail. This unaligned block must the last. */ 33914f24e14SRichard Henderson addr = -1; 34014f24e14SRichard Henderson break; 34114f24e14SRichard Henderson } 34214f24e14SRichard Henderson } else { 34314f24e14SRichard Henderson /* Since the result the kernel gave didn't fit, start 34414f24e14SRichard Henderson again at low memory. If any repetition, fail. */ 34514f24e14SRichard Henderson addr = (repeat ? -1 : 0); 34614f24e14SRichard Henderson } 34714f24e14SRichard Henderson 34814f24e14SRichard Henderson /* Unmap and try again. */ 34914f24e14SRichard Henderson munmap(ptr, size); 35014f24e14SRichard Henderson 35114f24e14SRichard Henderson /* ENOMEM if we checked the whole of the target address space. */ 352d0b3e4f5SBlue Swirl if (addr == (abi_ulong)-1) { 35314f24e14SRichard Henderson return (abi_ulong)-1; 35414f24e14SRichard Henderson } else if (addr == 0) { 35514f24e14SRichard Henderson if (wrapped) { 35614f24e14SRichard Henderson return (abi_ulong)-1; 35714f24e14SRichard Henderson } 35814f24e14SRichard Henderson wrapped = 1; 35914f24e14SRichard Henderson /* Don't actually use 0 when wrapping, instead indicate 3608186e783SStefan Weil that we'd truly like an allocation in low memory. */ 36114f24e14SRichard Henderson addr = (mmap_min_addr > TARGET_PAGE_SIZE 36214f24e14SRichard Henderson ? TARGET_PAGE_ALIGN(mmap_min_addr) 36314f24e14SRichard Henderson : TARGET_PAGE_SIZE); 36414f24e14SRichard Henderson } else if (wrapped && addr >= start) { 36514f24e14SRichard Henderson return (abi_ulong)-1; 36614f24e14SRichard Henderson } 36714f24e14SRichard Henderson } 368a03e2d42Sbellard } 369a03e2d42Sbellard 37054936004Sbellard /* NOTE: all the constants are the HOST ones */ 371992f48a0Sblueswir1 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, 372992f48a0Sblueswir1 int flags, int fd, abi_ulong offset) 37354936004Sbellard { 374992f48a0Sblueswir1 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; 37554936004Sbellard 376c8a706feSpbrook mmap_lock(); 37754936004Sbellard #ifdef DEBUG_MMAP 37854936004Sbellard { 3790bf9e31aSBlue Swirl printf("mmap: start=0x" TARGET_ABI_FMT_lx 3800bf9e31aSBlue Swirl " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=", 38154936004Sbellard start, len, 38254936004Sbellard prot & PROT_READ ? 'r' : '-', 38354936004Sbellard prot & PROT_WRITE ? 'w' : '-', 38454936004Sbellard prot & PROT_EXEC ? 'x' : '-'); 38554936004Sbellard if (flags & MAP_FIXED) 38654936004Sbellard printf("MAP_FIXED "); 38754936004Sbellard if (flags & MAP_ANONYMOUS) 38854936004Sbellard printf("MAP_ANON "); 38954936004Sbellard switch(flags & MAP_TYPE) { 39054936004Sbellard case MAP_PRIVATE: 39154936004Sbellard printf("MAP_PRIVATE "); 39254936004Sbellard break; 39354936004Sbellard case MAP_SHARED: 39454936004Sbellard printf("MAP_SHARED "); 39554936004Sbellard break; 39654936004Sbellard default: 39754936004Sbellard printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); 39854936004Sbellard break; 39954936004Sbellard } 4000bf9e31aSBlue Swirl printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset); 40154936004Sbellard } 40254936004Sbellard #endif 40354936004Sbellard 404e89f07d3Spbrook if (offset & ~TARGET_PAGE_MASK) { 405e89f07d3Spbrook errno = EINVAL; 406c8a706feSpbrook goto fail; 407e89f07d3Spbrook } 40854936004Sbellard 40954936004Sbellard len = TARGET_PAGE_ALIGN(len); 41054936004Sbellard if (len == 0) 411c8a706feSpbrook goto the_end; 41253a5960aSpbrook real_start = start & qemu_host_page_mask; 413a5e7ee46SRichard Henderson host_offset = offset & qemu_host_page_mask; 414a5e7ee46SRichard Henderson 415a5e7ee46SRichard Henderson /* If the user is asking for the kernel to find a location, do that 416a5e7ee46SRichard Henderson before we truncate the length for mapping files below. */ 417a5e7ee46SRichard Henderson if (!(flags & MAP_FIXED)) { 418a5e7ee46SRichard Henderson host_len = len + offset - host_offset; 419a5e7ee46SRichard Henderson host_len = HOST_PAGE_ALIGN(host_len); 420a5e7ee46SRichard Henderson start = mmap_find_vma(real_start, host_len); 421a5e7ee46SRichard Henderson if (start == (abi_ulong)-1) { 422a5e7ee46SRichard Henderson errno = ENOMEM; 423a5e7ee46SRichard Henderson goto fail; 424a5e7ee46SRichard Henderson } 425a5e7ee46SRichard Henderson } 42654936004Sbellard 42754c5a2aeSedgar_igl /* When mapping files into a memory area larger than the file, accesses 42854c5a2aeSedgar_igl to pages beyond the file size will cause a SIGBUS. 42954c5a2aeSedgar_igl 43054c5a2aeSedgar_igl For example, if mmaping a file of 100 bytes on a host with 4K pages 43154c5a2aeSedgar_igl emulating a target with 8K pages, the target expects to be able to 43254c5a2aeSedgar_igl access the first 8K. But the host will trap us on any access beyond 43354c5a2aeSedgar_igl 4K. 43454c5a2aeSedgar_igl 43554c5a2aeSedgar_igl When emulating a target with a larger page-size than the hosts, we 43654c5a2aeSedgar_igl may need to truncate file maps at EOF and add extra anonymous pages 43754c5a2aeSedgar_igl up to the targets page boundary. */ 43854c5a2aeSedgar_igl 43954c5a2aeSedgar_igl if ((qemu_real_host_page_size < TARGET_PAGE_SIZE) 44054c5a2aeSedgar_igl && !(flags & MAP_ANONYMOUS)) { 44154c5a2aeSedgar_igl struct stat sb; 44254c5a2aeSedgar_igl 44354c5a2aeSedgar_igl if (fstat (fd, &sb) == -1) 44454c5a2aeSedgar_igl goto fail; 44554c5a2aeSedgar_igl 44654c5a2aeSedgar_igl /* Are we trying to create a map beyond EOF?. */ 44754c5a2aeSedgar_igl if (offset + len > sb.st_size) { 44854c5a2aeSedgar_igl /* If so, truncate the file map at eof aligned with 44954c5a2aeSedgar_igl the hosts real pagesize. Additional anonymous maps 45054c5a2aeSedgar_igl will be created beyond EOF. */ 45154c5a2aeSedgar_igl len = (sb.st_size - offset); 45254c5a2aeSedgar_igl len += qemu_real_host_page_size - 1; 45354c5a2aeSedgar_igl len &= ~(qemu_real_host_page_size - 1); 45454c5a2aeSedgar_igl } 45554c5a2aeSedgar_igl } 45654c5a2aeSedgar_igl 45754936004Sbellard if (!(flags & MAP_FIXED)) { 458a5e7ee46SRichard Henderson unsigned long host_start; 459a03e2d42Sbellard void *p; 460a5e7ee46SRichard Henderson 46154936004Sbellard host_len = len + offset - host_offset; 462a03e2d42Sbellard host_len = HOST_PAGE_ALIGN(host_len); 463a5e7ee46SRichard Henderson 464a03e2d42Sbellard /* Note: we prefer to control the mapping address. It is 465a03e2d42Sbellard especially important if qemu_host_page_size > 466a03e2d42Sbellard qemu_real_host_page_size */ 467a5e7ee46SRichard Henderson p = mmap(g2h(start), host_len, prot, 468a5e7ee46SRichard Henderson flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0); 46980210bcdSths if (p == MAP_FAILED) 470c8a706feSpbrook goto fail; 47154936004Sbellard /* update start so that it points to the file position at 'offset' */ 47280210bcdSths host_start = (unsigned long)p; 47354c5a2aeSedgar_igl if (!(flags & MAP_ANONYMOUS)) { 474a5e7ee46SRichard Henderson p = mmap(g2h(start), len, prot, 47554c5a2aeSedgar_igl flags | MAP_FIXED, fd, host_offset); 4768384274eSJürg Billeter if (p == MAP_FAILED) { 4778384274eSJürg Billeter munmap(g2h(start), host_len); 4788384274eSJürg Billeter goto fail; 4798384274eSJürg Billeter } 48053a5960aSpbrook host_start += offset - host_offset; 48154c5a2aeSedgar_igl } 48253a5960aSpbrook start = h2g(host_start); 483a03e2d42Sbellard } else { 484e89f07d3Spbrook if (start & ~TARGET_PAGE_MASK) { 485e89f07d3Spbrook errno = EINVAL; 486c8a706feSpbrook goto fail; 487e89f07d3Spbrook } 48854936004Sbellard end = start + len; 48953a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 49054936004Sbellard 49145bc1f52Saurel32 /* 49245bc1f52Saurel32 * Test if requested memory area fits target address space 49345bc1f52Saurel32 * It can fail only on 64-bit host with 32-bit target. 49445bc1f52Saurel32 * On any other target/host host mmap() handles this error correctly. 49545bc1f52Saurel32 */ 49645bc1f52Saurel32 if ((unsigned long)start + len - 1 > (abi_ulong) -1) { 49745bc1f52Saurel32 errno = EINVAL; 49845bc1f52Saurel32 goto fail; 49945bc1f52Saurel32 } 50045bc1f52Saurel32 50154936004Sbellard /* worst case: we cannot map the file because the offset is not 50254936004Sbellard aligned, so we read it */ 50354936004Sbellard if (!(flags & MAP_ANONYMOUS) && 50483fb7adfSbellard (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { 50554936004Sbellard /* msync() won't work here, so we return an error if write is 50654936004Sbellard possible while it is a shared mapping */ 50754936004Sbellard if ((flags & MAP_TYPE) == MAP_SHARED && 508e89f07d3Spbrook (prot & PROT_WRITE)) { 509e89f07d3Spbrook errno = EINVAL; 510c8a706feSpbrook goto fail; 511e89f07d3Spbrook } 51254936004Sbellard retaddr = target_mmap(start, len, prot | PROT_WRITE, 51354936004Sbellard MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 51454936004Sbellard -1, 0); 51554936004Sbellard if (retaddr == -1) 516c8a706feSpbrook goto fail; 517fb7e378cSKirill A. Shutemov if (pread(fd, g2h(start), len, offset) == -1) 518fb7e378cSKirill A. Shutemov goto fail; 51954936004Sbellard if (!(prot & PROT_WRITE)) { 52054936004Sbellard ret = target_mprotect(start, len, prot); 521c8a706feSpbrook if (ret != 0) { 522c8a706feSpbrook start = ret; 523c8a706feSpbrook goto the_end; 524c8a706feSpbrook } 52554936004Sbellard } 52654936004Sbellard goto the_end; 52754936004Sbellard } 52854936004Sbellard 52954936004Sbellard /* handle the start of the mapping */ 53053a5960aSpbrook if (start > real_start) { 53153a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 53254936004Sbellard /* one single host page */ 53353a5960aSpbrook ret = mmap_frag(real_start, start, end, 53454936004Sbellard prot, flags, fd, offset); 53554936004Sbellard if (ret == -1) 536c8a706feSpbrook goto fail; 53754936004Sbellard goto the_end1; 53854936004Sbellard } 53953a5960aSpbrook ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, 54054936004Sbellard prot, flags, fd, offset); 54154936004Sbellard if (ret == -1) 542c8a706feSpbrook goto fail; 54353a5960aSpbrook real_start += qemu_host_page_size; 54454936004Sbellard } 54554936004Sbellard /* handle the end of the mapping */ 54653a5960aSpbrook if (end < real_end) { 54753a5960aSpbrook ret = mmap_frag(real_end - qemu_host_page_size, 54853a5960aSpbrook real_end - qemu_host_page_size, real_end, 54954936004Sbellard prot, flags, fd, 55053a5960aSpbrook offset + real_end - qemu_host_page_size - start); 55154936004Sbellard if (ret == -1) 552c8a706feSpbrook goto fail; 55353a5960aSpbrook real_end -= qemu_host_page_size; 55454936004Sbellard } 55554936004Sbellard 55654936004Sbellard /* map the middle (easier) */ 55753a5960aSpbrook if (real_start < real_end) { 55880210bcdSths void *p; 5594a585ccbSbellard unsigned long offset1; 5604a585ccbSbellard if (flags & MAP_ANONYMOUS) 5614a585ccbSbellard offset1 = 0; 5624a585ccbSbellard else 56353a5960aSpbrook offset1 = offset + real_start - start; 56480210bcdSths p = mmap(g2h(real_start), real_end - real_start, 5654a585ccbSbellard prot, flags, fd, offset1); 56680210bcdSths if (p == MAP_FAILED) 567c8a706feSpbrook goto fail; 56854936004Sbellard } 569a03e2d42Sbellard } 57054936004Sbellard the_end1: 57154936004Sbellard page_set_flags(start, start + len, prot | PAGE_VALID); 57254936004Sbellard the_end: 57354936004Sbellard #ifdef DEBUG_MMAP 5740bf9e31aSBlue Swirl printf("ret=0x" TARGET_ABI_FMT_lx "\n", start); 57554936004Sbellard page_dump(stdout); 57654936004Sbellard printf("\n"); 57754936004Sbellard #endif 578*35865339SPaolo Bonzini tb_invalidate_phys_range(start, start + len); 579c8a706feSpbrook mmap_unlock(); 58054936004Sbellard return start; 581c8a706feSpbrook fail: 582c8a706feSpbrook mmap_unlock(); 583c8a706feSpbrook return -1; 58454936004Sbellard } 58554936004Sbellard 58668a1c816SPaul Brook static void mmap_reserve(abi_ulong start, abi_ulong size) 58768a1c816SPaul Brook { 58868a1c816SPaul Brook abi_ulong real_start; 58968a1c816SPaul Brook abi_ulong real_end; 59068a1c816SPaul Brook abi_ulong addr; 59168a1c816SPaul Brook abi_ulong end; 59268a1c816SPaul Brook int prot; 59368a1c816SPaul Brook 59468a1c816SPaul Brook real_start = start & qemu_host_page_mask; 59568a1c816SPaul Brook real_end = HOST_PAGE_ALIGN(start + size); 59668a1c816SPaul Brook end = start + size; 59768a1c816SPaul Brook if (start > real_start) { 59868a1c816SPaul Brook /* handle host page containing start */ 59968a1c816SPaul Brook prot = 0; 60068a1c816SPaul Brook for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { 60168a1c816SPaul Brook prot |= page_get_flags(addr); 60268a1c816SPaul Brook } 60368a1c816SPaul Brook if (real_end == real_start + qemu_host_page_size) { 60468a1c816SPaul Brook for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 60568a1c816SPaul Brook prot |= page_get_flags(addr); 60668a1c816SPaul Brook } 60768a1c816SPaul Brook end = real_end; 60868a1c816SPaul Brook } 60968a1c816SPaul Brook if (prot != 0) 61068a1c816SPaul Brook real_start += qemu_host_page_size; 61168a1c816SPaul Brook } 61268a1c816SPaul Brook if (end < real_end) { 61368a1c816SPaul Brook prot = 0; 61468a1c816SPaul Brook for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 61568a1c816SPaul Brook prot |= page_get_flags(addr); 61668a1c816SPaul Brook } 61768a1c816SPaul Brook if (prot != 0) 61868a1c816SPaul Brook real_end -= qemu_host_page_size; 61968a1c816SPaul Brook } 62068a1c816SPaul Brook if (real_start != real_end) { 62168a1c816SPaul Brook mmap(g2h(real_start), real_end - real_start, PROT_NONE, 62268a1c816SPaul Brook MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, 62368a1c816SPaul Brook -1, 0); 62468a1c816SPaul Brook } 62568a1c816SPaul Brook } 62668a1c816SPaul Brook 627992f48a0Sblueswir1 int target_munmap(abi_ulong start, abi_ulong len) 62854936004Sbellard { 629992f48a0Sblueswir1 abi_ulong end, real_start, real_end, addr; 63054936004Sbellard int prot, ret; 63154936004Sbellard 63254936004Sbellard #ifdef DEBUG_MMAP 6330bf9e31aSBlue Swirl printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x" 6340bf9e31aSBlue Swirl TARGET_ABI_FMT_lx "\n", 6350bf9e31aSBlue Swirl start, len); 63654936004Sbellard #endif 63754936004Sbellard if (start & ~TARGET_PAGE_MASK) 63854936004Sbellard return -EINVAL; 63954936004Sbellard len = TARGET_PAGE_ALIGN(len); 64054936004Sbellard if (len == 0) 64154936004Sbellard return -EINVAL; 642c8a706feSpbrook mmap_lock(); 64354936004Sbellard end = start + len; 64453a5960aSpbrook real_start = start & qemu_host_page_mask; 64553a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 64654936004Sbellard 64753a5960aSpbrook if (start > real_start) { 64854936004Sbellard /* handle host page containing start */ 64954936004Sbellard prot = 0; 65053a5960aSpbrook for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { 65154936004Sbellard prot |= page_get_flags(addr); 65254936004Sbellard } 65353a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 65453a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 655d418c81eSbellard prot |= page_get_flags(addr); 656d418c81eSbellard } 65753a5960aSpbrook end = real_end; 658d418c81eSbellard } 65954936004Sbellard if (prot != 0) 66053a5960aSpbrook real_start += qemu_host_page_size; 66154936004Sbellard } 66253a5960aSpbrook if (end < real_end) { 66354936004Sbellard prot = 0; 66453a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 66554936004Sbellard prot |= page_get_flags(addr); 66654936004Sbellard } 66754936004Sbellard if (prot != 0) 66853a5960aSpbrook real_end -= qemu_host_page_size; 66954936004Sbellard } 67054936004Sbellard 671c8a706feSpbrook ret = 0; 67254936004Sbellard /* unmap what we can */ 67353a5960aSpbrook if (real_start < real_end) { 67418e9ea8aSAurelien Jarno if (RESERVED_VA) { 67568a1c816SPaul Brook mmap_reserve(real_start, real_end - real_start); 67668a1c816SPaul Brook } else { 6774118a970Sj_mayer ret = munmap(g2h(real_start), real_end - real_start); 67854936004Sbellard } 67968a1c816SPaul Brook } 68054936004Sbellard 68177a8f1a5SAlexander Graf if (ret == 0) { 68254936004Sbellard page_set_flags(start, start + len, 0); 683*35865339SPaolo Bonzini tb_invalidate_phys_range(start, start + len); 68477a8f1a5SAlexander Graf } 685c8a706feSpbrook mmap_unlock(); 686c8a706feSpbrook return ret; 68754936004Sbellard } 68854936004Sbellard 689992f48a0Sblueswir1 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, 690992f48a0Sblueswir1 abi_ulong new_size, unsigned long flags, 691992f48a0Sblueswir1 abi_ulong new_addr) 69254936004Sbellard { 69354936004Sbellard int prot; 694f19412a2Saurel32 void *host_addr; 69554936004Sbellard 696c8a706feSpbrook mmap_lock(); 697f19412a2Saurel32 69868a1c816SPaul Brook if (flags & MREMAP_FIXED) { 6993af72a4dSblueswir1 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), 7003af72a4dSblueswir1 old_size, new_size, 7013af72a4dSblueswir1 flags, 70268a1c816SPaul Brook g2h(new_addr)); 70368a1c816SPaul Brook 70418e9ea8aSAurelien Jarno if (RESERVED_VA && host_addr != MAP_FAILED) { 70568a1c816SPaul Brook /* If new and old addresses overlap then the above mremap will 70668a1c816SPaul Brook already have failed with EINVAL. */ 70768a1c816SPaul Brook mmap_reserve(old_addr, old_size); 70868a1c816SPaul Brook } 70968a1c816SPaul Brook } else if (flags & MREMAP_MAYMOVE) { 710f19412a2Saurel32 abi_ulong mmap_start; 711f19412a2Saurel32 712f19412a2Saurel32 mmap_start = mmap_find_vma(0, new_size); 713f19412a2Saurel32 714f19412a2Saurel32 if (mmap_start == -1) { 715f19412a2Saurel32 errno = ENOMEM; 716f19412a2Saurel32 host_addr = MAP_FAILED; 71768a1c816SPaul Brook } else { 7183af72a4dSblueswir1 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), 7193af72a4dSblueswir1 old_size, new_size, 7203af72a4dSblueswir1 flags | MREMAP_FIXED, 7213af72a4dSblueswir1 g2h(mmap_start)); 722c65ffe6dSamateur if ( RESERVED_VA ) { 72368a1c816SPaul Brook mmap_reserve(old_addr, old_size); 72468a1c816SPaul Brook } 725c65ffe6dSamateur } 7263af72a4dSblueswir1 } else { 72768a1c816SPaul Brook int prot = 0; 72818e9ea8aSAurelien Jarno if (RESERVED_VA && old_size < new_size) { 72968a1c816SPaul Brook abi_ulong addr; 73068a1c816SPaul Brook for (addr = old_addr + old_size; 73168a1c816SPaul Brook addr < old_addr + new_size; 73268a1c816SPaul Brook addr++) { 73368a1c816SPaul Brook prot |= page_get_flags(addr); 73468a1c816SPaul Brook } 73568a1c816SPaul Brook } 73668a1c816SPaul Brook if (prot == 0) { 737f19412a2Saurel32 host_addr = mremap(g2h(old_addr), old_size, new_size, flags); 73818e9ea8aSAurelien Jarno if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) { 73968a1c816SPaul Brook mmap_reserve(old_addr + old_size, new_size - old_size); 74068a1c816SPaul Brook } 74168a1c816SPaul Brook } else { 74268a1c816SPaul Brook errno = ENOMEM; 74368a1c816SPaul Brook host_addr = MAP_FAILED; 74468a1c816SPaul Brook } 745f19412a2Saurel32 /* Check if address fits target address space */ 746f19412a2Saurel32 if ((unsigned long)host_addr + new_size > (abi_ulong)-1) { 747f19412a2Saurel32 /* Revert mremap() changes */ 748f19412a2Saurel32 host_addr = mremap(g2h(old_addr), new_size, old_size, flags); 749f19412a2Saurel32 errno = ENOMEM; 750f19412a2Saurel32 host_addr = MAP_FAILED; 751f19412a2Saurel32 } 752f19412a2Saurel32 } 753f19412a2Saurel32 754f19412a2Saurel32 if (host_addr == MAP_FAILED) { 755c8a706feSpbrook new_addr = -1; 756c8a706feSpbrook } else { 757a5b85f79Sths new_addr = h2g(host_addr); 75854936004Sbellard prot = page_get_flags(old_addr); 75954936004Sbellard page_set_flags(old_addr, old_addr + old_size, 0); 76054936004Sbellard page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID); 761c8a706feSpbrook } 762*35865339SPaolo Bonzini tb_invalidate_phys_range(new_addr, new_addr + new_size); 763c8a706feSpbrook mmap_unlock(); 76454936004Sbellard return new_addr; 76554936004Sbellard } 76654936004Sbellard 767992f48a0Sblueswir1 int target_msync(abi_ulong start, abi_ulong len, int flags) 76854936004Sbellard { 769992f48a0Sblueswir1 abi_ulong end; 77054936004Sbellard 77154936004Sbellard if (start & ~TARGET_PAGE_MASK) 77254936004Sbellard return -EINVAL; 77354936004Sbellard len = TARGET_PAGE_ALIGN(len); 77454936004Sbellard end = start + len; 775d418c81eSbellard if (end < start) 776d418c81eSbellard return -EINVAL; 777d418c81eSbellard if (end == start) 778d418c81eSbellard return 0; 77954936004Sbellard 78083fb7adfSbellard start &= qemu_host_page_mask; 78153a5960aSpbrook return msync(g2h(start), end - start, flags); 78254936004Sbellard } 783