Skip to content

Commit

Permalink
Merge branch 'linux-user.next' of git://git.linaro.org/people/pmaydel…
Browse files Browse the repository at this point in the history
…l/qemu-arm

* 'linux-user.next' of git://git.linaro.org/people/pmaydell/qemu-arm:
  linux-user: ARM: Ignore immediate value for svc in thumb mode
  linux-user: Use init_guest_space when -R and -B are specified
  linux-user: Factor out guest space probing into a function
  flatload: fix bss clearing
  linux-user: make host_to_target_cmsg support SO_TIMESTAMP cmsg_type
  linux-user: make do_setsockopt support SOL_RAW ICMP_FILTER socket option
  linux-user: pass sockaddr from host to target
  linux-user: Move target_to_host_errno_table[] setup out of ioctl loop
  linux-user: Fix SNDCTL_DSP_MAP{IN, OUT}BUF ioctl definitions
  linux-user: Fix incorrect TARGET_BLKBSZGET, TARGET_BLKBSZSET
  • Loading branch information
blueswirl committed Aug 14, 2012
2 parents 92fc4b5 + 3a1363a commit 873359d
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 92 deletions.
161 changes: 123 additions & 38 deletions linux-user/elfload.c
Expand Up @@ -332,16 +332,33 @@ enum
ARM_HWCAP_ARM_VFPv3D16 = 1 << 13,
};

