From e0e65bee16ae8515315d2bad02e021f3fb5dd236 Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Tue, 3 Jan 2012 09:38:34 +0000 Subject: [PATCH 01/17] linux-user: improve fake /proc/self/stat making `ps` not segfault. With the current fake /proc/self/stat implementation `ps` is segfaulting because it expects to read PID and argv[0] as first and second field respectively, with the latter being enclosed between backets. Reproducing is as easy as running: `ps` inside qemu-user chroot with /proc mounted. Signed-off-by: Fabio Erculiani Acked-by: Alexander Graf Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/syscall.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9f5e53a7fe..0e74ee05ed 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4662,11 +4662,22 @@ static int open_self_stat(void *cpu_env, int fd) int len; uint64_t val = 0; - if (i == 27) { - /* stack bottom */ - val = start_stack; + if (i == 0) { + /* pid */ + val = getpid(); + snprintf(buf, sizeof(buf), "%"PRId64 " ", val); + } else if (i == 1) { + /* app name */ + snprintf(buf, sizeof(buf), "(%s) ", ts->bprm->argv[0]); + } else if (i == 27) { + /* stack bottom */ + val = start_stack; + snprintf(buf, sizeof(buf), "%"PRId64 " ", val); + } else { + /* for the rest, there is MasterCard */ + snprintf(buf, sizeof(buf), "0%c", i == 43 ? '\n' : ' '); } - snprintf(buf, sizeof(buf), "%"PRId64 "%c", val, i == 43 ? '\n' : ' '); + len = strlen(buf); if (write(fd, buf, len) != len) { return -1; From 84803b87a183bd71963584c3be5ca838d32c55df Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Tue, 3 Jan 2012 09:38:35 +0000 Subject: [PATCH 02/17] linux-user: target_argv is placed on ts->bprm->argv and can't be freed() TaskState contains linux_bprm struct which encapsulates argv among other things. argv might be used around the code and is expected to contain valid data. Before this patch, ts->bprm->argv was NULL due to it being freed right after loader_exec(). Signed-off-by: Fabio Erculiani Acked-by: Alexander Graf Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/main.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 962677e01d..25701403e4 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3486,11 +3486,6 @@ int main(int argc, char **argv, char **envp) _exit(1); } - for (i = 0; i < target_argc; i++) { - free(target_argv[i]); - } - free(target_argv); - for (wrk = target_environ; *wrk; wrk++) { free(*wrk); } From 56e904ecb2018e30b10e9ec846635ff3b1e1d923 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 18:42:06 +0100 Subject: [PATCH 03/17] linux-user: implement device mapper ioctls This patch implements all ioctls currently implemented by device mapper, enabling us to run dmsetup and kpartx inside of linux-user. Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 32 ++++++ linux-user/syscall.c | 226 +++++++++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 18 +++ linux-user/syscall_types.h | 36 ++++++ 4 files changed, 312 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 6514502dc4..fd8b7bbbb4 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -345,3 +345,35 @@ IOCTL(VT_SETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode))) IOCTL(VT_RELDISP, 0, TYPE_INT) IOCTL(VT_DISALLOCATE, 0, TYPE_INT) + + IOCTL(DM_VERSION, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_REMOVE_ALL, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_LIST_DEVICES, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_DEV_CREATE, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_DEV_REMOVE, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_DEV_RENAME, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_DEV_SUSPEND, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_DEV_STATUS, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_DEV_WAIT, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_TABLE_LOAD, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_TABLE_CLEAR, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_TABLE_DEPS, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_TABLE_STATUS, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_LIST_VERSIONS,IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_TARGET_MSG, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) + IOCTL_SPECIAL(DM_DEV_SET_GEOMETRY, IOC_RW, do_ioctl_dm, + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 0e74ee05ed..9d1c8b2fe2 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -95,6 +95,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #endif #include #include +#include #include "linux_loop.h" #include "cpu-uname.h" @@ -3354,6 +3355,231 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, return ret; } +static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, + abi_long cmd, abi_long arg) +{ + void *argptr; + struct dm_ioctl *host_dm; + abi_long guest_data; + uint32_t guest_data_size; + int target_size; + const argtype *arg_type = ie->arg_type; + abi_long ret; + void *big_buf = NULL; + char *host_data; + + arg_type++; + target_size = thunk_type_size(arg_type, 0); + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) { + ret = -TARGET_EFAULT; + goto out; + } + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + + /* buf_temp is too small, so fetch things into a bigger buffer */ + big_buf = g_malloc0(((struct dm_ioctl*)buf_temp)->data_size * 2); + memcpy(big_buf, buf_temp, target_size); + buf_temp = big_buf; + host_dm = big_buf; + + guest_data = arg + host_dm->data_start; + if ((guest_data - arg) < 0) { + ret = -EINVAL; + goto out; + } + guest_data_size = host_dm->data_size - host_dm->data_start; + host_data = (char*)host_dm + host_dm->data_start; + + argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1); + switch (ie->host_cmd) { + case DM_REMOVE_ALL: + case DM_LIST_DEVICES: + case DM_DEV_CREATE: + case DM_DEV_REMOVE: + case DM_DEV_SUSPEND: + case DM_DEV_STATUS: + case DM_DEV_WAIT: + case DM_TABLE_STATUS: + case DM_TABLE_CLEAR: + case DM_TABLE_DEPS: + case DM_LIST_VERSIONS: + /* no input data */ + break; + case DM_DEV_RENAME: + case DM_DEV_SET_GEOMETRY: + /* data contains only strings */ + memcpy(host_data, argptr, guest_data_size); + break; + case DM_TARGET_MSG: + memcpy(host_data, argptr, guest_data_size); + *(uint64_t*)host_data = tswap64(*(uint64_t*)argptr); + break; + case DM_TABLE_LOAD: + { + void *gspec = argptr; + void *cur_data = host_data; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; + int spec_size = thunk_type_size(arg_type, 0); + int i; + + for (i = 0; i < host_dm->target_count; i++) { + struct dm_target_spec *spec = cur_data; + uint32_t next; + int slen; + + thunk_convert(spec, gspec, arg_type, THUNK_HOST); + slen = strlen((char*)gspec + spec_size) + 1; + next = spec->next; + spec->next = sizeof(*spec) + slen; + strcpy((char*)&spec[1], gspec + spec_size); + gspec += next; + cur_data += spec->next; + } + break; + } + default: + ret = -TARGET_EINVAL; + goto out; + } + unlock_user(argptr, guest_data, 0); + + ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp)); + if (!is_error(ret)) { + guest_data = arg + host_dm->data_start; + guest_data_size = host_dm->data_size - host_dm->data_start; + argptr = lock_user(VERIFY_WRITE, guest_data, guest_data_size, 0); + switch (ie->host_cmd) { + case DM_REMOVE_ALL: + case DM_DEV_CREATE: + case DM_DEV_REMOVE: + case DM_DEV_RENAME: + case DM_DEV_SUSPEND: + case DM_DEV_STATUS: + case DM_TABLE_LOAD: + case DM_TABLE_CLEAR: + case DM_TARGET_MSG: + case DM_DEV_SET_GEOMETRY: + /* no return data */ + break; + case DM_LIST_DEVICES: + { + struct dm_name_list *nl = (void*)host_dm + host_dm->data_start; + uint32_t remaining_data = guest_data_size; + void *cur_data = argptr; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) }; + int nl_size = 12; /* can't use thunk_size due to alignment */ + + while (1) { + uint32_t next = nl->next; + if (next) { + nl->next = nl_size + (strlen(nl->name) + 1); + } + if (remaining_data < nl->next) { + host_dm->flags |= DM_BUFFER_FULL_FLAG; + break; + } + thunk_convert(cur_data, nl, arg_type, THUNK_TARGET); + strcpy(cur_data + nl_size, nl->name); + cur_data += nl->next; + remaining_data -= nl->next; + if (!next) { + break; + } + nl = (void*)nl + next; + } + break; + } + case DM_DEV_WAIT: + case DM_TABLE_STATUS: + { + struct dm_target_spec *spec = (void*)host_dm + host_dm->data_start; + void *cur_data = argptr; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; + int spec_size = thunk_type_size(arg_type, 0); + int i; + + for (i = 0; i < host_dm->target_count; i++) { + uint32_t next = spec->next; + int slen = strlen((char*)&spec[1]) + 1; + spec->next = (cur_data - argptr) + spec_size + slen; + if (guest_data_size < spec->next) { + host_dm->flags |= DM_BUFFER_FULL_FLAG; + break; + } + thunk_convert(cur_data, spec, arg_type, THUNK_TARGET); + strcpy(cur_data + spec_size, (char*)&spec[1]); + cur_data = argptr + spec->next; + spec = (void*)host_dm + host_dm->data_start + next; + } + break; + } + case DM_TABLE_DEPS: + { + void *hdata = (void*)host_dm + host_dm->data_start; + int count = *(uint32_t*)hdata; + uint64_t *hdev = hdata + 8; + uint64_t *gdev = argptr + 8; + int i; + + *(uint32_t*)argptr = tswap32(count); + for (i = 0; i < count; i++) { + *gdev = tswap64(*hdev); + gdev++; + hdev++; + } + break; + } + case DM_LIST_VERSIONS: + { + struct dm_target_versions *vers = (void*)host_dm + host_dm->data_start; + uint32_t remaining_data = guest_data_size; + void *cur_data = argptr; + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) }; + int vers_size = thunk_type_size(arg_type, 0); + + while (1) { + uint32_t next = vers->next; + if (next) { + vers->next = vers_size + (strlen(vers->name) + 1); + } + if (remaining_data < vers->next) { + host_dm->flags |= DM_BUFFER_FULL_FLAG; + break; + } + thunk_convert(cur_data, vers, arg_type, THUNK_TARGET); + strcpy(cur_data + vers_size, vers->name); + cur_data += vers->next; + remaining_data -= vers->next; + if (!next) { + break; + } + vers = (void*)vers + next; + } + break; + } + default: + ret = -TARGET_EINVAL; + goto out; + } + unlock_user(argptr, guest_data, guest_data_size); + + argptr = lock_user(VERIFY_WRITE, arg, target_size, 0); + if (!argptr) { + ret = -TARGET_EFAULT; + goto out; + } + thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET); + unlock_user(argptr, arg, target_size); + } +out: + if (big_buf) { + free(big_buf); + } + return ret; +} + static IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, ...) \ { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 41f0ff8c7d..f8f3af361b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -989,6 +989,24 @@ struct target_pollfd { #define TARGET_VT_RELDISP 0x5605 #define TARGET_VT_DISALLOCATE 0x5608 +/* device mapper */ +#define TARGET_DM_VERSION TARGET_IOWRU(0xfd, 0x00) +#define TARGET_DM_REMOVE_ALL TARGET_IOWRU(0xfd, 0x01) +#define TARGET_DM_LIST_DEVICES TARGET_IOWRU(0xfd, 0x02) +#define TARGET_DM_DEV_CREATE TARGET_IOWRU(0xfd, 0x03) +#define TARGET_DM_DEV_REMOVE TARGET_IOWRU(0xfd, 0x04) +#define TARGET_DM_DEV_RENAME TARGET_IOWRU(0xfd, 0x05) +#define TARGET_DM_DEV_SUSPEND TARGET_IOWRU(0xfd, 0x06) +#define TARGET_DM_DEV_STATUS TARGET_IOWRU(0xfd, 0x07) +#define TARGET_DM_DEV_WAIT TARGET_IOWRU(0xfd, 0x08) +#define TARGET_DM_TABLE_LOAD TARGET_IOWRU(0xfd, 0x09) +#define TARGET_DM_TABLE_CLEAR TARGET_IOWRU(0xfd, 0x0a) +#define TARGET_DM_TABLE_DEPS TARGET_IOWRU(0xfd, 0x0b) +#define TARGET_DM_TABLE_STATUS TARGET_IOWRU(0xfd, 0x0c) +#define TARGET_DM_LIST_VERSIONS TARGET_IOWRU(0xfd, 0x0d) +#define TARGET_DM_TARGET_MSG TARGET_IOWRU(0xfd, 0x0e) +#define TARGET_DM_DEV_SET_GEOMETRY TARGET_IOWRU(0xfd, 0x0f) + /* from asm/termbits.h */ #define TARGET_NCC 8 diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index c370125170..fb8c9c980c 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -186,6 +186,42 @@ STRUCT(vt_mode, TYPE_SHORT, /* acqsig */ TYPE_SHORT) /* frsig */ +STRUCT(dm_ioctl, + MK_ARRAY(TYPE_INT, 3), /* version */ + TYPE_INT, /* data_size */ + TYPE_INT, /* data_start */ + TYPE_INT, /* target_count*/ + TYPE_INT, /* open_count */ + TYPE_INT, /* flags */ + TYPE_INT, /* event_nr */ + TYPE_INT, /* padding */ + TYPE_ULONGLONG, /* dev */ + MK_ARRAY(TYPE_CHAR, 128), /* name */ + MK_ARRAY(TYPE_CHAR, 129), /* uuid */ + MK_ARRAY(TYPE_CHAR, 7)) /* data */ + +STRUCT(dm_target_spec, + TYPE_ULONGLONG, /* sector_start */ + TYPE_ULONGLONG, /* length */ + TYPE_INT, /* status */ + TYPE_INT, /* next */ + MK_ARRAY(TYPE_CHAR, 16)) /* target_type */ + +STRUCT(dm_target_deps, + TYPE_INT, /* count */ + TYPE_INT) /* padding */ + +STRUCT(dm_name_list, + TYPE_ULONGLONG, /* dev */ + TYPE_INT) /* next */ + +STRUCT(dm_target_versions, + TYPE_INT, /* next */ + MK_ARRAY(TYPE_INT, 3)) /* version*/ + +STRUCT(dm_target_msg, + TYPE_ULONGLONG) /* sector */ + STRUCT(fiemap_extent, TYPE_ULONGLONG, /* fe_logical */ TYPE_ULONGLONG, /* fe_physical */ From 6083abd9aa134c9b0804e584f8279f54e6c90a1c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 19:44:41 +0100 Subject: [PATCH 04/17] linux-user: add struct old_dev_t compat The compat LOOP_SET_STATUS ioctl uses struct old_dev_t in its passed struct. That variable type is vastly different between different architectures. Implement wrapping around it so we can use it. This fixes running arm kpartx on an x86_64 host for me. Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/syscall_types.h | 4 ++-- thunk.c | 28 ++++++++++++++++++++++++++++ thunk.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index fb8c9c980c..601618df98 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -83,9 +83,9 @@ STRUCT(mixer_info, /* loop device ioctls */ STRUCT(loop_info, TYPE_INT, /* lo_number */ - TYPE_SHORT, /* lo_device */ + TYPE_OLDDEVT, /* lo_device */ TYPE_ULONG, /* lo_inode */ - TYPE_SHORT, /* lo_rdevice */ + TYPE_OLDDEVT, /* lo_rdevice */ TYPE_INT, /* lo_offset */ TYPE_INT, /* lo_encrypt_type */ TYPE_INT, /* lo_encrypt_key_size */ diff --git a/thunk.c b/thunk.c index 06571889ae..8ebbbb46b6 100644 --- a/thunk.c +++ b/thunk.c @@ -46,6 +46,7 @@ static inline const argtype *thunk_type_next(const argtype *type_ptr) case TYPE_LONG: case TYPE_ULONG: case TYPE_PTRVOID: + case TYPE_OLDDEVT: return type_ptr; case TYPE_PTR: return thunk_type_next_ptr(type_ptr); @@ -188,6 +189,33 @@ const argtype *thunk_convert(void *dst, const void *src, #else #warning unsupported conversion #endif + case TYPE_OLDDEVT: + { + uint64_t val = 0; + switch (thunk_type_size(type_ptr - 1, !to_host)) { + case 2: + val = *(uint16_t *)src; + break; + case 4: + val = *(uint32_t *)src; + break; + case 8: + val = *(uint64_t *)src; + break; + } + switch (thunk_type_size(type_ptr - 1, to_host)) { + case 2: + *(uint16_t *)dst = tswap16(val); + break; + case 4: + *(uint32_t *)dst = tswap32(val); + break; + case 8: + *(uint64_t *)dst = tswap64(val); + break; + } + break; + } case TYPE_ARRAY: { int array_length, i, dst_size, src_size; diff --git a/thunk.h b/thunk.h index 9810743191..5be8f912f5 100644 --- a/thunk.h +++ b/thunk.h @@ -37,6 +37,7 @@ typedef enum argtype { TYPE_PTR, TYPE_ARRAY, TYPE_STRUCT, + TYPE_OLDDEVT, } argtype; #define MK_PTR(type) TYPE_PTR, type @@ -104,6 +105,31 @@ static inline int thunk_type_size(const argtype *type_ptr, int is_host) return TARGET_ABI_BITS / 8; } break; + case TYPE_OLDDEVT: + if (is_host) { +#if defined(HOST_X86_64) + return 8; +#elif defined(HOST_ALPHA) || defined(HOST_IA64) || defined(HOST_MIPS) || \ + defined(HOST_PARISC) || defined(HOST_SPARC64) + return 4; +#elif defined(HOST_PPC) + return HOST_LONG_SIZE; +#else + return 2; +#endif + } else { +#if defined(TARGET_X86_64) + return 8; +#elif defined(TARGET_ALPHA) || defined(TARGET_IA64) || defined(TARGET_MIPS) || \ + defined(TARGET_PARISC) || defined(TARGET_SPARC64) + return 4; +#elif defined(TARGET_PPC) + return TARGET_ABI_BITS / 8; +#else + return 2; +#endif + } + break; case TYPE_ARRAY: size = type_ptr[1]; return size * thunk_type_size_array(type_ptr + 2, is_host); @@ -141,6 +167,8 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) return TARGET_ABI_BITS / 8; } break; + case TYPE_OLDDEVT: + return thunk_type_size(type_ptr, is_host); case TYPE_ARRAY: return thunk_type_align_array(type_ptr + 2, is_host); case TYPE_STRUCT: From edafea1330133eb8f6580a1c4f84fc5ec766eb4c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 20:10:20 +0100 Subject: [PATCH 05/17] linux-user: fix BLK ioctl arguments Some BLK ioctls passed sizeof(x) into a macro that already did sizeof() on the passed in argument, rendering the size information inside the ioctl be the size of the host default integer type. Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/syscall_defs.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index f8f3af361b..a79b67df49 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -832,9 +832,11 @@ 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,sizeof(int)) -#define TARGET_BLKBSZSET TARGET_IOW(0x12,113,sizeof(int)) -#define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ +#define TARGET_BLKBSZGET TARGET_IOR(0x12,112,int) +#define TARGET_BLKBSZSET TARGET_IOW(0x12,113,int) +#define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,abi_ulong) + /* return device size in bytes + (u64 *arg) */ #define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */ #define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */ #define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap) From 49e9a0775224039701d34b208d8a1da982477fc9 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 20:11:37 +0100 Subject: [PATCH 06/17] linux-user: add BLKSSZGET ioctl wrapper This patch adds an ioctl definition for BLKSSZGET. Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index fd8b7bbbb4..5b70f92c03 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -74,6 +74,7 @@ IOCTL(BLKFLSBUF, 0, TYPE_NULL) IOCTL(BLKRASET, 0, TYPE_INT) IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG)) + IOCTL(BLKSSZGET, IOC_R, MK_PTR(TYPE_LONG)) #ifdef FIBMAP IOCTL(FIBMAP, IOC_W | IOC_R, MK_PTR(TYPE_LONG)) #endif From 354a0008270e9167ce89527fdf8f1a85c3a7fb87 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 2 Feb 2012 02:22:34 +0100 Subject: [PATCH 07/17] linux-user: Add ioctl for BLKBSZGET This patch adds the ioctl wrapper definition for BLKBSZGET. Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 5b70f92c03..eb96a084c2 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -75,6 +75,7 @@ IOCTL(BLKRASET, 0, TYPE_INT) IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG)) IOCTL(BLKSSZGET, IOC_R, MK_PTR(TYPE_LONG)) + IOCTL(BLKBSZGET, IOC_R, MK_PTR(TYPE_INT)) #ifdef FIBMAP IOCTL(FIBMAP, IOC_W | IOC_R, MK_PTR(TYPE_LONG)) #endif From 20249ae189ac0baa5011770bccabf3ee802eb2ab Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 6 Feb 2012 21:37:07 +0100 Subject: [PATCH 08/17] linux-user: fix fallocate Fallocate gets off_t parameters passed in, so we should also read them out accordingly. Signed-off-by: Alexander Graf --- v1 -> v2: - unbreak 64-bit guests Signed-off-by: Riku Voipio --- linux-user/syscall.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9d1c8b2fe2..fdd49b1940 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8485,7 +8485,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif /* CONFIG_EVENTFD */ #if defined(CONFIG_FALLOCATE) && defined(TARGET_NR_fallocate) case TARGET_NR_fallocate: +#if TARGET_ABI_BITS == 32 + ret = get_errno(fallocate(arg1, arg2, target_offset64(arg3, arg4), + target_offset64(arg5, arg6))); +#else ret = get_errno(fallocate(arg1, arg2, arg3, arg4)); +#endif break; #endif #if defined(CONFIG_SYNC_FILE_RANGE) From 39879bbbea4661a4004ca26673e3d1e6ae1e0bc3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 2 Feb 2012 03:14:18 +0100 Subject: [PATCH 09/17] linux-user: take RESERVED_VA into account for g2h_valid() When running with -R (RESERVED_VA > 0) all guest virtual addresses are within the [0..RESERVED_VA] range. Reflect this with g2h_valid() too so we can safely check for boundaries of our guest address space. This is required to have the /proc/self/maps code not show maps that aren't accessible from the guest process's point of view. Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- cpu-all.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpu-all.h b/cpu-all.h index 9621c3c92e..4512518065 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -204,7 +204,8 @@ extern unsigned long reserved_va; #else #define h2g_valid(x) ({ \ unsigned long __guest = (unsigned long)(x) - GUEST_BASE; \ - __guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS); \ + (__guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS)) && \ + (!RESERVED_VA || (__guest < RESERVED_VA)); \ }) #endif From 59e9d91c7ae1b655997aec61c08eec1685414117 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 8 Mar 2012 14:40:33 +0000 Subject: [PATCH 10/17] linux-user: resolve reserved_va vma downwards After consulting with Paul Brook, we concluded that it's best to search the VMA space downwards, so that we don't even get the chance to conflict with the brk range. This patch resolves a bunch of allocation conflicts when using -R. Signed-off-by: Alexander Graf [minor changes to get it to apply -- PMM] Signed-off-by: Riku Voipio --- linux-user/main.c | 1 + linux-user/mmap.c | 35 ++++++++++++++++++++++++----------- linux-user/qemu.h | 1 + 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 25701403e4..aa95db3a6d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3420,6 +3420,7 @@ int main(int argc, char **argv, char **envp) 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) { diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 994c02bb77..7125d1cd4b 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -212,7 +212,7 @@ static int mmap_frag(abi_ulong real_start, #else # define TASK_UNMAPPED_BASE 0x40000000 #endif -static abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; +abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; unsigned long last_brk; @@ -222,7 +222,7 @@ unsigned long last_brk; static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) { abi_ulong addr; - abi_ulong last_addr; + abi_ulong end_addr; int prot; int looped = 0; @@ -230,25 +230,38 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) return (abi_ulong)-1; } - last_addr = start; - for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) { - if (last_addr + size >= RESERVED_VA - || (abi_ulong)(last_addr + size) < last_addr) { + size = HOST_PAGE_ALIGN(size); + end_addr = start + size; + if (end_addr > RESERVED_VA) { + end_addr = RESERVED_VA; + } + addr = end_addr - qemu_host_page_size; + + while (1) { + if (addr > end_addr) { if (looped) { return (abi_ulong)-1; } - last_addr = qemu_host_page_size; - addr = 0; + end_addr = RESERVED_VA; + addr = end_addr - qemu_host_page_size; looped = 1; continue; } prot = page_get_flags(addr); if (prot) { - last_addr = addr + qemu_host_page_size; + end_addr = addr; } + if (addr + size == end_addr) { + break; + } + addr -= qemu_host_page_size; } - mmap_next_start = addr; - return last_addr; + + if (start == mmap_next_start) { + mmap_next_start = addr; + } + + return addr; } #endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 68895671ed..dd74cc0510 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -251,6 +251,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, abi_ulong new_addr); int target_msync(abi_ulong start, abi_ulong len, int flags); extern unsigned long last_brk; +extern abi_ulong mmap_next_start; void mmap_lock(void); void mmap_unlock(void); abi_ulong mmap_find_vma(abi_ulong, abi_ulong); From 288e65b9eea0c9b3cbe21be46f3e24e4e8b2a090 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Dec 2011 00:33:28 +0100 Subject: [PATCH 11/17] linux-user: reserve 4GB of vmem for 32-on-64 When running 32-on-64 bit guests, we should always reserve as much virtual memory as we possibly can for the guest process, so it can never overlap with QEMU address space. Fortunately we already have the infrastructure for that. All that's missing is some sane default value to also make use of it! Signed-off-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/main.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index aa95db3a6d..23ad357b50 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -48,8 +48,19 @@ unsigned long mmap_min_addr; #if defined(CONFIG_USE_GUEST_BASE) unsigned long guest_base; int have_guest_base; +#if (TARGET_LONG_BITS == 32) && (HOST_LONG_BITS == 64) +/* + * When running 32-on-64 we should make sure we can fit all of the possible + * guest address space into a contiguous chunk of virtual host memory. + * + * This way we will never overlap with our own libraries or binaries or stack + * or anything else that QEMU maps. + */ +unsigned long reserved_va = 0xf7000000; +#else unsigned long reserved_va; #endif +#endif static void usage(void); From 1e6722f8b0c1eaff305c39d32c07054450ebdad1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 3 Feb 2012 14:48:03 +0000 Subject: [PATCH 12/17] linux-user/syscall.c: Fix indentation in prctl handling Clean up the odd indentation of this switch statement before we double its size by adding new cases to it. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index fdd49b1940..ea44f99a99 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -7242,21 +7242,22 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, goto unimplemented; #endif case TARGET_NR_prctl: - switch (arg1) - { - case PR_GET_PDEATHSIG: - { - int deathsig; - ret = get_errno(prctl(arg1, &deathsig, arg3, arg4, arg5)); - if (!is_error(ret) && arg2 - && put_user_ual(deathsig, arg2)) - goto efault; - } - break; - default: - ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); - break; + switch (arg1) { + case PR_GET_PDEATHSIG: + { + int deathsig; + ret = get_errno(prctl(arg1, &deathsig, arg3, arg4, arg5)); + if (!is_error(ret) && arg2 + && put_user_ual(deathsig, arg2)) { + goto efault; } + break; + } + default: + /* Most prctl options have no pointer arguments */ + ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); + break; + } break; #ifdef TARGET_NR_arch_prctl case TARGET_NR_arch_prctl: From db9526b10a956fd80c08727685c300b63d5a7465 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 3 Feb 2012 14:48:03 +0000 Subject: [PATCH 13/17] linux-user: Add support for prctl PR_GET_NAME and PR_SET_NAME Add support for the prctl options PR_GET_NAME and PR_SET_NAME, which take or return a name in a 16 byte buffer pointed to by arg2. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ea44f99a99..8a92162155 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -7253,6 +7253,30 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; } +#ifdef PR_GET_NAME + case PR_GET_NAME: + { + void *name = lock_user(VERIFY_WRITE, arg2, 16, 1); + if (!name) { + goto efault; + } + ret = get_errno(prctl(arg1, (unsigned long)name, + arg3, arg4, arg5)); + unlock_user(name, arg2, 16); + break; + } + case PR_SET_NAME: + { + void *name = lock_user(VERIFY_READ, arg2, 16, 1); + if (!name) { + goto efault; + } + ret = get_errno(prctl(arg1, (unsigned long)name, + arg3, arg4, arg5)); + unlock_user(name, arg2, 0); + break; + } +#endif default: /* Most prctl options have no pointer arguments */ ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); From d1b02ea0dcd20998ef178ddc55fb9765f6aa1d44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 16 Mar 2012 17:16:36 +0000 Subject: [PATCH 14/17] linux-user/arm/syscall_nr.h: Add syscall number for ppoll The list of ARM syscall numbers was missing the entry for ppoll, which meant we were accidentally not providing it. (This wasn't causing any practical issues beyond warnings about unimplemented syscalls, because glibc will fall back to another code path if the syscall isn't present.) Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/arm/syscall_nr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h index 7f05879ea3..5356395659 100644 --- a/linux-user/arm/syscall_nr.h +++ b/linux-user/arm/syscall_nr.h @@ -339,7 +339,7 @@ #define TARGET_NR_fchmodat (333) #define TARGET_NR_faccessat (334) #define TARGET_NR_pselect6 (335) - /* 336 for ppoll */ +#define TARGET_NR_ppoll (336) #define TARGET_NR_unshare (337) #define TARGET_NR_set_robust_list (338) #define TARGET_NR_get_robust_list (339) From adf050b1b9f0fe7f921780eeb1888ce53d94d10b Mon Sep 17 00:00:00 2001 From: Benoit Canet Date: Wed, 9 Nov 2011 03:37:23 +0000 Subject: [PATCH 15/17] arm-linux-user: fix elfload.c's AT_HWCAP to reflect cpu features. The cpu capabilities passed by the elf loader in AT_HWCAP where a constant. Make AT_HWCAP reflect the emulated cpu features in order to give correct clues to eglibc. Riku Voipio: fixed to apply to current head Fix : [Bug 887516] [NEW] VFP support reported for the PXA270 Signed-off-by: Benoit Canet Signed-off-by: Riku Voipio --- linux-user/elfload.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e502b39007..4ce97434e5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -375,10 +375,33 @@ bool guest_validate_base(unsigned long guest_base) return 1; /* All good */ } -#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \ - | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \ - | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \ - | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 ) + +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + CPUARMState *e = thread_env; + uint32_t hwcaps = 0; + + hwcaps |= ARM_HWCAP_ARM_SWP; + hwcaps |= ARM_HWCAP_ARM_HALF; + hwcaps |= ARM_HWCAP_ARM_THUMB; + hwcaps |= ARM_HWCAP_ARM_FAST_MULT; + hwcaps |= ARM_HWCAP_ARM_FPA; + + /* probe for the extra features */ +#define GET_FEATURE(feat, hwcap) \ + do {if (arm_feature(e, feat)) { hwcaps |= hwcap; } } while (0) + GET_FEATURE(ARM_FEATURE_VFP, ARM_HWCAP_ARM_VFP); + GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); + GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); + GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); + GET_FEATURE(ARM_FEATURE_VFP3, ARM_HWCAP_ARM_VFPv3); + GET_FEATURE(ARM_FEATURE_VFP_FP16, ARM_HWCAP_ARM_VFPv3D16); +#undef GET_FEATURE + + return hwcaps; +} #endif From ef8b0c0474aa7eab5172e866dc37428b41ff2934 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Mar 2012 18:02:49 +0100 Subject: [PATCH 16/17] elf.h: Update EF_ARM_ constants to newer ABI versions Update the EF_ARM_* constants (for the ELF header e_flags field) to include the newer flags specified for later versions of the ABI. (This set of constants is from include/elf/arm.h from binutils-2.17 and so licensed under GPL-v2-or-later.) Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- elf.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/elf.h b/elf.h index 36bcac4c11..e1422b81ee 100644 --- a/elf.h +++ b/elf.h @@ -538,6 +538,27 @@ typedef struct { #define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */ #define EF_NEW_ABI 0x80 #define EF_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +/* Other constants defined in the ARM ELF spec. version B-01. */ +#define EF_ARM_SYMSARESORTED 0x04 /* NB conflicts with EF_INTERWORK */ +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 /* NB conflicts with EF_APCS26 */ +#define EF_ARM_MAPSYMSFIRST 0x10 /* NB conflicts with EF_APCS_FLOAT */ +#define EF_ARM_EABIMASK 0xFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 /* Additional symbol types for Thumb */ #define STT_ARM_TFUNC 0xd From d8fd2954996255ba6ad610917e7849832d0120b7 Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Fri, 30 Mar 2012 18:02:50 +0100 Subject: [PATCH 17/17] Userspace ARM BE8 support Add support for ARM BE8 userspace binaries. i.e. big-endian data and little-endian code. In principle LE8 mode is also possible, but AFAIK has never actually been implemented/used. System emulation doesn't have any useable big-endian board models, but should in principle work once you fix that. Dynamic endianness switching requires messing with data accesses, preferably with TCG cooperation, and is orthogonal to BE8 support. Signed-off-by: Paul Brook [PMM: various changes, mostly as per my suggestions in code review: * rebase * use EF_ defines rather than hardcoded constants * make bswap_code a bool for future VMSTATE macro compatibility * update comment in cpu.h about TB flags bit field usage * factor out load-code-and-swap into arm_ld*_code functions and get_user_code* macros * fix stray trailing space at end of line * added braces in disas.c to satisfy checkpatch ] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- disas.c | 18 +++++++++++++----- linux-user/elfload.c | 1 + linux-user/main.c | 34 +++++++++++++++++++++++++++++----- linux-user/qemu.h | 1 + target-arm/cpu.h | 32 ++++++++++++++++++++++++++++++-- target-arm/helper.c | 9 +++++---- target-arm/translate.c | 11 +++++++---- 7 files changed, 86 insertions(+), 20 deletions(-) diff --git a/disas.c b/disas.c index 94858241de..4f2c4e4cb9 100644 --- a/disas.c +++ b/disas.c @@ -138,7 +138,7 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info) /* Disassemble this for me please... (debugging). 'flags' has the following values: i386 - 1 means 16 bit code, 2 means 64 bit code - arm - nonzero means thumb code + arm - bit 0 = thumb, bit 1 = reverse endian ppc - nonzero means little endian other targets - unused */ @@ -169,10 +169,18 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) disasm_info.mach = bfd_mach_i386_i386; print_insn = print_insn_i386; #elif defined(TARGET_ARM) - if (flags) - print_insn = print_insn_thumb1; - else - print_insn = print_insn_arm; + if (flags & 1) { + print_insn = print_insn_thumb1; + } else { + print_insn = print_insn_arm; + } + if (flags & 2) { +#ifdef TARGET_WORDS_BIGENDIAN + disasm_info.endian = BFD_ENDIAN_LITTLE; +#else + disasm_info.endian = BFD_ENDIAN_BIG; +#endif + } #elif defined(TARGET_SPARC) print_insn = print_insn_sparc; #ifdef TARGET_SPARC64 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4ce97434e5..f3b1552e9e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1576,6 +1576,7 @@ static void load_elf_image(const char *image_name, int image_fd, info->start_data = -1; info->end_data = 0; info->brk = 0; + info->elf_flags = ehdr->e_flags; for (i = 0; i < ehdr->e_phnum; i++) { struct elf_phdr *eppnt = phdr + i; diff --git a/linux-user/main.c b/linux-user/main.c index 23ad357b50..191b75060d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -33,6 +33,7 @@ #include "tcg.h" #include "qemu-timer.h" #include "envlist.h" +#include "elf.h" #define DEBUG_LOGFILE "/tmp/qemu.log" @@ -474,6 +475,22 @@ void cpu_loop(CPUX86State *env) #ifdef TARGET_ARM +#define get_user_code_u32(x, gaddr, doswap) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && (doswap)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_code_u16(x, gaddr, doswap) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && (doswap)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + /* * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt * Input: @@ -707,7 +724,7 @@ void cpu_loop(CPUARMState *env) /* we handle the FPU emulation here, as Linux */ /* we get the opcode */ /* FIXME - what to do if get_user() fails? */ - get_user_u32(opcode, env->regs[15]); + get_user_code_u32(opcode, env->regs[15], env->bswap_code); rc = EmulateAll(opcode, &ts->fpa, env); if (rc == 0) { /* illegal instruction */ @@ -777,23 +794,25 @@ void cpu_loop(CPUARMState *env) if (trapnr == EXCP_BKPT) { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_u16(insn, env->regs[15]); + get_user_code_u16(insn, env->regs[15], env->bswap_code); n = insn & 0xff; env->regs[15] += 2; } else { /* FIXME - what to do if get_user() fails? */ - get_user_u32(insn, env->regs[15]); + get_user_code_u32(insn, env->regs[15], env->bswap_code); n = (insn & 0xf) | ((insn >> 4) & 0xff0); env->regs[15] += 4; } } else { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_u16(insn, env->regs[15] - 2); + get_user_code_u16(insn, env->regs[15] - 2, + env->bswap_code); n = insn & 0xff; } else { /* FIXME - what to do if get_user() fails? */ - get_user_u32(insn, env->regs[15] - 4); + get_user_code_u32(insn, env->regs[15] - 4, + env->bswap_code); n = insn & 0xffffff; } } @@ -3657,6 +3676,11 @@ int main(int argc, char **argv, char **envp) for(i = 0; i < 16; i++) { env->regs[i] = regs->uregs[i]; } + /* Enable BE8. */ + if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 + && (info->elf_flags & EF_ARM_BE8)) { + env->bswap_code = 1; + } } #elif defined(TARGET_UNICORE32) { diff --git a/linux-user/qemu.h b/linux-user/qemu.h index dd74cc0510..7b299b7bc3 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -51,6 +51,7 @@ struct image_info { abi_ulong auxv_len; abi_ulong arg_start; abi_ulong arg_end; + uint32_t elf_flags; int personality; #ifdef CONFIG_USE_FDPIC abi_ulong loadmap_addr; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index e176c5f65c..c208c804aa 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -216,6 +216,9 @@ typedef struct CPUARMState { uint32_t cregs[16]; } iwmmxt; + /* For mixed endian mode. */ + bool bswap_code; + #if defined(CONFIG_USER_ONLY) /* For usermode syscall translation. */ int eabi; @@ -491,7 +494,9 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) #define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC_SHIFT 8 #define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) -/* Bits 31..16 are currently unused. */ +#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16 +#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT) +/* Bits 31..17 are currently unused. */ /* some convenience accessor macros */ #define ARM_TBFLAG_THUMB(F) \ @@ -506,6 +511,8 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC(F) \ (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) +#define ARM_TBFLAG_BSWAP_CODE(F) \ + (((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT) static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -516,7 +523,8 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) - | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT); + | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) + | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT); if (arm_feature(env, ARM_FEATURE_M)) { privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1)); } else { @@ -543,4 +551,24 @@ static inline void cpu_pc_from_tb(CPUARMState *env, TranslationBlock *tb) env->regs[15] = tb->pc; } +/* Load an instruction and return it in the standard little-endian order */ +static inline uint32_t arm_ldl_code(uint32_t addr, bool do_swap) +{ + uint32_t insn = ldl_code(addr); + if (do_swap) { + return bswap32(insn); + } + return insn; +} + +/* Ditto, for a halfword (Thumb) instruction */ +static inline uint16_t arm_lduw_code(uint32_t addr, bool do_swap) +{ + uint16_t insn = lduw_code(addr); + if (do_swap) { + return bswap16(insn); + } + return insn; +} + #endif diff --git a/target-arm/helper.c b/target-arm/helper.c index d974b579dc..28f127baf8 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -842,7 +842,7 @@ static void do_interrupt_v7m(CPUARMState *env) case EXCP_BKPT: if (semihosting_enabled) { int nr; - nr = lduw_code(env->regs[15]) & 0xff; + nr = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff; if (nr == 0xab) { env->regs[15] += 2; env->regs[0] = do_arm_semihosting(env); @@ -914,9 +914,10 @@ void do_interrupt(CPUARMState *env) if (semihosting_enabled) { /* Check for semihosting interrupt. */ if (env->thumb) { - mask = lduw_code(env->regs[15] - 2) & 0xff; + mask = arm_lduw_code(env->regs[15] - 2, env->bswap_code) & 0xff; } else { - mask = ldl_code(env->regs[15] - 4) & 0xffffff; + mask = arm_ldl_code(env->regs[15] - 4, env->bswap_code) + & 0xffffff; } /* Only intercept calls from privileged modes, to provide some semblance of security. */ @@ -936,7 +937,7 @@ void do_interrupt(CPUARMState *env) case EXCP_BKPT: /* See if this is a semihosting syscall. */ if (env->thumb && semihosting_enabled) { - mask = lduw_code(env->regs[15]) & 0xff; + mask = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff; if (mask == 0xab && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { env->regs[15] += 2; diff --git a/target-arm/translate.c b/target-arm/translate.c index 46d1d3ef9f..7a3c7d650c 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -59,6 +59,7 @@ typedef struct DisasContext { struct TranslationBlock *tb; int singlestep_enabled; int thumb; + int bswap_code; #if !defined(CONFIG_USER_ONLY) int user; #endif @@ -6705,7 +6706,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) TCGv addr; TCGv_i64 tmp64; - insn = ldl_code(s->pc); + insn = arm_ldl_code(s->pc, s->bswap_code); s->pc += 4; /* M variants do not implement ARM mode. */ @@ -8133,7 +8134,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw /* Fall through to 32-bit decode. */ } - insn = lduw_code(s->pc); + insn = arm_lduw_code(s->pc, s->bswap_code); s->pc += 2; insn |= (uint32_t)insn_hw1 << 16; @@ -9163,7 +9164,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } } - insn = lduw_code(s->pc); + insn = arm_lduw_code(s->pc, s->bswap_code); s->pc += 2; switch (insn >> 12) { @@ -9872,6 +9873,7 @@ static inline void gen_intermediate_code_internal(CPUARMState *env, dc->singlestep_enabled = env->singlestep_enabled; dc->condjmp = 0; dc->thumb = ARM_TBFLAG_THUMB(tb->flags); + dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; #if !defined(CONFIG_USER_ONLY) @@ -10105,7 +10107,8 @@ done_generating: if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(pc_start, dc->pc - pc_start, dc->thumb); + log_target_disas(pc_start, dc->pc - pc_start, + dc->thumb | (dc->bswap_code << 1)); qemu_log("\n"); } #endif