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 1754936004Sbellard * along with this program; if not, write to the Free Software 1854936004Sbellard * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 1954936004Sbellard */ 2054936004Sbellard #include <stdlib.h> 2154936004Sbellard #include <stdio.h> 2254936004Sbellard #include <stdarg.h> 2354936004Sbellard #include <string.h> 2454936004Sbellard #include <unistd.h> 2554936004Sbellard #include <errno.h> 2654936004Sbellard #include <sys/mman.h> 2754936004Sbellard 2854936004Sbellard #include "qemu.h" 2954936004Sbellard 3054936004Sbellard //#define DEBUG_MMAP 3154936004Sbellard 3253a5960aSpbrook /* NOTE: all the constants are the HOST ones, but addresses are target. */ 33992f48a0Sblueswir1 int target_mprotect(abi_ulong start, abi_ulong len, int prot) 3454936004Sbellard { 35992f48a0Sblueswir1 abi_ulong end, host_start, host_end, addr; 3654936004Sbellard int prot1, ret; 3754936004Sbellard 3854936004Sbellard #ifdef DEBUG_MMAP 39a5b85f79Sths printf("mprotect: start=0x" TARGET_FMT_lx 40a5b85f79Sths "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len, 4154936004Sbellard prot & PROT_READ ? 'r' : '-', 4254936004Sbellard prot & PROT_WRITE ? 'w' : '-', 4354936004Sbellard prot & PROT_EXEC ? 'x' : '-'); 4454936004Sbellard #endif 4554936004Sbellard 4654936004Sbellard if ((start & ~TARGET_PAGE_MASK) != 0) 4754936004Sbellard return -EINVAL; 4854936004Sbellard len = TARGET_PAGE_ALIGN(len); 4954936004Sbellard end = start + len; 5054936004Sbellard if (end < start) 5154936004Sbellard return -EINVAL; 52171cd1cdSbalrog prot &= PROT_READ | PROT_WRITE | PROT_EXEC; 5354936004Sbellard if (len == 0) 5454936004Sbellard return 0; 5554936004Sbellard 5683fb7adfSbellard host_start = start & qemu_host_page_mask; 5754936004Sbellard host_end = HOST_PAGE_ALIGN(end); 5854936004Sbellard if (start > host_start) { 5954936004Sbellard /* handle host page containing start */ 6054936004Sbellard prot1 = prot; 6154936004Sbellard for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) { 6254936004Sbellard prot1 |= page_get_flags(addr); 6354936004Sbellard } 6483fb7adfSbellard if (host_end == host_start + qemu_host_page_size) { 65d418c81eSbellard for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { 66d418c81eSbellard prot1 |= page_get_flags(addr); 67d418c81eSbellard } 68d418c81eSbellard end = host_end; 69d418c81eSbellard } 7053a5960aSpbrook ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS); 7154936004Sbellard if (ret != 0) 7254936004Sbellard return ret; 7383fb7adfSbellard host_start += qemu_host_page_size; 7454936004Sbellard } 7554936004Sbellard if (end < host_end) { 7654936004Sbellard prot1 = prot; 7754936004Sbellard for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { 7854936004Sbellard prot1 |= page_get_flags(addr); 7954936004Sbellard } 8053a5960aSpbrook ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size, 8154936004Sbellard prot1 & PAGE_BITS); 8254936004Sbellard if (ret != 0) 8354936004Sbellard return ret; 8483fb7adfSbellard host_end -= qemu_host_page_size; 8554936004Sbellard } 8654936004Sbellard 8754936004Sbellard /* handle the pages in the middle */ 8854936004Sbellard if (host_start < host_end) { 8953a5960aSpbrook ret = mprotect(g2h(host_start), host_end - host_start, prot); 9054936004Sbellard if (ret != 0) 9154936004Sbellard return ret; 9254936004Sbellard } 9354936004Sbellard page_set_flags(start, start + len, prot | PAGE_VALID); 9454936004Sbellard return 0; 9554936004Sbellard } 9654936004Sbellard 9754936004Sbellard /* map an incomplete host page */ 98992f48a0Sblueswir1 static int mmap_frag(abi_ulong real_start, 99992f48a0Sblueswir1 abi_ulong start, abi_ulong end, 100992f48a0Sblueswir1 int prot, int flags, int fd, abi_ulong offset) 10154936004Sbellard { 10280210bcdSths abi_ulong real_end, addr; 10353a5960aSpbrook void *host_start; 10454936004Sbellard int prot1, prot_new; 10554936004Sbellard 10653a5960aSpbrook real_end = real_start + qemu_host_page_size; 10753a5960aSpbrook host_start = g2h(real_start); 10854936004Sbellard 10954936004Sbellard /* get the protection of the target pages outside the mapping */ 11054936004Sbellard prot1 = 0; 11153a5960aSpbrook for(addr = real_start; addr < real_end; addr++) { 11254936004Sbellard if (addr < start || addr >= end) 11354936004Sbellard prot1 |= page_get_flags(addr); 11454936004Sbellard } 11554936004Sbellard 11654936004Sbellard if (prot1 == 0) { 11754936004Sbellard /* no page was there, so we allocate one */ 11880210bcdSths void *p = mmap(host_start, qemu_host_page_size, prot, 11954936004Sbellard flags | MAP_ANONYMOUS, -1, 0); 12080210bcdSths if (p == MAP_FAILED) 12180210bcdSths return -1; 12253a5960aSpbrook prot1 = prot; 12354936004Sbellard } 12454936004Sbellard prot1 &= PAGE_BITS; 12554936004Sbellard 12654936004Sbellard prot_new = prot | prot1; 12754936004Sbellard if (!(flags & MAP_ANONYMOUS)) { 12854936004Sbellard /* msync() won't work here, so we return an error if write is 12954936004Sbellard possible while it is a shared mapping */ 13054936004Sbellard if ((flags & MAP_TYPE) == MAP_SHARED && 13154936004Sbellard (prot & PROT_WRITE)) 13254936004Sbellard return -EINVAL; 13354936004Sbellard 13454936004Sbellard /* adjust protection to be able to read */ 13554936004Sbellard if (!(prot1 & PROT_WRITE)) 13653a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); 13754936004Sbellard 13854936004Sbellard /* read the corresponding file data */ 13953a5960aSpbrook pread(fd, g2h(start), end - start, offset); 14054936004Sbellard 14154936004Sbellard /* put final protection */ 14254936004Sbellard if (prot_new != (prot1 | PROT_WRITE)) 14353a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot_new); 14454936004Sbellard } else { 14554936004Sbellard /* just update the protection */ 14654936004Sbellard if (prot_new != prot1) { 14753a5960aSpbrook mprotect(host_start, qemu_host_page_size, prot_new); 14854936004Sbellard } 14954936004Sbellard } 15054936004Sbellard return 0; 15154936004Sbellard } 15254936004Sbellard 153a03e2d42Sbellard #if defined(__CYGWIN__) 154a03e2d42Sbellard /* Cygwin doesn't have a whole lot of address space. */ 155a03e2d42Sbellard static abi_ulong mmap_next_start = 0x18000000; 156a03e2d42Sbellard #else 157a03e2d42Sbellard static abi_ulong mmap_next_start = 0x40000000; 158a03e2d42Sbellard #endif 159a03e2d42Sbellard 160*0776590dSpbrook unsigned long last_brk; 161*0776590dSpbrook 162a03e2d42Sbellard /* find a free memory area of size 'size'. The search starts at 163a03e2d42Sbellard 'start'. If 'start' == 0, then a default start address is used. 164a03e2d42Sbellard Return -1 if error. 165a03e2d42Sbellard */ 16650a9569bSbalrog /* page_init() marks pages used by the host as reserved to be sure not 167a03e2d42Sbellard to use them. */ 168a03e2d42Sbellard static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) 169a03e2d42Sbellard { 170a03e2d42Sbellard abi_ulong addr, addr1, addr_start; 171a03e2d42Sbellard int prot; 172*0776590dSpbrook unsigned long new_brk; 173*0776590dSpbrook 174*0776590dSpbrook new_brk = (unsigned long)sbrk(0); 175*0776590dSpbrook if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) { 176*0776590dSpbrook /* This is a hack to catch the host allocating memory with brk(). 177*0776590dSpbrook If it uses mmap then we loose. 178*0776590dSpbrook FIXME: We really want to avoid the host allocating memory in 179*0776590dSpbrook the first place, and maybe leave some slack to avoid switching 180*0776590dSpbrook to mmap. */ 181*0776590dSpbrook page_set_flags(last_brk & TARGET_PAGE_MASK, 182*0776590dSpbrook TARGET_PAGE_ALIGN(new_brk), 183*0776590dSpbrook PAGE_RESERVED); 184*0776590dSpbrook } 185*0776590dSpbrook last_brk = new_brk; 186a03e2d42Sbellard 187a03e2d42Sbellard size = HOST_PAGE_ALIGN(size); 188a03e2d42Sbellard start = start & qemu_host_page_mask; 189a03e2d42Sbellard addr = start; 190a03e2d42Sbellard if (addr == 0) 191a03e2d42Sbellard addr = mmap_next_start; 192a03e2d42Sbellard addr_start = addr; 193a03e2d42Sbellard for(;;) { 194a03e2d42Sbellard prot = 0; 195a03e2d42Sbellard for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) { 196a03e2d42Sbellard prot |= page_get_flags(addr1); 197a03e2d42Sbellard } 198a03e2d42Sbellard if (prot == 0) 199a03e2d42Sbellard break; 200a03e2d42Sbellard addr += qemu_host_page_size; 201a03e2d42Sbellard /* we found nothing */ 202a03e2d42Sbellard if (addr == addr_start) 203a03e2d42Sbellard return (abi_ulong)-1; 204a03e2d42Sbellard } 205a03e2d42Sbellard if (start == 0) 206a03e2d42Sbellard mmap_next_start = addr + size; 207a03e2d42Sbellard return addr; 208a03e2d42Sbellard } 209a03e2d42Sbellard 21054936004Sbellard /* NOTE: all the constants are the HOST ones */ 211992f48a0Sblueswir1 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, 212992f48a0Sblueswir1 int flags, int fd, abi_ulong offset) 21354936004Sbellard { 214992f48a0Sblueswir1 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; 215a5b85f79Sths unsigned long host_start; 21654936004Sbellard 21754936004Sbellard #ifdef DEBUG_MMAP 21854936004Sbellard { 219a5b85f79Sths printf("mmap: start=0x" TARGET_FMT_lx 220a5b85f79Sths " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=", 22154936004Sbellard start, len, 22254936004Sbellard prot & PROT_READ ? 'r' : '-', 22354936004Sbellard prot & PROT_WRITE ? 'w' : '-', 22454936004Sbellard prot & PROT_EXEC ? 'x' : '-'); 22554936004Sbellard if (flags & MAP_FIXED) 22654936004Sbellard printf("MAP_FIXED "); 22754936004Sbellard if (flags & MAP_ANONYMOUS) 22854936004Sbellard printf("MAP_ANON "); 22954936004Sbellard switch(flags & MAP_TYPE) { 23054936004Sbellard case MAP_PRIVATE: 23154936004Sbellard printf("MAP_PRIVATE "); 23254936004Sbellard break; 23354936004Sbellard case MAP_SHARED: 23454936004Sbellard printf("MAP_SHARED "); 23554936004Sbellard break; 23654936004Sbellard default: 23754936004Sbellard printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); 23854936004Sbellard break; 23954936004Sbellard } 240a5b85f79Sths printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset); 24154936004Sbellard } 24254936004Sbellard #endif 24354936004Sbellard 244e89f07d3Spbrook if (offset & ~TARGET_PAGE_MASK) { 245e89f07d3Spbrook errno = EINVAL; 246e89f07d3Spbrook return -1; 247e89f07d3Spbrook } 24854936004Sbellard 24954936004Sbellard len = TARGET_PAGE_ALIGN(len); 25054936004Sbellard if (len == 0) 25154936004Sbellard return start; 25253a5960aSpbrook real_start = start & qemu_host_page_mask; 25354936004Sbellard 25454936004Sbellard if (!(flags & MAP_FIXED)) { 255a03e2d42Sbellard abi_ulong mmap_start; 256a03e2d42Sbellard void *p; 25783fb7adfSbellard host_offset = offset & qemu_host_page_mask; 25854936004Sbellard host_len = len + offset - host_offset; 259a03e2d42Sbellard host_len = HOST_PAGE_ALIGN(host_len); 260a03e2d42Sbellard mmap_start = mmap_find_vma(real_start, host_len); 261a03e2d42Sbellard if (mmap_start == (abi_ulong)-1) { 262a03e2d42Sbellard errno = ENOMEM; 263a5b85f79Sths return -1; 264a03e2d42Sbellard } 265a03e2d42Sbellard /* Note: we prefer to control the mapping address. It is 266a03e2d42Sbellard especially important if qemu_host_page_size > 267a03e2d42Sbellard qemu_real_host_page_size */ 268a03e2d42Sbellard p = mmap(g2h(mmap_start), 269a03e2d42Sbellard host_len, prot, flags | MAP_FIXED, fd, host_offset); 27080210bcdSths if (p == MAP_FAILED) 271a5b85f79Sths return -1; 27254936004Sbellard /* update start so that it points to the file position at 'offset' */ 27380210bcdSths host_start = (unsigned long)p; 27454936004Sbellard if (!(flags & MAP_ANONYMOUS)) 27553a5960aSpbrook host_start += offset - host_offset; 27653a5960aSpbrook start = h2g(host_start); 277a03e2d42Sbellard } else { 2787ab240adSbalrog int flg; 2797ab240adSbalrog target_ulong addr; 2807ab240adSbalrog 281e89f07d3Spbrook if (start & ~TARGET_PAGE_MASK) { 282e89f07d3Spbrook errno = EINVAL; 283e89f07d3Spbrook return -1; 284e89f07d3Spbrook } 28554936004Sbellard end = start + len; 28653a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 28754936004Sbellard 2887ab240adSbalrog for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) { 2897ab240adSbalrog flg = page_get_flags(addr); 2907ab240adSbalrog if (flg & PAGE_RESERVED) { 2917ab240adSbalrog errno = ENXIO; 2927ab240adSbalrog return -1; 2937ab240adSbalrog } 2947ab240adSbalrog } 2957ab240adSbalrog 29654936004Sbellard /* worst case: we cannot map the file because the offset is not 29754936004Sbellard aligned, so we read it */ 29854936004Sbellard if (!(flags & MAP_ANONYMOUS) && 29983fb7adfSbellard (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { 30054936004Sbellard /* msync() won't work here, so we return an error if write is 30154936004Sbellard possible while it is a shared mapping */ 30254936004Sbellard if ((flags & MAP_TYPE) == MAP_SHARED && 303e89f07d3Spbrook (prot & PROT_WRITE)) { 304e89f07d3Spbrook errno = EINVAL; 305e89f07d3Spbrook return -1; 306e89f07d3Spbrook } 30754936004Sbellard retaddr = target_mmap(start, len, prot | PROT_WRITE, 30854936004Sbellard MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 30954936004Sbellard -1, 0); 31054936004Sbellard if (retaddr == -1) 311a5b85f79Sths return -1; 31253a5960aSpbrook pread(fd, g2h(start), len, offset); 31354936004Sbellard if (!(prot & PROT_WRITE)) { 31454936004Sbellard ret = target_mprotect(start, len, prot); 31554936004Sbellard if (ret != 0) 31654936004Sbellard return ret; 31754936004Sbellard } 31854936004Sbellard goto the_end; 31954936004Sbellard } 32054936004Sbellard 32154936004Sbellard /* handle the start of the mapping */ 32253a5960aSpbrook if (start > real_start) { 32353a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 32454936004Sbellard /* one single host page */ 32553a5960aSpbrook ret = mmap_frag(real_start, start, end, 32654936004Sbellard prot, flags, fd, offset); 32754936004Sbellard if (ret == -1) 32854936004Sbellard return ret; 32954936004Sbellard goto the_end1; 33054936004Sbellard } 33153a5960aSpbrook ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, 33254936004Sbellard prot, flags, fd, offset); 33354936004Sbellard if (ret == -1) 33454936004Sbellard return ret; 33553a5960aSpbrook real_start += qemu_host_page_size; 33654936004Sbellard } 33754936004Sbellard /* handle the end of the mapping */ 33853a5960aSpbrook if (end < real_end) { 33953a5960aSpbrook ret = mmap_frag(real_end - qemu_host_page_size, 34053a5960aSpbrook real_end - qemu_host_page_size, real_end, 34154936004Sbellard prot, flags, fd, 34253a5960aSpbrook offset + real_end - qemu_host_page_size - start); 34354936004Sbellard if (ret == -1) 344a5b85f79Sths return -1; 34553a5960aSpbrook real_end -= qemu_host_page_size; 34654936004Sbellard } 34754936004Sbellard 34854936004Sbellard /* map the middle (easier) */ 34953a5960aSpbrook if (real_start < real_end) { 35080210bcdSths void *p; 3514a585ccbSbellard unsigned long offset1; 3524a585ccbSbellard if (flags & MAP_ANONYMOUS) 3534a585ccbSbellard offset1 = 0; 3544a585ccbSbellard else 35553a5960aSpbrook offset1 = offset + real_start - start; 35680210bcdSths p = mmap(g2h(real_start), real_end - real_start, 3574a585ccbSbellard prot, flags, fd, offset1); 35880210bcdSths if (p == MAP_FAILED) 359a5b85f79Sths return -1; 36054936004Sbellard } 361a03e2d42Sbellard } 36254936004Sbellard the_end1: 36354936004Sbellard page_set_flags(start, start + len, prot | PAGE_VALID); 36454936004Sbellard the_end: 36554936004Sbellard #ifdef DEBUG_MMAP 3662e0ded9cSedgar_igl printf("ret=0x" TARGET_FMT_lx "\n", start); 36754936004Sbellard page_dump(stdout); 36854936004Sbellard printf("\n"); 36954936004Sbellard #endif 37054936004Sbellard return start; 37154936004Sbellard } 37254936004Sbellard 373992f48a0Sblueswir1 int target_munmap(abi_ulong start, abi_ulong len) 37454936004Sbellard { 375992f48a0Sblueswir1 abi_ulong end, real_start, real_end, addr; 37654936004Sbellard int prot, ret; 37754936004Sbellard 37854936004Sbellard #ifdef DEBUG_MMAP 37954936004Sbellard printf("munmap: start=0x%lx len=0x%lx\n", start, len); 38054936004Sbellard #endif 38154936004Sbellard if (start & ~TARGET_PAGE_MASK) 38254936004Sbellard return -EINVAL; 38354936004Sbellard len = TARGET_PAGE_ALIGN(len); 38454936004Sbellard if (len == 0) 38554936004Sbellard return -EINVAL; 38654936004Sbellard end = start + len; 38753a5960aSpbrook real_start = start & qemu_host_page_mask; 38853a5960aSpbrook real_end = HOST_PAGE_ALIGN(end); 38954936004Sbellard 39053a5960aSpbrook if (start > real_start) { 39154936004Sbellard /* handle host page containing start */ 39254936004Sbellard prot = 0; 39353a5960aSpbrook for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { 39454936004Sbellard prot |= page_get_flags(addr); 39554936004Sbellard } 39653a5960aSpbrook if (real_end == real_start + qemu_host_page_size) { 39753a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 398d418c81eSbellard prot |= page_get_flags(addr); 399d418c81eSbellard } 40053a5960aSpbrook end = real_end; 401d418c81eSbellard } 40254936004Sbellard if (prot != 0) 40353a5960aSpbrook real_start += qemu_host_page_size; 40454936004Sbellard } 40553a5960aSpbrook if (end < real_end) { 40654936004Sbellard prot = 0; 40753a5960aSpbrook for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { 40854936004Sbellard prot |= page_get_flags(addr); 40954936004Sbellard } 41054936004Sbellard if (prot != 0) 41153a5960aSpbrook real_end -= qemu_host_page_size; 41254936004Sbellard } 41354936004Sbellard 41454936004Sbellard /* unmap what we can */ 41553a5960aSpbrook if (real_start < real_end) { 4164118a970Sj_mayer ret = munmap(g2h(real_start), real_end - real_start); 41754936004Sbellard if (ret != 0) 41854936004Sbellard return ret; 41954936004Sbellard } 42054936004Sbellard 42154936004Sbellard page_set_flags(start, start + len, 0); 42254936004Sbellard return 0; 42354936004Sbellard } 42454936004Sbellard 42554936004Sbellard /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED 42654936004Sbellard blocks which have been allocated starting on a host page */ 427992f48a0Sblueswir1 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, 428992f48a0Sblueswir1 abi_ulong new_size, unsigned long flags, 429992f48a0Sblueswir1 abi_ulong new_addr) 43054936004Sbellard { 43154936004Sbellard int prot; 432a5b85f79Sths unsigned long host_addr; 43354936004Sbellard 43454936004Sbellard /* XXX: use 5 args syscall */ 435a5b85f79Sths host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags); 436a5b85f79Sths if (host_addr == -1) 437a5b85f79Sths return -1; 438a5b85f79Sths new_addr = h2g(host_addr); 43954936004Sbellard prot = page_get_flags(old_addr); 44054936004Sbellard page_set_flags(old_addr, old_addr + old_size, 0); 44154936004Sbellard page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID); 44254936004Sbellard return new_addr; 44354936004Sbellard } 44454936004Sbellard 445992f48a0Sblueswir1 int target_msync(abi_ulong start, abi_ulong len, int flags) 44654936004Sbellard { 447992f48a0Sblueswir1 abi_ulong end; 44854936004Sbellard 44954936004Sbellard if (start & ~TARGET_PAGE_MASK) 45054936004Sbellard return -EINVAL; 45154936004Sbellard len = TARGET_PAGE_ALIGN(len); 45254936004Sbellard end = start + len; 453d418c81eSbellard if (end < start) 454d418c81eSbellard return -EINVAL; 455d418c81eSbellard if (end == start) 456d418c81eSbellard return 0; 45754936004Sbellard 45883fb7adfSbellard start &= qemu_host_page_mask; 45953a5960aSpbrook return msync(g2h(start), end - start, flags); 46054936004Sbellard } 461