diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6b622d4ff9..819fdd515a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -332,9 +332,17 @@ 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; @@ -342,6 +350,15 @@ bool guest_validate_base(unsigned long guest_base) * 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, @@ -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) { @@ -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); diff --git a/linux-user/flatload.c b/linux-user/flatload.c index be794960cc..58f679e072 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -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; } diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index eb96a084c2..8a4776756a 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -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) diff --git a/linux-user/main.c b/linux-user/main.c index 9d921aa4f0..7dea084873 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -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]; @@ -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 */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 7b299b7bc3..69b27d7146 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -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" diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3ba3ef5719..41c869bfe0 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -60,6 +60,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include #include #include +#include #include "qemu-common.h" #ifdef TARGET_GPROF #include @@ -1268,7 +1269,6 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr, return 0; } -/* ??? Should this also swap msgh->name? */ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, struct target_msghdr *target_msgh) { @@ -1325,7 +1325,6 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, return 0; } -/* ??? Should this also swap msgh->name? */ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct msghdr *msgh) { @@ -1360,16 +1359,28 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type); target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len)); - if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { - gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); - memcpy(target_data, data, len); - } else { + if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + (cmsg->cmsg_type == SCM_RIGHTS)) { int *fd = (int *)data; int *target_fd = (int *)target_data; int i, numfds = len / sizeof(int); for (i = 0; i < numfds; i++) target_fd[i] = tswap32(fd[i]); + } else if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + (cmsg->cmsg_type == SO_TIMESTAMP) && + (len == sizeof(struct timeval))) { + /* copy struct timeval to target */ + struct timeval *tv = (struct timeval *)data; + struct target_timeval *target_tv = + (struct target_timeval *)target_data; + + target_tv->tv_sec = tswapal(tv->tv_sec); + target_tv->tv_usec = tswapal(tv->tv_usec); + } else { + gemu_log("Unsupported ancillary data: %d/%d\n", + cmsg->cmsg_level, cmsg->cmsg_type); + memcpy(target_data, data, len); } cmsg = CMSG_NXTHDR(msgh, cmsg); @@ -1450,6 +1461,25 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, unlock_user (ip_mreq_source, optval_addr, 0); break; + default: + goto unimplemented; + } + break; + case SOL_RAW: + switch (optname) { + case ICMP_FILTER: + /* struct icmp_filter takes an u32 value */ + if (optlen < sizeof(uint32_t)) { + return -TARGET_EINVAL; + } + + if (get_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + ret = get_errno(setsockopt(sockfd, level, optname, + &val, sizeof(val))); + break; + default: goto unimplemented; } @@ -1885,10 +1915,22 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg, if (!is_error(ret)) { len = ret; ret = host_to_target_cmsg(msgp, &msg); - if (!is_error(ret)) + if (!is_error(ret)) { + msgp->msg_namelen = tswap32(msg.msg_namelen); + if (msg.msg_name != NULL) { + ret = host_to_target_sockaddr(tswapal(msgp->msg_name), + msg.msg_name, msg.msg_namelen); + if (ret) { + goto out; + } + } + ret = len; + } } } + +out: unlock_iovec(vec, target_vec, count, !send); unlock_user_struct(msgp, target_msg, send ? 0 : 1); return ret; @@ -4606,6 +4648,12 @@ void syscall_init(void) #undef STRUCT #undef STRUCT_SPECIAL + /* Build target_to_host_errno_table[] table from + * host_to_target_errno_table[]. */ + for (i = 0; i < ERRNO_TABLE_SIZE; i++) { + target_to_host_errno_table[host_to_target_errno_table[i]] = i; + } + /* we patch the ioctl size if necessary. We rely on the fact that no ioctl has all the bits at '1' in the size field */ ie = ioctl_entries; @@ -4625,11 +4673,6 @@ void syscall_init(void) (size << TARGET_IOC_SIZESHIFT); } - /* Build target_to_host_errno_table[] table from - * host_to_target_errno_table[]. */ - for (i=0; i < ERRNO_TABLE_SIZE; i++) - target_to_host_errno_table[host_to_target_errno_table[i]] = i; - /* automatic consistency check if same arch */ #if (defined(__i386__) && defined(TARGET_I386) && defined(TARGET_ABI32)) || \ (defined(__x86_64__) && defined(TARGET_X86_64)) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index ba9a58c814..2cfda5afd5 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -880,8 +880,8 @@ struct target_pollfd { #define TARGET_BLKSECTGET TARGET_IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */ #define TARGET_BLKSSZGET TARGET_IO(0x12,104)/* get block device sector size */ /* A jump here: 108-111 have been used for various private purposes. */ -#define TARGET_BLKBSZGET TARGET_IOR(0x12,112,int) -#define TARGET_BLKBSZSET TARGET_IOW(0x12,113,int) +#define TARGET_BLKBSZGET TARGET_IOR(0x12, 112, abi_ulong) +#define TARGET_BLKBSZSET TARGET_IOW(0x12, 113, abi_ulong) #define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,abi_ulong) /* return device size in bytes (u64 *arg) */ @@ -2226,8 +2226,8 @@ struct target_eabi_flock64 { #define TARGET_SNDCTL_DSP_GETTRIGGER TARGET_IOR('P',16, int) #define TARGET_SNDCTL_DSP_GETIPTR TARGET_IORU('P',17) #define TARGET_SNDCTL_DSP_GETOPTR TARGET_IORU('P',18) -#define TARGET_SNDCTL_DSP_MAPINBUF 0x80085013 -#define TARGET_SNDCTL_DSP_MAPOUTBUF 0x80085014 +#define TARGET_SNDCTL_DSP_MAPINBUF TARGET_IORU('P', 19) +#define TARGET_SNDCTL_DSP_MAPOUTBUF TARGET_IORU('P', 20) #define TARGET_SNDCTL_DSP_NONBLOCK 0x0000500e #define TARGET_SNDCTL_DSP_SAMPLESIZE 0xc0045005 #define TARGET_SNDCTL_DSP_SETDUPLEX 0x00005016 diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 601618df98..44b6a58820 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -77,6 +77,9 @@ STRUCT(audio_buf_info, STRUCT(count_info, TYPE_INT, TYPE_INT, TYPE_INT) +STRUCT(buffmem_desc, + TYPE_PTRVOID, TYPE_INT) + STRUCT(mixer_info, MK_ARRAY(TYPE_CHAR, 16), MK_ARRAY(TYPE_CHAR, 32), TYPE_INT, MK_ARRAY(TYPE_INT, 10))