From e0e65bee16ae8515315d2bad02e021f3fb5dd236 Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Tue, 3 Jan 2012 09:38:34 +0000 Subject: 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(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9f5e53a..0e74ee0 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; -- cgit v1.1 From 84803b87a183bd71963584c3be5ca838d32c55df Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Tue, 3 Jan 2012 09:38:35 +0000 Subject: 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(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 962677e..2570140 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); } -- cgit v1.1 From 56e904ecb2018e30b10e9ec846635ff3b1e1d923 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 18:42:06 +0100 Subject: 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(+) (limited to 'linux-user') diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 6514502..fd8b7bb 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 0e74ee0..9d1c8b2 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 41f0ff8..f8f3af3 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 c370125..fb8c9c9 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 */ -- cgit v1.1 From 6083abd9aa134c9b0804e584f8279f54e6c90a1c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 19:44:41 +0100 Subject: 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 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index fb8c9c9..601618d 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 */ -- cgit v1.1 From edafea1330133eb8f6580a1c4f84fc5ec766eb4c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 20:10:20 +0100 Subject: 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(-) (limited to 'linux-user') diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index f8f3af3..a79b67d 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) -- cgit v1.1 From 49e9a0775224039701d34b208d8a1da982477fc9 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Jan 2012 20:11:37 +0100 Subject: 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(+) (limited to 'linux-user') diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index fd8b7bb..5b70f92 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 -- cgit v1.1 From 354a0008270e9167ce89527fdf8f1a85c3a7fb87 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 2 Feb 2012 02:22:34 +0100 Subject: 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(+) (limited to 'linux-user') diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 5b70f92..eb96a08 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 -- cgit v1.1 From 20249ae189ac0baa5011770bccabf3ee802eb2ab Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 6 Feb 2012 21:37:07 +0100 Subject: 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(+) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9d1c8b2..fdd49b1 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) -- cgit v1.1 From 59e9d91c7ae1b655997aec61c08eec1685414117 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 8 Mar 2012 14:40:33 +0000 Subject: 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(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 2570140..aa95db3 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 994c02b..7125d1c 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; + } + + if (start == mmap_next_start) { + mmap_next_start = addr; } - mmap_next_start = addr; - return last_addr; + + return addr; } #endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 6889567..dd74cc0 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); -- cgit v1.1 From 288e65b9eea0c9b3cbe21be46f3e24e4e8b2a090 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Dec 2011 00:33:28 +0100 Subject: 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(+) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index aa95db3..23ad357 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); -- cgit v1.1 From 1e6722f8b0c1eaff305c39d32c07054450ebdad1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 3 Feb 2012 14:48:03 +0000 Subject: 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(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index fdd49b1..ea44f99 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: -- cgit v1.1 From db9526b10a956fd80c08727685c300b63d5a7465 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 3 Feb 2012 14:48:03 +0000 Subject: 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(+) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ea44f99..8a92162 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)); -- cgit v1.1 From d1b02ea0dcd20998ef178ddc55fb9765f6aa1d44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 16 Mar 2012 17:16:36 +0000 Subject: 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(-) (limited to 'linux-user') diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h index 7f05879..5356395 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) -- cgit v1.1 From adf050b1b9f0fe7f921780eeb1888ce53d94d10b Mon Sep 17 00:00:00 2001 From: Benoit Canet Date: Wed, 9 Nov 2011 03:37:23 +0000 Subject: 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(-) (limited to 'linux-user') diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e502b39..4ce9743 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 -- cgit v1.1 From d8fd2954996255ba6ad610917e7849832d0120b7 Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Fri, 30 Mar 2012 18:02:50 +0100 Subject: 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 --- linux-user/elfload.c | 1 + linux-user/main.c | 34 +++++++++++++++++++++++++++++----- linux-user/qemu.h | 1 + 3 files changed, 31 insertions(+), 5 deletions(-) (limited to 'linux-user') diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4ce9743..f3b1552 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 23ad357..191b750 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 dd74cc0..7b299b7 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; -- cgit v1.1