d5f1f46341
- add fixes for reserved_va mmap(NULL) case, fixes git build on arm OBS-URL: https://build.opensuse.org/package/show/Virtualization/qemu?expand=0&rev=59
377 lines
13 KiB
Diff
377 lines
13 KiB
Diff
From 0f7996403949df0249e833d7e0f250042eed3384 Mon Sep 17 00:00:00 2001
|
|
From: Alexander Graf <agraf@suse.de>
|
|
Date: Tue, 31 Jan 2012 18:42:06 +0100
|
|
Subject: [PATCH] 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 <agraf@suse.de>
|
|
---
|
|
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(+), 0 deletions(-)
|
|
|
|
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
|
|
index 224dbc6..8647d64 100644
|
|
--- a/linux-user/ioctls.h
|
|
+++ b/linux-user/ioctls.h
|
|
@@ -350,3 +350,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 3e8e3dd..7a20af4 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 <linux/fb.h>
|
|
#include <linux/vt.h>
|
|
+#include <linux/dm-ioctl.h>
|
|
#include "linux_loop.h"
|
|
#include "cpu-uname.h"
|
|
|
|
@@ -3320,6 +3321,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 f0acc72..c8b57c9 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 8fde25c..dea520e 100644
|
|
--- a/linux-user/syscall_types.h
|
|
+++ b/linux-user/syscall_types.h
|
|
@@ -191,6 +191,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 */
|