#define TARGET_HAS_GUEST_VALIDATE_BASE
/* We want the opportunity to check the suggested base */
bool guest_validate_base(unsigned long guest_base)
#define TARGET_HAS_VALIDATE_GUEST_SPACE
/* Return 1 if the proposed guest space is suitable for the guest.
* Return 0 if the proposed guest space isn't suitable, but another
* address space should be tried.
* Return -1 if there is no way the proposed guest space can be
* valid regardless of the base.
* The guest code may leave a page mapped and populate it if the
* address is suitable.
*/
static int validate_guest_space(unsigned long guest_base,
unsigned long guest_size)
{
unsigned long real_start, test_page_addr;

/* We need to check that we can force a fault on access to the
* commpage at 0xffff0fxx
*/
test_page_addr = guest_base + (0xffff0f00 & qemu_host_page_mask);

/* If the commpage lies within the already allocated guest space,
* then there is no way we can allocate it.
*/
if (test_page_addr >= guest_base
&& test_page_addr <= (guest_base + guest_size)) {
return -1;
}

/* Note it needs to be writeable to let us initialise it */
real_start = (unsigned long)
mmap((void *)test_page_addr, qemu_host_page_size,
Expand Down Expand Up @@ -1418,14 +1435,105 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
return sp;
}

#ifndef TARGET_HAS_GUEST_VALIDATE_BASE
#ifndef TARGET_HAS_VALIDATE_GUEST_SPACE
/* If the guest doesn't have a validation function just agree */
bool guest_validate_base(unsigned long guest_base)
static int validate_guest_space(unsigned long guest_base,
unsigned long guest_size)
{
return 1;
}
#endif

unsigned long init_guest_space(unsigned long host_start,
unsigned long host_size,
unsigned long guest_start,
bool fixed)
{
unsigned long current_start, real_start;
int flags;

assert(host_start || host_size);

/* If just a starting address is given, then just verify that
* address. */
if (host_start && !host_size) {
if (validate_guest_space(host_start, host_size) == 1) {
return host_start;
} else {
return (unsigned long)-1;
}
}

/* Setup the initial flags and start address. */
current_start = host_start & qemu_host_page_mask;
flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
if (fixed) {
flags |= MAP_FIXED;
}

/* Otherwise, a non-zero size region of memory needs to be mapped
* and validated. */
while (1) {
unsigned long real_size = host_size;

/* Do not use mmap_find_vma here because that is limited to the
* guest address space. We are going to make the
* guest address space fit whatever we're given.
*/
real_start = (unsigned long)
mmap((void *)current_start, host_size, PROT_NONE, flags, -1, 0);
if (real_start == (unsigned long)-1) {
return (unsigned long)-1;
}

/* Ensure the address is properly aligned. */
if (real_start & ~qemu_host_page_mask) {
munmap((void *)real_start, host_size);
real_size = host_size + qemu_host_page_size;
real_start = (unsigned long)
mmap((void *)real_start, real_size, PROT_NONE, flags, -1, 0);
if (real_start == (unsigned long)-1) {
return (unsigned long)-1;
}
real_start = HOST_PAGE_ALIGN(real_start);
}

/* Check to see if the address is valid. */
if (!host_start || real_start == current_start) {
int valid = validate_guest_space(real_start - guest_start,
real_size);
if (valid == 1) {
break;
} else if (valid == -1) {
return (unsigned long)-1;
}
/* valid == 0, so try again. */
}

/* That address didn't work. Unmap and try a different one.
* The address the host picked because is typically right at
* the top of the host address space and leaves the guest with
* no usable address space. Resort to a linear search. We
* already compensated for mmap_min_addr, so this should not
* happen often. Probably means we got unlucky and host
* address space randomization put a shared library somewhere
* inconvenient.
*/
munmap((void *)real_start, host_size);
current_start += qemu_host_page_size;
if (host_start == current_start) {
/* Theoretically possible if host doesn't have any suitably
* aligned areas. Normally the first mmap will fail.
*/
return (unsigned long)-1;
}
}

qemu_log("Reserved 0x%lx bytes of guest address space\n", host_size);

return real_start;
}

static void probe_guest_base(const char *image_name,
abi_ulong loaddr, abi_ulong hiaddr)
{
Expand All @@ -1452,46 +1560,23 @@ static void probe_guest_base(const char *image_name,
}
}
host_size = hiaddr - loaddr;
while (1) {
/* Do not use mmap_find_vma here because that is limited to the
guest address space. We are going to make the
guest address space fit whatever we're given. */
real_start = (unsigned long)
mmap((void *)host_start, host_size, PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
if (real_start == (unsigned long)-1) {
goto exit_perror;
}
guest_base = real_start - loaddr;
if ((real_start == host_start) &&
guest_validate_base(guest_base)) {
break;
}
/* That address didn't work. Unmap and try a different one.
The address the host picked because is typically right at
the top of the host address space and leaves the guest with
no usable address space. Resort to a linear search. We
already compensated for mmap_min_addr, so this should not
happen often. Probably means we got unlucky and host
address space randomization put a shared library somewhere
inconvenient. */
munmap((void *)real_start, host_size);
host_start += qemu_host_page_size;
if (host_start == loaddr) {
/* Theoretically possible if host doesn't have any suitably
aligned areas. Normally the first mmap will fail. */
errmsg = "Unable to find space for application";
goto exit_errmsg;
}

/* Setup the initial guest memory space with ranges gleaned from
* the ELF image that is being loaded.
*/
real_start = init_guest_space(host_start, host_size, loaddr, false);
if (real_start == (unsigned long)-1) {
errmsg = "Unable to find space for application";
goto exit_errmsg;
}
guest_base = real_start - loaddr;

qemu_log("Relocating guest address space from 0x"
TARGET_ABI_FMT_lx " to 0x%lx\n",
loaddr, real_start);
}
return;

exit_perror:
errmsg = strerror(errno);
exit_errmsg:
fprintf(stderr, "%s: %s\n", image_name, errmsg);
exit(-1);
Expand Down
2 changes: 1 addition & 1 deletion linux-user/flatload.c
Expand Up @@ -660,7 +660,7 @@ static int load_flat_file(struct linux_binprm * bprm,
}

/* zero the BSS. */
memset((void *)((unsigned long)datapos + data_len), 0, bss_len);
memset(g2h(datapos + data_len), 0, bss_len);

return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions linux-user/ioctls.h
Expand Up @@ -186,8 +186,8 @@
IOCTL(SNDCTL_DSP_GETISPACE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_audio_buf_info)))
IOCTL(SNDCTL_DSP_GETOSPACE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_audio_buf_info)))
IOCTL(SNDCTL_DSP_GETTRIGGER, IOC_R, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_DSP_MAPINBUF, IOC_R, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_DSP_MAPOUTBUF, IOC_R, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_DSP_MAPINBUF, IOC_R, MK_PTR(MK_STRUCT(STRUCT_buffmem_desc)))
IOCTL(SNDCTL_DSP_MAPOUTBUF, IOC_R, MK_PTR(MK_STRUCT(STRUCT_buffmem_desc)))
IOCTL(SNDCTL_DSP_NONBLOCK, 0, TYPE_NULL)
IOCTL(SNDCTL_DSP_POST, 0, TYPE_NULL)
IOCTL(SNDCTL_DSP_RESET, 0, TYPE_NULL)
Expand Down
38 changes: 7 additions & 31 deletions linux-user/main.c
Expand Up @@ -822,8 +822,7 @@ void cpu_loop(CPUARMState *env)
} else if (n == ARM_NR_semihosting
|| n == ARM_NR_thumb_semihosting) {
env->regs[0] = do_arm_semihosting (env);
} else if (n == 0 || n >= ARM_SYSCALL_BASE
|| (env->thumb && n == ARM_THUMB_SYSCALL)) {
} else if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) {
/* linux syscall */
if (env->thumb || n == 0) {
n = env->regs[7];
Expand Down Expand Up @@ -3516,39 +3515,16 @@ int main(int argc, char **argv, char **envp)
*/
guest_base = HOST_PAGE_ALIGN(guest_base);

if (reserved_va) {
void *p;
int flags;

flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
if (have_guest_base) {
flags |= MAP_FIXED;
}
p = mmap((void *)guest_base, reserved_va, PROT_NONE, flags, -1, 0);
if (p == MAP_FAILED) {
if (reserved_va || have_guest_base) {
guest_base = init_guest_space(guest_base, reserved_va, 0,
have_guest_base);
if (guest_base == (unsigned long)-1) {
fprintf(stderr, "Unable to reserve guest address space\n");
exit(1);
}
guest_base = (unsigned long)p;
/* Make sure the address is properly aligned. */
if (guest_base & ~qemu_host_page_mask) {
munmap(p, reserved_va);
p = mmap((void *)guest_base, reserved_va + qemu_host_page_size,
PROT_NONE, flags, -1, 0);
if (p == MAP_FAILED) {
fprintf(stderr, "Unable to reserve guest address space\n");
exit(1);
}
guest_base = HOST_PAGE_ALIGN((unsigned long)p);
}
qemu_log("Reserved 0x%lx bytes of guest address space\n", reserved_va);
mmap_next_start = reserved_va;
}

if (reserved_va || have_guest_base) {
if (!guest_validate_base(guest_base)) {
fprintf(stderr, "Guest base/Reserved VA rejected by guest code\n");
exit(1);
if (reserved_va) {
mmap_next_start = reserved_va;
}
}
#endif /* CONFIG_USE_GUEST_BASE */
Expand Down
15 changes: 11 additions & 4 deletions linux-user/qemu.h
Expand Up @@ -204,11 +204,18 @@ int get_osversion(void);
void fork_start(void);
void fork_end(int child);

/* Return true if the proposed guest_base is suitable for the guest.
* The guest code may leave a page mapped and populate it if the
* address is suitable.
/* Creates the initial guest address space in the host memory space using
* the given host start address hint and size. The guest_start parameter
* specifies the start address of the guest space. guest_base will be the
* difference between the host start address computed by this function and
* guest_start. If fixed is specified, then the mapped address space must
* start at host_start. The real start address of the mapped memory space is
* returned or -1 if there was an error.
*/
bool guest_validate_base(unsigned long guest_base);
unsigned long init_guest_space(unsigned long host_start,
unsigned long host_size,
unsigned long guest_start,
bool fixed);

#include "qemu-log.h"

Expand Down

0 comments on commit 873359d

Please sign in to comment.