mirror of https://github.com/xemu-project/xemu.git
fixed target_mmap() if host page size < TARGET_PAGE_SIZE
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3642 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
dae3270c6e
commit
a03e2d421e
|
@ -151,19 +151,53 @@ static int mmap_frag(abi_ulong real_start,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__CYGWIN__)
|
||||||
|
/* Cygwin doesn't have a whole lot of address space. */
|
||||||
|
static abi_ulong mmap_next_start = 0x18000000;
|
||||||
|
#else
|
||||||
|
static abi_ulong mmap_next_start = 0x40000000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* find a free memory area of size 'size'. The search starts at
|
||||||
|
'start'. If 'start' == 0, then a default start address is used.
|
||||||
|
Return -1 if error.
|
||||||
|
*/
|
||||||
|
/* XXX: should mark pages used by the host as reserved to be sure not
|
||||||
|
to use them. */
|
||||||
|
static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
|
||||||
|
{
|
||||||
|
abi_ulong addr, addr1, addr_start;
|
||||||
|
int prot;
|
||||||
|
|
||||||
|
size = HOST_PAGE_ALIGN(size);
|
||||||
|
start = start & qemu_host_page_mask;
|
||||||
|
addr = start;
|
||||||
|
if (addr == 0)
|
||||||
|
addr = mmap_next_start;
|
||||||
|
addr_start = addr;
|
||||||
|
for(;;) {
|
||||||
|
prot = 0;
|
||||||
|
for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
|
||||||
|
prot |= page_get_flags(addr1);
|
||||||
|
}
|
||||||
|
if (prot == 0)
|
||||||
|
break;
|
||||||
|
addr += qemu_host_page_size;
|
||||||
|
/* we found nothing */
|
||||||
|
if (addr == addr_start)
|
||||||
|
return (abi_ulong)-1;
|
||||||
|
}
|
||||||
|
if (start == 0)
|
||||||
|
mmap_next_start = addr + size;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
/* NOTE: all the constants are the HOST ones */
|
/* NOTE: all the constants are the HOST ones */
|
||||||
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
|
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
|
||||||
int flags, int fd, abi_ulong offset)
|
int flags, int fd, abi_ulong offset)
|
||||||
{
|
{
|
||||||
abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
|
abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
|
||||||
unsigned long host_start;
|
unsigned long host_start;
|
||||||
#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \
|
|
||||||
defined(__ia64) || defined(__mips__)
|
|
||||||
static abi_ulong last_start = 0x40000000;
|
|
||||||
#elif defined(__CYGWIN__)
|
|
||||||
/* Cygwin doesn't have a whole lot of address space. */
|
|
||||||
static abi_ulong last_start = 0x18000000;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG_MMAP
|
#ifdef DEBUG_MMAP
|
||||||
{
|
{
|
||||||
|
@ -203,147 +237,101 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
|
||||||
real_start = start & qemu_host_page_mask;
|
real_start = start & qemu_host_page_mask;
|
||||||
|
|
||||||
if (!(flags & MAP_FIXED)) {
|
if (!(flags & MAP_FIXED)) {
|
||||||
#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \
|
abi_ulong mmap_start;
|
||||||
defined(__ia64) || defined(__mips__) || defined(__CYGWIN__)
|
void *p;
|
||||||
/* tell the kernel to search at the same place as i386 */
|
host_offset = offset & qemu_host_page_mask;
|
||||||
if (real_start == 0) {
|
host_len = len + offset - host_offset;
|
||||||
real_start = last_start;
|
host_len = HOST_PAGE_ALIGN(host_len);
|
||||||
last_start += HOST_PAGE_ALIGN(len);
|
mmap_start = mmap_find_vma(real_start, host_len);
|
||||||
|
if (mmap_start == (abi_ulong)-1) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
/* Note: we prefer to control the mapping address. It is
|
||||||
host_offset = offset & qemu_host_page_mask;
|
especially important if qemu_host_page_size >
|
||||||
host_len = len + offset - host_offset;
|
qemu_real_host_page_size */
|
||||||
|
p = mmap(g2h(mmap_start),
|
||||||
if (qemu_host_page_size > qemu_real_host_page_size) {
|
host_len, prot, flags | MAP_FIXED, fd, host_offset);
|
||||||
/*
|
if (p == MAP_FAILED)
|
||||||
* The guest expects to see mmapped areas aligned to it's pagesize.
|
return -1;
|
||||||
* If the host's real page size is smaller than the guest's, we need
|
/* update start so that it points to the file position at 'offset' */
|
||||||
* to fixup the maps. It is done by allocating a larger area,
|
host_start = (unsigned long)p;
|
||||||
* displacing the map (if needed) and finally chopping off the spare
|
if (!(flags & MAP_ANONYMOUS))
|
||||||
* room at the edges.
|
host_start += offset - host_offset;
|
||||||
*/
|
start = h2g(host_start);
|
||||||
|
} else {
|
||||||
/*
|
if (start & ~TARGET_PAGE_MASK) {
|
||||||
* We assume qemu_host_page_size is always the same as
|
|
||||||
* TARGET_PAGE_SIZE, see exec.c. qemu_real_host_page_size is the
|
|
||||||
* hosts real page size.
|
|
||||||
*/
|
|
||||||
abi_ulong host_end;
|
|
||||||
unsigned long host_aligned_start;
|
|
||||||
void *p;
|
|
||||||
|
|
||||||
host_len = HOST_PAGE_ALIGN(host_len + qemu_host_page_size
|
|
||||||
- qemu_real_host_page_size);
|
|
||||||
p = mmap(real_start ? g2h(real_start) : NULL,
|
|
||||||
host_len, prot, flags, fd, host_offset);
|
|
||||||
if (p == MAP_FAILED)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
host_start = (unsigned long)p;
|
|
||||||
host_end = host_start + host_len;
|
|
||||||
|
|
||||||
/* Find start and end, aligned to the targets pagesize with-in the
|
|
||||||
large mmaped area. */
|
|
||||||
host_aligned_start = TARGET_PAGE_ALIGN(host_start);
|
|
||||||
if (!(flags & MAP_ANONYMOUS))
|
|
||||||
host_aligned_start += offset - host_offset;
|
|
||||||
|
|
||||||
start = h2g(host_aligned_start);
|
|
||||||
end = start + TARGET_PAGE_ALIGN(len);
|
|
||||||
|
|
||||||
/* Chop off the leftovers, if any. */
|
|
||||||
if (host_aligned_start > host_start)
|
|
||||||
munmap((void *)host_start, host_aligned_start - host_start);
|
|
||||||
if (end < host_end)
|
|
||||||
munmap((void *)g2h(end), host_end - end);
|
|
||||||
|
|
||||||
goto the_end1;
|
|
||||||
} else {
|
|
||||||
/* if not fixed, no need to do anything */
|
|
||||||
void *p = mmap(real_start ? g2h(real_start) : NULL,
|
|
||||||
host_len, prot, flags, fd, host_offset);
|
|
||||||
if (p == MAP_FAILED)
|
|
||||||
return -1;
|
|
||||||
/* update start so that it points to the file position at 'offset' */
|
|
||||||
host_start = (unsigned long)p;
|
|
||||||
if (!(flags & MAP_ANONYMOUS))
|
|
||||||
host_start += offset - host_offset;
|
|
||||||
start = h2g(host_start);
|
|
||||||
goto the_end1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start & ~TARGET_PAGE_MASK) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
end = start + len;
|
|
||||||
real_end = HOST_PAGE_ALIGN(end);
|
|
||||||
|
|
||||||
/* worst case: we cannot map the file because the offset is not
|
|
||||||
aligned, so we read it */
|
|
||||||
if (!(flags & MAP_ANONYMOUS) &&
|
|
||||||
(offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
|
|
||||||
/* msync() won't work here, so we return an error if write is
|
|
||||||
possible while it is a shared mapping */
|
|
||||||
if ((flags & MAP_TYPE) == MAP_SHARED &&
|
|
||||||
(prot & PROT_WRITE)) {
|
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
retaddr = target_mmap(start, len, prot | PROT_WRITE,
|
end = start + len;
|
||||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
|
real_end = HOST_PAGE_ALIGN(end);
|
||||||
-1, 0);
|
|
||||||
if (retaddr == -1)
|
/* worst case: we cannot map the file because the offset is not
|
||||||
return -1;
|
aligned, so we read it */
|
||||||
pread(fd, g2h(start), len, offset);
|
if (!(flags & MAP_ANONYMOUS) &&
|
||||||
if (!(prot & PROT_WRITE)) {
|
(offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
|
||||||
ret = target_mprotect(start, len, prot);
|
/* msync() won't work here, so we return an error if write is
|
||||||
if (ret != 0)
|
possible while it is a shared mapping */
|
||||||
return ret;
|
if ((flags & MAP_TYPE) == MAP_SHARED &&
|
||||||
|
(prot & PROT_WRITE)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
retaddr = target_mmap(start, len, prot | PROT_WRITE,
|
||||||
|
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
|
||||||
|
-1, 0);
|
||||||
|
if (retaddr == -1)
|
||||||
|
return -1;
|
||||||
|
pread(fd, g2h(start), len, offset);
|
||||||
|
if (!(prot & PROT_WRITE)) {
|
||||||
|
ret = target_mprotect(start, len, prot);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
goto the_end;
|
||||||
}
|
}
|
||||||
goto the_end;
|
|
||||||
}
|
/* handle the start of the mapping */
|
||||||
|
if (start > real_start) {
|
||||||
/* handle the start of the mapping */
|
if (real_end == real_start + qemu_host_page_size) {
|
||||||
if (start > real_start) {
|
/* one single host page */
|
||||||
if (real_end == real_start + qemu_host_page_size) {
|
ret = mmap_frag(real_start, start, end,
|
||||||
/* one single host page */
|
prot, flags, fd, offset);
|
||||||
ret = mmap_frag(real_start, start, end,
|
if (ret == -1)
|
||||||
|
return ret;
|
||||||
|
goto the_end1;
|
||||||
|
}
|
||||||
|
ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
|
||||||
prot, flags, fd, offset);
|
prot, flags, fd, offset);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
return ret;
|
return ret;
|
||||||
goto the_end1;
|
real_start += qemu_host_page_size;
|
||||||
|
}
|
||||||
|
/* handle the end of the mapping */
|
||||||
|
if (end < real_end) {
|
||||||
|
ret = mmap_frag(real_end - qemu_host_page_size,
|
||||||
|
real_end - qemu_host_page_size, real_end,
|
||||||
|
prot, flags, fd,
|
||||||
|
offset + real_end - qemu_host_page_size - start);
|
||||||
|
if (ret == -1)
|
||||||
|
return -1;
|
||||||
|
real_end -= qemu_host_page_size;
|
||||||
}
|
}
|
||||||
ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
|
|
||||||
prot, flags, fd, offset);
|
|
||||||
if (ret == -1)
|
|
||||||
return ret;
|
|
||||||
real_start += qemu_host_page_size;
|
|
||||||
}
|
|
||||||
/* handle the end of the mapping */
|
|
||||||
if (end < real_end) {
|
|
||||||
ret = mmap_frag(real_end - qemu_host_page_size,
|
|
||||||
real_end - qemu_host_page_size, real_end,
|
|
||||||
prot, flags, fd,
|
|
||||||
offset + real_end - qemu_host_page_size - start);
|
|
||||||
if (ret == -1)
|
|
||||||
return -1;
|
|
||||||
real_end -= qemu_host_page_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* map the middle (easier) */
|
/* map the middle (easier) */
|
||||||
if (real_start < real_end) {
|
if (real_start < real_end) {
|
||||||
void *p;
|
void *p;
|
||||||
unsigned long offset1;
|
unsigned long offset1;
|
||||||
if (flags & MAP_ANONYMOUS)
|
if (flags & MAP_ANONYMOUS)
|
||||||
offset1 = 0;
|
offset1 = 0;
|
||||||
else
|
else
|
||||||
offset1 = offset + real_start - start;
|
offset1 = offset + real_start - start;
|
||||||
p = mmap(g2h(real_start), real_end - real_start,
|
p = mmap(g2h(real_start), real_end - real_start,
|
||||||
prot, flags, fd, offset1);
|
prot, flags, fd, offset1);
|
||||||
if (p == MAP_FAILED)
|
if (p == MAP_FAILED)
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
the_end1:
|
the_end1:
|
||||||
page_set_flags(start, start + len, prot | PAGE_VALID);
|
page_set_flags(start, start + len, prot | PAGE_VALID);
|
||||||
|
|
Loading…
Reference in New Issue