Message ID | 1452808031-706-4-git-send-email-konrad.wilk@oracle.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 01/14/2016 09:47 PM, Konrad Rzeszutek Wilk wrote: > +struct xen_sysctl_xsplice_summary { > + xen_xsplice_name_t name; /* IN, name of the payload. */ > + xen_xsplice_status_t status; /* IN/OUT, state of it. */ > +}; > +typedef struct xen_sysctl_xsplice_summary xen_sysctl_xsplice_summary_t; > +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_summary_t); > + > +/* > + * Retrieve an array of abbreviated status and names of payloads that are > + * loaded in the hypervisor. > + * > + * If the hypercall returns an positive number, it is the number (up to `nr`) > + * of the payloads returned, along with `nr` updated with the number of remaining > + * payloads, `version` updated (it may be the same across hypercalls. If it > + * varies the data is stale and further calls could fail). The `status`, > + * `name`, and `len`' are updated at their designed index value (`idx`) with > + * the returned value of data. > + * > + * If the hypercall returns E2BIG the `nr` is too big and should be > + * lowered. > + * > + * This operation can be preempted by the hypercall returning EAGAIN. > + * Retry. > + * > + * Note that due to the asynchronous nature of hypercalls the domain might have > + * added or removed the number of payloads making this information stale. It is > + * the responsibility of the toolstack to use the `version` field to check > + * between each invocation. if the version differs it should discard the stale > + * data and start from scratch. It is OK for the toolstack to use the new > + * `version` field. > + */ > +#define XEN_SYSCTL_XSPLICE_LIST 2 > +struct xen_sysctl_xsplice_list { > + uint32_t version; /* IN/OUT: Initially *MUST* be zero. > + On subsequent calls reuse value. > + If varies between calls, we are > + * getting stale data. */ > + uint32_t idx; /* IN/OUT: Index into array. */ > + uint32_t nr; /* IN: How many status, id, and len s/id/name/ > + should fill out. > + OUT: How many payloads left. */ > + uint32_t pad; /* IN: Must be zero. */ > + XEN_GUEST_HANDLE_64(xen_xsplice_status_t) status; /* OUT. Must have enough > + space allocate for nr of them. */ > + XEN_GUEST_HANDLE_64(char) name; /* OUT: Array of ids. Each member s/id/name/ > + MUST XEN_XSPLICE_NAME_SIZE in size. > + Must have nr of them. */ > + XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of ids. s/id/name/ > + Must have nr of them. */ > +}; > +typedef struct xen_sysctl_xsplice_list xen_sysctl_xsplice_list_t; > +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_list_t); > + > +/* > + * Perform an operation on the payload structure referenced by the `name` field. > + * The operation request is asynchronous and the status should be retrieved > + * by using either XEN_SYSCTL_XSPLICE_GET or XEN_SYSCTL_XSPLICE_LIST hypercall. > + */ > +#define XEN_SYSCTL_XSPLICE_ACTION 3 > +struct xen_sysctl_xsplice_action {
On 1/14/16 3:47 PM, Konrad Rzeszutek Wilk wrote: > The implementation does not actually do any patching. > > It just adds the framework for doing the hypercalls, > keeping track of ELF payloads, and the basic operations: > - query which payloads exist, > - query for specific payloads, > - check*1, apply*1, replace*1, and unload payloads. > > *1: Which of course in this patch are nops. > > Acked-by: Daniel De Graaf <dgdegra@tycho.nsa.gov> > Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> > Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com> > > --- > v2: Rebased on keyhandler: rework keyhandler infrastructure > v3: Fixed XSM. > v4: Removed REVERTED state. > Split status and error code. > Add REPLACE action. > Separate payload data from the payload structure. > s/XSPLICE_ID_../XSPLICE_NAME_../ > v5: Add xsplice and CONFIG_XSPLICE build toption. > Fix code per Jan's review. > Update the sysctl.h (change bits to enum like) > v6: Rebase on Kconfig changes. > v7: Add missing pad checks. Re-order keyhandler.h to build on ARM. > > Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> > --- > tools/flask/policy/policy/modules/xen/xen.te | 1 + > xen/arch/arm/Kconfig | 1 + > xen/arch/x86/Kconfig | 1 + > xen/common/Kconfig | 14 + > xen/common/Makefile | 2 + > xen/common/sysctl.c | 8 + > xen/common/xsplice.c | 386 +++++++++++++++++++++++++++ > xen/include/public/sysctl.h | 156 +++++++++++ > xen/include/xen/xsplice.h | 7 + > xen/xsm/flask/hooks.c | 6 + > xen/xsm/flask/policy/access_vectors | 2 + > 11 files changed, 584 insertions(+) > create mode 100644 xen/common/xsplice.c > create mode 100644 xen/include/xen/xsplice.h > > diff --git a/tools/flask/policy/policy/modules/xen/xen.te b/tools/flask/policy/policy/modules/xen/xen.te > index d35ae22..542c3e1 100644 > --- a/tools/flask/policy/policy/modules/xen/xen.te > +++ b/tools/flask/policy/policy/modules/xen/xen.te > @@ -72,6 +72,7 @@ allow dom0_t xen_t:xen2 { > allow dom0_t xen_t:xen2 { > pmu_ctrl > get_symbol > + xsplice_op > }; > allow dom0_t xen_t:mmu memorymap; > > diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig > index 60e923c..3780949 100644 > --- a/xen/arch/arm/Kconfig > +++ b/xen/arch/arm/Kconfig > @@ -23,6 +23,7 @@ config ARM > select HAS_PASSTHROUGH > select HAS_PDX > select HAS_VIDEO > + select HAS_XSPLICE > > config ARCH_DEFCONFIG > string > diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig > index 4781b34..2b6c832 100644 > --- a/xen/arch/x86/Kconfig > +++ b/xen/arch/x86/Kconfig > @@ -18,6 +18,7 @@ config X86 > select HAS_PCI > select HAS_PDX > select HAS_VGA > + select HAS_XSPLICE > > config ARCH_DEFCONFIG > string > diff --git a/xen/common/Kconfig b/xen/common/Kconfig > index eadfc3b..aaf4053 100644 > --- a/xen/common/Kconfig > +++ b/xen/common/Kconfig > @@ -51,6 +51,9 @@ config HAS_GDBSX > config HAS_IOPORTS > bool > > +config HAS_XSPLICE > + bool > + > # Enable/Disable kexec support > config KEXEC > bool "kexec support" > @@ -97,4 +100,15 @@ config XSM > > If unsure, say N. > > +# Enable/Disable xsplice support > +config XSPLICE > + bool "xsplice support" > + default y > + depends on HAS_XSPLICE > + ---help--- > + Allows a running Xen hypervisor to be patched without rebooting. > + This is primarily used to patch an hypervisor with XSA fixes. > + > + If unsure, say Y. > + > endmenu I'm indifferent on the HAS_XSPLICE, you can drop that if you want to simply stuff. > diff --git a/xen/common/Makefile b/xen/common/Makefile > index 9f8b214..6fdeccf 100644 > --- a/xen/common/Makefile > +++ b/xen/common/Makefile > @@ -71,3 +71,5 @@ subdir-$(coverage) += gcov > > subdir-y += libelf > subdir-$(CONFIG_HAS_DEVICE_TREE) += libfdt > + > +obj-$(CONFIG_XSPLICE) += xsplice.o > diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c > index a3007b8..55e6cfa 100644 > --- a/xen/common/sysctl.c > +++ b/xen/common/sysctl.c > @@ -28,6 +28,7 @@ > #include <xsm/xsm.h> > #include <xen/pmstat.h> > #include <xen/gcov.h> > +#include <xen/xsplice.h> > > long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) > { > @@ -460,6 +461,13 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) > ret = tmem_control(&op->u.tmem_op); > break; > > +#ifdef CONFIG_XSPLICE > + case XEN_SYSCTL_xsplice_op: > + ret = xsplice_control(&op->u.xsplice); > + copyback = 1; > + break; > +#endif Should the case statement still exist and not just return -ENOSYS? Otherwise we're needlessly going into arch_do_sysctl() just to get the same result. > + > default: > ret = arch_do_sysctl(op, u_sysctl); > copyback = 0; > diff --git a/xen/common/xsplice.c b/xen/common/xsplice.c > new file mode 100644 > index 0000000..3c6acc3 > --- /dev/null > +++ b/xen/common/xsplice.c > @@ -0,0 +1,386 @@ > +/* > + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. > + * > + */ > + > +#include <xen/guest_access.h> > +#include <xen/keyhandler.h> > +#include <xen/lib.h> > +#include <xen/list.h> > +#include <xen/mm.h> > +#include <xen/sched.h> > +#include <xen/smp.h> > +#include <xen/spinlock.h> > +#include <xen/xsplice.h> > + > +#include <asm/event.h> > +#include <public/sysctl.h> > + > +static DEFINE_SPINLOCK(payload_list_lock); > +static LIST_HEAD(payload_list); > + > +static unsigned int payload_cnt; > +static unsigned int payload_version = 1; > + > +struct payload { > + int32_t state; /* One of the XSPLICE_STATE_*. */ > + int32_t rc; /* 0 or -XEN_EXX. */ > + struct list_head list; /* Linked to 'payload_list'. */ > + char name[XEN_XSPLICE_NAME_SIZE + 1];/* Name of it. */ > +}; > + > +static const char *state2str(int32_t state) > +{ > +#define STATE(x) [XSPLICE_STATE_##x] = #x > + static const char *const names[] = { > + STATE(LOADED), > + STATE(CHECKED), > + STATE(APPLIED), > + }; > +#undef STATE > + > + if (state >= ARRAY_SIZE(names)) > + return "unknown"; > + > + if (state < 0) > + return "-EXX"; > + > + if (!names[state]) > + return "unknown"; > + > + return names[state]; > +} > + > +static void xsplice_printall(unsigned char key) > +{ > + struct payload *data; > + > + spin_lock(&payload_list_lock); > + > + list_for_each_entry ( data, &payload_list, list ) > + printk(" name=%s state=%s(%d)\n", data->name, > + state2str(data->state), data->state); > + > + spin_unlock(&payload_list_lock); > +} > + > +static int verify_name(xen_xsplice_name_t *name) > +{ > + if ( name->size == 0 || name->size > XEN_XSPLICE_NAME_SIZE ) > + return -EINVAL; > + > + if ( name->pad[0] || name->pad[1] || name->pad[2] ) > + return -EINVAL; > + > + if ( !guest_handle_okay(name->name, name->size) ) > + return -EINVAL; > + > + return 0; > +} > + > +static int find_payload(xen_xsplice_name_t *name, bool_t need_lock, > + struct payload **f) > +{ > + struct payload *data; > + XEN_GUEST_HANDLE_PARAM(char) str; > + char n[XEN_XSPLICE_NAME_SIZE + 1] = { 0 }; > + int rc = -EINVAL; > + > + rc = verify_name(name); > + if ( rc ) > + return rc; > + > + str = guest_handle_cast(name->name, char); > + if ( copy_from_guest(n, str, name->size) ) > + return -EFAULT; > + > + if ( need_lock ) > + spin_lock(&payload_list_lock); > + > + rc = -ENOENT; > + list_for_each_entry ( data, &payload_list, list ) > + { > + if ( !strcmp(data->name, n) ) > + { > + *f = data; > + rc = 0; > + break; > + } > + } > + > + if ( need_lock ) > + spin_unlock(&payload_list_lock); > + > + return rc; > +} > + > +static int verify_payload(xen_sysctl_xsplice_upload_t *upload) > +{ > + if ( verify_name(&upload->name) ) > + return -EINVAL; > + > + if ( upload->size == 0 ) > + return -EINVAL; > + > + if ( !guest_handle_okay(upload->payload, upload->size) ) > + return -EFAULT; > + > + return 0; > +} > + > +/* > + * We MUST be holding the payload_list_lock spinlock. > + */ > +static void free_payload(struct payload *data) > +{ > + list_del(&data->list); > + payload_cnt--; > + payload_version++; > + xfree(data); > +} > + > +static int xsplice_upload(xen_sysctl_xsplice_upload_t *upload) > +{ > + struct payload *data = NULL; > + uint8_t *raw_data; > + int rc; > + > + rc = verify_payload(upload); > + if ( rc ) > + return rc; > + > + rc = find_payload(&upload->name, 1 /* true. */, &data); > + if ( rc == 0 /* Found. */ ) > + return -EEXIST; > + > + if ( rc != -ENOENT ) > + return rc; > + > + data = xzalloc(struct payload); > + if ( !data ) > + return -ENOMEM; > + > + memset(data, 0, sizeof *data); > + rc = -EFAULT; > + if ( copy_from_guest(data->name, upload->name.name, upload->name.size) ) > + goto err_data; > + > + rc = -ENOMEM; > + raw_data = alloc_xenheap_pages(get_order_from_bytes(upload->size), 0); > + if ( !raw_data ) > + goto err_data; > + > + rc = -EFAULT; > + if ( copy_from_guest(raw_data, upload->payload, upload->size) ) > + goto err_raw; > + > + data->state = XSPLICE_STATE_LOADED; > + data->rc = 0; > + INIT_LIST_HEAD(&data->list); > + > + spin_lock(&payload_list_lock); > + list_add_tail(&data->list, &payload_list); > + payload_cnt++; > + payload_version++; > + spin_unlock(&payload_list_lock); > + > + free_xenheap_pages(raw_data, get_order_from_bytes(upload->size)); > + return 0; > + > + err_raw: > + free_xenheap_pages(raw_data, get_order_from_bytes(upload->size)); > + err_data: > + xfree(data); > + return rc; > +} > + > +static int xsplice_get(xen_sysctl_xsplice_summary_t *summary) > +{ > + struct payload *data; > + int rc; > + > + if ( summary->status.state ) > + return -EINVAL; > + > + if ( summary->status.rc != 0 ) > + return -EINVAL; > + > + rc = verify_name(&summary->name); > + if ( rc ) > + return rc; > + > + rc = find_payload(&summary->name, 1 /* true. */, &data); > + if ( rc ) > + return rc; > + > + summary->status.state = data->state; > + summary->status.rc = data->rc; > + > + return 0; > +} > + > +static int xsplice_list(xen_sysctl_xsplice_list_t *list) > +{ > + xen_xsplice_status_t status; > + struct payload *data; > + unsigned int idx = 0, i = 0; > + int rc = 0; > + > + if ( list->nr > 1024 ) > + return -E2BIG; > + > + if ( list->pad != 0 ) > + return -EINVAL; > + > + if ( !guest_handle_okay(list->status, sizeof(status) * list->nr) || > + !guest_handle_okay(list->name, XEN_XSPLICE_NAME_SIZE * list->nr) || > + !guest_handle_okay(list->len, sizeof(uint32_t) * list->nr) ) > + return -EINVAL; > + > + spin_lock(&payload_list_lock); > + if ( list->idx > payload_cnt || !list->nr ) > + { > + spin_unlock(&payload_list_lock); > + return -EINVAL; > + } > + > + list_for_each_entry( data, &payload_list, list ) > + { > + uint32_t len; > + > + if ( list->idx > i++ ) > + continue; > + > + status.state = data->state; > + status.rc = data->rc; > + len = strlen(data->name); > + > + /* N.B. 'idx' != 'i'. */ > + if ( __copy_to_guest_offset(list->name, idx * XEN_XSPLICE_NAME_SIZE, > + data->name, len) || > + __copy_to_guest_offset(list->len, idx, &len, 1) || > + __copy_to_guest_offset(list->status, idx, &status, 1) ) > + { > + rc = -EFAULT; > + break; > + } > + idx++; > + if ( hypercall_preempt_check() || (idx + 1 > list->nr) ) > + break; > + } > + list->nr = payload_cnt - i; /* Remaining amount. */ > + list->version = payload_version; > + spin_unlock(&payload_list_lock); > + > + /* And how many we have processed. */ > + return rc ? : idx; > +} > + > +static int xsplice_action(xen_sysctl_xsplice_action_t *action) > +{ > + struct payload *data; > + int rc; > + > + rc = verify_name(&action->name); > + if ( rc ) > + return rc; > + > + spin_lock(&payload_list_lock); > + rc = find_payload(&action->name, 0 /* We are holding the lock. */, &data); > + if ( rc ) > + goto out; > + > + switch ( action->cmd ) > + { > + case XSPLICE_ACTION_CHECK: > + if ( (data->state == XSPLICE_STATE_LOADED) || > + (data->state == XSPLICE_STATE_CHECKED) ) > + { > + /* No implementation yet. */ > + data->state = XSPLICE_STATE_CHECKED; > + data->rc = 0; > + rc = 0; > + } > + break; > + case XSPLICE_ACTION_UNLOAD: > + if ( (data->state == XSPLICE_STATE_LOADED) || > + (data->state == XSPLICE_STATE_CHECKED) ) > + { > + free_payload(data); > + /* No touching 'data' from here on! */ > + rc = 0; > + } > + break; > + case XSPLICE_ACTION_REVERT: > + if ( data->state == XSPLICE_STATE_APPLIED ) > + { > + /* No implementation yet. */ > + data->state = XSPLICE_STATE_CHECKED; > + data->rc = 0; > + rc = 0; > + } > + break; > + case XSPLICE_ACTION_APPLY: > + if ( (data->state == XSPLICE_STATE_CHECKED) ) > + { > + /* No implementation yet. */ > + data->state = XSPLICE_STATE_APPLIED; > + data->rc = 0; > + rc = 0; > + } > + break; > + case XSPLICE_ACTION_REPLACE: > + if ( data->state == XSPLICE_STATE_CHECKED ) > + { > + /* No implementation yet. */ > + data->state = XSPLICE_STATE_CHECKED; > + data->rc = 0; > + rc = 0; > + } > + break; > + default: > + rc = -EOPNOTSUPP; > + break; > + } > + > + out: > + spin_unlock(&payload_list_lock); > + > + return rc; > +} > + > +int xsplice_control(xen_sysctl_xsplice_op_t *xsplice) > +{ > + int rc; > + > + if ( xsplice->pad != 0 ) > + return -EINVAL; > + > + switch ( xsplice->cmd ) > + { > + case XEN_SYSCTL_XSPLICE_UPLOAD: > + rc = xsplice_upload(&xsplice->u.upload); > + break; > + case XEN_SYSCTL_XSPLICE_GET: > + rc = xsplice_get(&xsplice->u.get); > + break; > + case XEN_SYSCTL_XSPLICE_LIST: > + rc = xsplice_list(&xsplice->u.list); > + break; > + case XEN_SYSCTL_XSPLICE_ACTION: > + rc = xsplice_action(&xsplice->u.action); > + break; > + default: > + rc = -EOPNOTSUPP; > + break; > + } > + > + return rc; > +} > + > +static int __init xsplice_init(void) > +{ > + register_keyhandler('x', xsplice_printall, "print xsplicing info", 1); > + return 0; > +} > +__initcall(xsplice_init); > diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h > index 96680eb..0b0b879 100644 > --- a/xen/include/public/sysctl.h > +++ b/xen/include/public/sysctl.h > @@ -766,6 +766,160 @@ struct xen_sysctl_tmem_op { > typedef struct xen_sysctl_tmem_op xen_sysctl_tmem_op_t; > DEFINE_XEN_GUEST_HANDLE(xen_sysctl_tmem_op_t); > > +/* > + * XEN_SYSCTL_XSPLICE_op > + * > + * Refer to the http://xenbits.xenproject.org/docs/unstable/misc/xsplice.html > + * for the design details of this hyprcall. > + */ > + > +/* > + * Structure describing an ELF payload. Uniquely identifies the > + * payload. Should be human readable. > + * Recommended length is upto XEN_XSPLICE_NAME_SIZE. > + */ > +#define XEN_XSPLICE_NAME_SIZE 128 > +struct xen_xsplice_name { > + XEN_GUEST_HANDLE_64(char) name; /* IN: pointer to name. */ > + uint16_t size; /* IN: size of name. May be upto > + XEN_XSPLICE_NAME_SIZE. */ > + uint16_t pad[3]; /* IN: MUST be zero. */ > +}; > +typedef struct xen_xsplice_name xen_xsplice_name_t; > +DEFINE_XEN_GUEST_HANDLE(xen_xsplice_name_t); > + > +/* > + * Upload a payload to the hypervisor. The payload is verified > + * against basic checks and if there are any issues the proper return code > + * will be returned. The payload is not applied at this time - that is > + * controlled by XEN_SYSCTL_XSPLICE_ACTION. > + * > + * The return value is zero if the payload was succesfully uploaded. > + * Otherwise an EXX return value is provided. Duplicate `name` are not > + * supported. > + * > + * The payload at this point is verified against the basic checks. > + * > + * The `payload` is the ELF payload as mentioned in the `Payload format` > + * section in the xSplice design document. > + */ > +#define XEN_SYSCTL_XSPLICE_UPLOAD 0 > +struct xen_sysctl_xsplice_upload { > + xen_xsplice_name_t name; /* IN, name of the patch. */ > + uint64_t size; /* IN, size of the ELF file. */ > + XEN_GUEST_HANDLE_64(uint8) payload; /* IN, the ELF file. */ > +}; > +typedef struct xen_sysctl_xsplice_upload xen_sysctl_xsplice_upload_t; > +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_upload_t); > + > +/* > + * Retrieve an status of an specific payload. > + * > + * Upon completion the `struct xen_xsplice_status` is updated. > + * > + * The return value is zero on success and XEN_EXX on failure. This operation > + * is synchronous and does not require preemption. > + */ > +#define XEN_SYSCTL_XSPLICE_GET 1 > + > +struct xen_xsplice_status { > +#define XSPLICE_STATE_LOADED 1 > +#define XSPLICE_STATE_CHECKED 2 > +#define XSPLICE_STATE_APPLIED 3 > + int32_t state; /* OUT: XSPLICE_STATE_*. IN: MUST be zero. */ > + int32_t rc; /* OUT: 0 if no error, otherwise -XEN_EXX. */ > + /* IN: MUST be zero. */ > +}; > +typedef struct xen_xsplice_status xen_xsplice_status_t; > +DEFINE_XEN_GUEST_HANDLE(xen_xsplice_status_t); > + > +struct xen_sysctl_xsplice_summary { > + xen_xsplice_name_t name; /* IN, name of the payload. */ > + xen_xsplice_status_t status; /* IN/OUT, state of it. */ > +}; > +typedef struct xen_sysctl_xsplice_summary xen_sysctl_xsplice_summary_t; > +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_summary_t); > + > +/* > + * Retrieve an array of abbreviated status and names of payloads that are > + * loaded in the hypervisor. > + * > + * If the hypercall returns an positive number, it is the number (up to `nr`) > + * of the payloads returned, along with `nr` updated with the number of remaining > + * payloads, `version` updated (it may be the same across hypercalls. If it > + * varies the data is stale and further calls could fail). The `status`, > + * `name`, and `len`' are updated at their designed index value (`idx`) with > + * the returned value of data. > + * > + * If the hypercall returns E2BIG the `nr` is too big and should be > + * lowered. > + * > + * This operation can be preempted by the hypercall returning EAGAIN. > + * Retry. > + * > + * Note that due to the asynchronous nature of hypercalls the domain might have > + * added or removed the number of payloads making this information stale. It is > + * the responsibility of the toolstack to use the `version` field to check > + * between each invocation. if the version differs it should discard the stale > + * data and start from scratch. It is OK for the toolstack to use the new > + * `version` field. > + */ > +#define XEN_SYSCTL_XSPLICE_LIST 2 > +struct xen_sysctl_xsplice_list { > + uint32_t version; /* IN/OUT: Initially *MUST* be zero. > + On subsequent calls reuse value. > + If varies between calls, we are > + * getting stale data. */ > + uint32_t idx; /* IN/OUT: Index into array. */ > + uint32_t nr; /* IN: How many status, id, and len > + should fill out. > + OUT: How many payloads left. */ > + uint32_t pad; /* IN: Must be zero. */ > + XEN_GUEST_HANDLE_64(xen_xsplice_status_t) status; /* OUT. Must have enough > + space allocate for nr of them. */ > + XEN_GUEST_HANDLE_64(char) name; /* OUT: Array of ids. Each member > + MUST XEN_XSPLICE_NAME_SIZE in size. > + Must have nr of them. */ > + XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of ids. > + Must have nr of them. */ > +}; > +typedef struct xen_sysctl_xsplice_list xen_sysctl_xsplice_list_t; > +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_list_t); > + > +/* > + * Perform an operation on the payload structure referenced by the `name` field. > + * The operation request is asynchronous and the status should be retrieved > + * by using either XEN_SYSCTL_XSPLICE_GET or XEN_SYSCTL_XSPLICE_LIST hypercall. > + */ > +#define XEN_SYSCTL_XSPLICE_ACTION 3 > +struct xen_sysctl_xsplice_action { > + xen_xsplice_name_t name; /* IN, name of the patch. */ > +#define XSPLICE_ACTION_CHECK 1 > +#define XSPLICE_ACTION_UNLOAD 2 > +#define XSPLICE_ACTION_REVERT 3 > +#define XSPLICE_ACTION_APPLY 4 > +#define XSPLICE_ACTION_REPLACE 5 > + uint32_t cmd; /* IN: XSPLICE_ACTION_*. */ > + uint32_t timeout; /* IN: Zero if no timeout. */ > + /* Or upper bound of time (ms) */ > + /* for operation to take. */ > +}; > +typedef struct xen_sysctl_xsplice_action xen_sysctl_xsplice_action_t; > +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_action_t); > + > +struct xen_sysctl_xsplice_op { > + uint32_t cmd; /* IN: XEN_SYSCTL_XSPLICE_*. */ > + uint32_t pad; /* IN: Always zero. */ > + union { > + xen_sysctl_xsplice_upload_t upload; > + xen_sysctl_xsplice_list_t list; > + xen_sysctl_xsplice_summary_t get; > + xen_sysctl_xsplice_action_t action; > + } u; > +}; > +typedef struct xen_sysctl_xsplice_op xen_sysctl_xsplice_op_t; > +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_op_t); > + > struct xen_sysctl { > uint32_t cmd; > #define XEN_SYSCTL_readconsole 1 > @@ -791,6 +945,7 @@ struct xen_sysctl { > #define XEN_SYSCTL_pcitopoinfo 22 > #define XEN_SYSCTL_psr_cat_op 23 > #define XEN_SYSCTL_tmem_op 24 > +#define XEN_SYSCTL_xsplice_op 25 > uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */ > union { > struct xen_sysctl_readconsole readconsole; > @@ -816,6 +971,7 @@ struct xen_sysctl { > struct xen_sysctl_psr_cmt_op psr_cmt_op; > struct xen_sysctl_psr_cat_op psr_cat_op; > struct xen_sysctl_tmem_op tmem_op; > + struct xen_sysctl_xsplice_op xsplice; > uint8_t pad[128]; > } u; > }; > diff --git a/xen/include/xen/xsplice.h b/xen/include/xen/xsplice.h > new file mode 100644 > index 0000000..2cb2035 > --- /dev/null > +++ b/xen/include/xen/xsplice.h > @@ -0,0 +1,7 @@ > +#ifndef __XEN_XSPLICE_H__ > +#define __XEN_XSPLICE_H__ > + > +struct xen_sysctl_xsplice_op; > +int xsplice_control(struct xen_sysctl_xsplice_op *); > + > +#endif /* __XEN_XSPLICE_H__ */ > diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c > index 9b7de30..5346dcf 100644 > --- a/xen/xsm/flask/hooks.c > +++ b/xen/xsm/flask/hooks.c > @@ -807,6 +807,12 @@ static int flask_sysctl(int cmd) > case XEN_SYSCTL_tmem_op: > return domain_has_xen(current->domain, XEN__TMEM_CONTROL); > > +#ifdef CONFIG_XSPLICE > + case XEN_SYSCTL_xsplice_op: > + return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2, > + XEN2__XSPLICE_OP, NULL); > +#endif > + > default: > printk("flask_sysctl: Unknown op %d\n", cmd); > return -EPERM; > diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors > index effb59f..5f08d05 100644 > --- a/xen/xsm/flask/policy/access_vectors > +++ b/xen/xsm/flask/policy/access_vectors > @@ -93,6 +93,8 @@ class xen2 > pmu_ctrl > # PMU use (domains, including unprivileged ones, will be using this operation) > pmu_use > +# XEN_SYSCTL_xsplice_op > + xsplice_op > } > > # Classes domain and domain2 consist of operations that a domain performs on >
>>> On 06.02.16 at 23:35, <cardoe@cardoe.com> wrote: (Just to demonstrate the effect - please go right to the end.) > On 1/14/16 3:47 PM, Konrad Rzeszutek Wilk wrote: >> The implementation does not actually do any patching. >> >> It just adds the framework for doing the hypercalls, >> keeping track of ELF payloads, and the basic operations: >> - query which payloads exist, >> - query for specific payloads, >> - check*1, apply*1, replace*1, and unload payloads. >> >> *1: Which of course in this patch are nops. >> >> Acked-by: Daniel De Graaf <dgdegra@tycho.nsa.gov> >> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> >> Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com> >> >> --- >> v2: Rebased on keyhandler: rework keyhandler infrastructure >> v3: Fixed XSM. >> v4: Removed REVERTED state. >> Split status and error code. >> Add REPLACE action. >> Separate payload data from the payload structure. >> s/XSPLICE_ID_../XSPLICE_NAME_../ >> v5: Add xsplice and CONFIG_XSPLICE build toption. >> Fix code per Jan's review. >> Update the sysctl.h (change bits to enum like) >> v6: Rebase on Kconfig changes. >> v7: Add missing pad checks. Re-order keyhandler.h to build on ARM. >> >> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> >> --- >> tools/flask/policy/policy/modules/xen/xen.te | 1 + >> xen/arch/arm/Kconfig | 1 + >> xen/arch/x86/Kconfig | 1 + >> xen/common/Kconfig | 14 + >> xen/common/Makefile | 2 + >> xen/common/sysctl.c | 8 + >> xen/common/xsplice.c | 386 > +++++++++++++++++++++++++++ >> xen/include/public/sysctl.h | 156 +++++++++++ >> xen/include/xen/xsplice.h | 7 + >> xen/xsm/flask/hooks.c | 6 + >> xen/xsm/flask/policy/access_vectors | 2 + >> 11 files changed, 584 insertions(+) >> create mode 100644 xen/common/xsplice.c >> create mode 100644 xen/include/xen/xsplice.h >> >> diff --git a/tools/flask/policy/policy/modules/xen/xen.te > b/tools/flask/policy/policy/modules/xen/xen.te >> index d35ae22..542c3e1 100644 >> --- a/tools/flask/policy/policy/modules/xen/xen.te >> +++ b/tools/flask/policy/policy/modules/xen/xen.te >> @@ -72,6 +72,7 @@ allow dom0_t xen_t:xen2 { >> allow dom0_t xen_t:xen2 { >> pmu_ctrl >> get_symbol >> + xsplice_op >> }; >> allow dom0_t xen_t:mmu memorymap; >> >> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig >> index 60e923c..3780949 100644 >> --- a/xen/arch/arm/Kconfig >> +++ b/xen/arch/arm/Kconfig >> @@ -23,6 +23,7 @@ config ARM >> select HAS_PASSTHROUGH >> select HAS_PDX >> select HAS_VIDEO >> + select HAS_XSPLICE >> >> config ARCH_DEFCONFIG >> string >> diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig >> index 4781b34..2b6c832 100644 >> --- a/xen/arch/x86/Kconfig >> +++ b/xen/arch/x86/Kconfig >> @@ -18,6 +18,7 @@ config X86 >> select HAS_PCI >> select HAS_PDX >> select HAS_VGA >> + select HAS_XSPLICE >> >> config ARCH_DEFCONFIG >> string >> diff --git a/xen/common/Kconfig b/xen/common/Kconfig >> index eadfc3b..aaf4053 100644 >> --- a/xen/common/Kconfig >> +++ b/xen/common/Kconfig >> @@ -51,6 +51,9 @@ config HAS_GDBSX >> config HAS_IOPORTS >> bool >> >> +config HAS_XSPLICE >> + bool >> + >> # Enable/Disable kexec support >> config KEXEC >> bool "kexec support" >> @@ -97,4 +100,15 @@ config XSM >> >> If unsure, say N. >> >> +# Enable/Disable xsplice support >> +config XSPLICE >> + bool "xsplice support" >> + default y >> + depends on HAS_XSPLICE >> + ---help--- >> + Allows a running Xen hypervisor to be patched without rebooting. >> + This is primarily used to patch an hypervisor with XSA fixes. >> + >> + If unsure, say Y. >> + >> endmenu > > I'm indifferent on the HAS_XSPLICE, you can drop that if you want to > simply stuff. > > >> diff --git a/xen/common/Makefile b/xen/common/Makefile >> index 9f8b214..6fdeccf 100644 >> --- a/xen/common/Makefile >> +++ b/xen/common/Makefile >> @@ -71,3 +71,5 @@ subdir-$(coverage) += gcov >> >> subdir-y += libelf >> subdir-$(CONFIG_HAS_DEVICE_TREE) += libfdt >> + >> +obj-$(CONFIG_XSPLICE) += xsplice.o >> diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c >> index a3007b8..55e6cfa 100644 >> --- a/xen/common/sysctl.c >> +++ b/xen/common/sysctl.c >> @@ -28,6 +28,7 @@ >> #include <xsm/xsm.h> >> #include <xen/pmstat.h> >> #include <xen/gcov.h> >> +#include <xen/xsplice.h> >> >> long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) >> { >> @@ -460,6 +461,13 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) > u_sysctl) >> ret = tmem_control(&op->u.tmem_op); >> break; >> >> +#ifdef CONFIG_XSPLICE >> + case XEN_SYSCTL_xsplice_op: >> + ret = xsplice_control(&op->u.xsplice); >> + copyback = 1; >> + break; >> +#endif > > Should the case statement still exist and not just return -ENOSYS? > Otherwise we're needlessly going into arch_do_sysctl() just to get the > same result. > > >> + >> default: >> ret = arch_do_sysctl(op, u_sysctl); >> copyback = 0; >> diff --git a/xen/common/xsplice.c b/xen/common/xsplice.c >> new file mode 100644 >> index 0000000..3c6acc3 >> --- /dev/null >> +++ b/xen/common/xsplice.c >> @@ -0,0 +1,386 @@ >> +/* >> + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. >> + * >> + */ >> + >> +#include <xen/guest_access.h> >> +#include <xen/keyhandler.h> >> +#include <xen/lib.h> >> +#include <xen/list.h> >> +#include <xen/mm.h> >> +#include <xen/sched.h> >> +#include <xen/smp.h> >> +#include <xen/spinlock.h> >> +#include <xen/xsplice.h> >> + >> +#include <asm/event.h> >> +#include <public/sysctl.h> >> + >> +static DEFINE_SPINLOCK(payload_list_lock); >> +static LIST_HEAD(payload_list); >> + >> +static unsigned int payload_cnt; >> +static unsigned int payload_version = 1; >> + >> +struct payload { >> + int32_t state; /* One of the XSPLICE_STATE_*. */ >> + int32_t rc; /* 0 or -XEN_EXX. */ >> + struct list_head list; /* Linked to 'payload_list'. */ >> + char name[XEN_XSPLICE_NAME_SIZE + 1];/* Name of it. */ >> +}; >> + >> +static const char *state2str(int32_t state) >> +{ >> +#define STATE(x) [XSPLICE_STATE_##x] = #x >> + static const char *const names[] = { >> + STATE(LOADED), >> + STATE(CHECKED), >> + STATE(APPLIED), >> + }; >> +#undef STATE >> + >> + if (state >= ARRAY_SIZE(names)) >> + return "unknown"; >> + >> + if (state < 0) >> + return "-EXX"; >> + >> + if (!names[state]) >> + return "unknown"; >> + >> + return names[state]; >> +} >> + >> +static void xsplice_printall(unsigned char key) >> +{ >> + struct payload *data; >> + >> + spin_lock(&payload_list_lock); >> + >> + list_for_each_entry ( data, &payload_list, list ) >> + printk(" name=%s state=%s(%d)\n", data->name, >> + state2str(data->state), data->state); >> + >> + spin_unlock(&payload_list_lock); >> +} >> + >> +static int verify_name(xen_xsplice_name_t *name) >> +{ >> + if ( name->size == 0 || name->size > XEN_XSPLICE_NAME_SIZE ) >> + return -EINVAL; >> + >> + if ( name->pad[0] || name->pad[1] || name->pad[2] ) >> + return -EINVAL; >> + >> + if ( !guest_handle_okay(name->name, name->size) ) >> + return -EINVAL; >> + >> + return 0; >> +} >> + >> +static int find_payload(xen_xsplice_name_t *name, bool_t need_lock, >> + struct payload **f) >> +{ >> + struct payload *data; >> + XEN_GUEST_HANDLE_PARAM(char) str; >> + char n[XEN_XSPLICE_NAME_SIZE + 1] = { 0 }; >> + int rc = -EINVAL; >> + >> + rc = verify_name(name); >> + if ( rc ) >> + return rc; >> + >> + str = guest_handle_cast(name->name, char); >> + if ( copy_from_guest(n, str, name->size) ) >> + return -EFAULT; >> + >> + if ( need_lock ) >> + spin_lock(&payload_list_lock); >> + >> + rc = -ENOENT; >> + list_for_each_entry ( data, &payload_list, list ) >> + { >> + if ( !strcmp(data->name, n) ) >> + { >> + *f = data; >> + rc = 0; >> + break; >> + } >> + } >> + >> + if ( need_lock ) >> + spin_unlock(&payload_list_lock); >> + >> + return rc; >> +} >> + >> +static int verify_payload(xen_sysctl_xsplice_upload_t *upload) >> +{ >> + if ( verify_name(&upload->name) ) >> + return -EINVAL; >> + >> + if ( upload->size == 0 ) >> + return -EINVAL; >> + >> + if ( !guest_handle_okay(upload->payload, upload->size) ) >> + return -EFAULT; >> + >> + return 0; >> +} >> + >> +/* >> + * We MUST be holding the payload_list_lock spinlock. >> + */ >> +static void free_payload(struct payload *data) >> +{ >> + list_del(&data->list); >> + payload_cnt--; >> + payload_version++; >> + xfree(data); >> +} >> + >> +static int xsplice_upload(xen_sysctl_xsplice_upload_t *upload) >> +{ >> + struct payload *data = NULL; >> + uint8_t *raw_data; >> + int rc; >> + >> + rc = verify_payload(upload); >> + if ( rc ) >> + return rc; >> + >> + rc = find_payload(&upload->name, 1 /* true. */, &data); >> + if ( rc == 0 /* Found. */ ) >> + return -EEXIST; >> + >> + if ( rc != -ENOENT ) >> + return rc; >> + >> + data = xzalloc(struct payload); >> + if ( !data ) >> + return -ENOMEM; >> + >> + memset(data, 0, sizeof *data); >> + rc = -EFAULT; >> + if ( copy_from_guest(data->name, upload->name.name, upload->name.size) ) >> + goto err_data; >> + >> + rc = -ENOMEM; >> + raw_data = alloc_xenheap_pages(get_order_from_bytes(upload->size), 0); >> + if ( !raw_data ) >> + goto err_data; >> + >> + rc = -EFAULT; >> + if ( copy_from_guest(raw_data, upload->payload, upload->size) ) >> + goto err_raw; >> + >> + data->state = XSPLICE_STATE_LOADED; >> + data->rc = 0; >> + INIT_LIST_HEAD(&data->list); >> + >> + spin_lock(&payload_list_lock); >> + list_add_tail(&data->list, &payload_list); >> + payload_cnt++; >> + payload_version++; >> + spin_unlock(&payload_list_lock); >> + >> + free_xenheap_pages(raw_data, get_order_from_bytes(upload->size)); >> + return 0; >> + >> + err_raw: >> + free_xenheap_pages(raw_data, get_order_from_bytes(upload->size)); >> + err_data: >> + xfree(data); >> + return rc; >> +} >> + >> +static int xsplice_get(xen_sysctl_xsplice_summary_t *summary) >> +{ >> + struct payload *data; >> + int rc; >> + >> + if ( summary->status.state ) >> + return -EINVAL; >> + >> + if ( summary->status.rc != 0 ) >> + return -EINVAL; >> + >> + rc = verify_name(&summary->name); >> + if ( rc ) >> + return rc; >> + >> + rc = find_payload(&summary->name, 1 /* true. */, &data); >> + if ( rc ) >> + return rc; >> + >> + summary->status.state = data->state; >> + summary->status.rc = data->rc; >> + >> + return 0; >> +} >> + >> +static int xsplice_list(xen_sysctl_xsplice_list_t *list) >> +{ >> + xen_xsplice_status_t status; >> + struct payload *data; >> + unsigned int idx = 0, i = 0; >> + int rc = 0; >> + >> + if ( list->nr > 1024 ) >> + return -E2BIG; >> + >> + if ( list->pad != 0 ) >> + return -EINVAL; >> + >> + if ( !guest_handle_okay(list->status, sizeof(status) * list->nr) || >> + !guest_handle_okay(list->name, XEN_XSPLICE_NAME_SIZE * list->nr) || >> + !guest_handle_okay(list->len, sizeof(uint32_t) * list->nr) ) >> + return -EINVAL; >> + >> + spin_lock(&payload_list_lock); >> + if ( list->idx > payload_cnt || !list->nr ) >> + { >> + spin_unlock(&payload_list_lock); >> + return -EINVAL; >> + } >> + >> + list_for_each_entry( data, &payload_list, list ) >> + { >> + uint32_t len; >> + >> + if ( list->idx > i++ ) >> + continue; >> + >> + status.state = data->state; >> + status.rc = data->rc; >> + len = strlen(data->name); >> + >> + /* N.B. 'idx' != 'i'. */ >> + if ( __copy_to_guest_offset(list->name, idx * XEN_XSPLICE_NAME_SIZE, >> + data->name, len) || >> + __copy_to_guest_offset(list->len, idx, &len, 1) || >> + __copy_to_guest_offset(list->status, idx, &status, 1) ) >> + { >> + rc = -EFAULT; >> + break; >> + } >> + idx++; >> + if ( hypercall_preempt_check() || (idx + 1 > list->nr) ) >> + break; >> + } >> + list->nr = payload_cnt - i; /* Remaining amount. */ >> + list->version = payload_version; >> + spin_unlock(&payload_list_lock); >> + >> + /* And how many we have processed. */ >> + return rc ? : idx; >> +} >> + >> +static int xsplice_action(xen_sysctl_xsplice_action_t *action) >> +{ >> + struct payload *data; >> + int rc; >> + >> + rc = verify_name(&action->name); >> + if ( rc ) >> + return rc; >> + >> + spin_lock(&payload_list_lock); >> + rc = find_payload(&action->name, 0 /* We are holding the lock. */, &data); >> + if ( rc ) >> + goto out; >> + >> + switch ( action->cmd ) >> + { >> + case XSPLICE_ACTION_CHECK: >> + if ( (data->state == XSPLICE_STATE_LOADED) || >> + (data->state == XSPLICE_STATE_CHECKED) ) >> + { >> + /* No implementation yet. */ >> + data->state = XSPLICE_STATE_CHECKED; >> + data->rc = 0; >> + rc = 0; >> + } >> + break; >> + case XSPLICE_ACTION_UNLOAD: >> + if ( (data->state == XSPLICE_STATE_LOADED) || >> + (data->state == XSPLICE_STATE_CHECKED) ) >> + { >> + free_payload(data); >> + /* No touching 'data' from here on! */ >> + rc = 0; >> + } >> + break; >> + case XSPLICE_ACTION_REVERT: >> + if ( data->state == XSPLICE_STATE_APPLIED ) >> + { >> + /* No implementation yet. */ >> + data->state = XSPLICE_STATE_CHECKED; >> + data->rc = 0; >> + rc = 0; >> + } >> + break; >> + case XSPLICE_ACTION_APPLY: >> + if ( (data->state == XSPLICE_STATE_CHECKED) ) >> + { >> + /* No implementation yet. */ >> + data->state = XSPLICE_STATE_APPLIED; >> + data->rc = 0; >> + rc = 0; >> + } >> + break; >> + case XSPLICE_ACTION_REPLACE: >> + if ( data->state == XSPLICE_STATE_CHECKED ) >> + { >> + /* No implementation yet. */ >> + data->state = XSPLICE_STATE_CHECKED; >> + data->rc = 0; >> + rc = 0; >> + } >> + break; >> + default: >> + rc = -EOPNOTSUPP; >> + break; >> + } >> + >> + out: >> + spin_unlock(&payload_list_lock); >> + >> + return rc; >> +} >> + >> +int xsplice_control(xen_sysctl_xsplice_op_t *xsplice) >> +{ >> + int rc; >> + >> + if ( xsplice->pad != 0 ) >> + return -EINVAL; >> + >> + switch ( xsplice->cmd ) >> + { >> + case XEN_SYSCTL_XSPLICE_UPLOAD: >> + rc = xsplice_upload(&xsplice->u.upload); >> + break; >> + case XEN_SYSCTL_XSPLICE_GET: >> + rc = xsplice_get(&xsplice->u.get); >> + break; >> + case XEN_SYSCTL_XSPLICE_LIST: >> + rc = xsplice_list(&xsplice->u.list); >> + break; >> + case XEN_SYSCTL_XSPLICE_ACTION: >> + rc = xsplice_action(&xsplice->u.action); >> + break; >> + default: >> + rc = -EOPNOTSUPP; >> + break; >> + } >> + >> + return rc; >> +} >> + >> +static int __init xsplice_init(void) >> +{ >> + register_keyhandler('x', xsplice_printall, "print xsplicing info", 1); >> + return 0; >> +} >> +__initcall(xsplice_init); >> diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h >> index 96680eb..0b0b879 100644 >> --- a/xen/include/public/sysctl.h >> +++ b/xen/include/public/sysctl.h >> @@ -766,6 +766,160 @@ struct xen_sysctl_tmem_op { >> typedef struct xen_sysctl_tmem_op xen_sysctl_tmem_op_t; >> DEFINE_XEN_GUEST_HANDLE(xen_sysctl_tmem_op_t); >> >> +/* >> + * XEN_SYSCTL_XSPLICE_op >> + * >> + * Refer to the > http://xenbits.xenproject.org/docs/unstable/misc/xsplice.html >> + * for the design details of this hyprcall. >> + */ >> + >> +/* >> + * Structure describing an ELF payload. Uniquely identifies the >> + * payload. Should be human readable. >> + * Recommended length is upto XEN_XSPLICE_NAME_SIZE. >> + */ >> +#define XEN_XSPLICE_NAME_SIZE 128 >> +struct xen_xsplice_name { >> + XEN_GUEST_HANDLE_64(char) name; /* IN: pointer to name. */ >> + uint16_t size; /* IN: size of name. May be > upto >> + XEN_XSPLICE_NAME_SIZE. */ >> + uint16_t pad[3]; /* IN: MUST be zero. */ >> +}; >> +typedef struct xen_xsplice_name xen_xsplice_name_t; >> +DEFINE_XEN_GUEST_HANDLE(xen_xsplice_name_t); >> + >> +/* >> + * Upload a payload to the hypervisor. The payload is verified >> + * against basic checks and if there are any issues the proper return code >> + * will be returned. The payload is not applied at this time - that is >> + * controlled by XEN_SYSCTL_XSPLICE_ACTION. >> + * >> + * The return value is zero if the payload was succesfully uploaded. >> + * Otherwise an EXX return value is provided. Duplicate `name` are not >> + * supported. >> + * >> + * The payload at this point is verified against the basic checks. >> + * >> + * The `payload` is the ELF payload as mentioned in the `Payload format` >> + * section in the xSplice design document. >> + */ >> +#define XEN_SYSCTL_XSPLICE_UPLOAD 0 >> +struct xen_sysctl_xsplice_upload { >> + xen_xsplice_name_t name; /* IN, name of the patch. */ >> + uint64_t size; /* IN, size of the ELF file. */ >> + XEN_GUEST_HANDLE_64(uint8) payload; /* IN, the ELF file. */ >> +}; >> +typedef struct xen_sysctl_xsplice_upload xen_sysctl_xsplice_upload_t; >> +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_upload_t); >> + >> +/* >> + * Retrieve an status of an specific payload. >> + * >> + * Upon completion the `struct xen_xsplice_status` is updated. >> + * >> + * The return value is zero on success and XEN_EXX on failure. This > operation >> + * is synchronous and does not require preemption. >> + */ >> +#define XEN_SYSCTL_XSPLICE_GET 1 >> + >> +struct xen_xsplice_status { >> +#define XSPLICE_STATE_LOADED 1 >> +#define XSPLICE_STATE_CHECKED 2 >> +#define XSPLICE_STATE_APPLIED 3 >> + int32_t state; /* OUT: XSPLICE_STATE_*. IN: MUST be > zero. */ >> + int32_t rc; /* OUT: 0 if no error, otherwise > -XEN_EXX. */ >> + /* IN: MUST be zero. */ >> +}; >> +typedef struct xen_xsplice_status xen_xsplice_status_t; >> +DEFINE_XEN_GUEST_HANDLE(xen_xsplice_status_t); >> + >> +struct xen_sysctl_xsplice_summary { >> + xen_xsplice_name_t name; /* IN, name of the payload. */ >> + xen_xsplice_status_t status; /* IN/OUT, state of it. */ >> +}; >> +typedef struct xen_sysctl_xsplice_summary xen_sysctl_xsplice_summary_t; >> +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_summary_t); >> + >> +/* >> + * Retrieve an array of abbreviated status and names of payloads that are >> + * loaded in the hypervisor. >> + * >> + * If the hypercall returns an positive number, it is the number (up to > `nr`) >> + * of the payloads returned, along with `nr` updated with the number of > remaining >> + * payloads, `version` updated (it may be the same across hypercalls. If it >> + * varies the data is stale and further calls could fail). The `status`, >> + * `name`, and `len`' are updated at their designed index value (`idx`) > with >> + * the returned value of data. >> + * >> + * If the hypercall returns E2BIG the `nr` is too big and should be >> + * lowered. >> + * >> + * This operation can be preempted by the hypercall returning EAGAIN. >> + * Retry. >> + * >> + * Note that due to the asynchronous nature of hypercalls the domain might > have >> + * added or removed the number of payloads making this information stale. > It is >> + * the responsibility of the toolstack to use the `version` field to check >> + * between each invocation. if the version differs it should discard the > stale >> + * data and start from scratch. It is OK for the toolstack to use the new >> + * `version` field. >> + */ >> +#define XEN_SYSCTL_XSPLICE_LIST 2 >> +struct xen_sysctl_xsplice_list { >> + uint32_t version; /* IN/OUT: Initially *MUST* be > zero. >> + On subsequent calls reuse > value. >> + If varies between calls, we > are >> + * getting stale data. */ >> + uint32_t idx; /* IN/OUT: Index into array. */ >> + uint32_t nr; /* IN: How many status, id, and > len >> + should fill out. >> + OUT: How many payloads left. > */ >> + uint32_t pad; /* IN: Must be zero. */ >> + XEN_GUEST_HANDLE_64(xen_xsplice_status_t) status; /* OUT. Must have > enough >> + space allocate for nr of > them. */ >> + XEN_GUEST_HANDLE_64(char) name; /* OUT: Array of ids. Each > member >> + MUST XEN_XSPLICE_NAME_SIZE > in size. >> + Must have nr of them. */ >> + XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of > ids. >> + Must have nr of them. */ >> +}; >> +typedef struct xen_sysctl_xsplice_list xen_sysctl_xsplice_list_t; >> +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_list_t); >> + >> +/* >> + * Perform an operation on the payload structure referenced by the `name` > field. >> + * The operation request is asynchronous and the status should be retrieved >> + * by using either XEN_SYSCTL_XSPLICE_GET or XEN_SYSCTL_XSPLICE_LIST > hypercall. >> + */ >> +#define XEN_SYSCTL_XSPLICE_ACTION 3 >> +struct xen_sysctl_xsplice_action { >> + xen_xsplice_name_t name; /* IN, name of the patch. */ >> +#define XSPLICE_ACTION_CHECK 1 >> +#define XSPLICE_ACTION_UNLOAD 2 >> +#define XSPLICE_ACTION_REVERT 3 >> +#define XSPLICE_ACTION_APPLY 4 >> +#define XSPLICE_ACTION_REPLACE 5 >> + uint32_t cmd; /* IN: XSPLICE_ACTION_*. */ >> + uint32_t timeout; /* IN: Zero if no timeout. */ >> + /* Or upper bound of time (ms) > */ >> + /* for operation to take. */ >> +}; >> +typedef struct xen_sysctl_xsplice_action xen_sysctl_xsplice_action_t; >> +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_action_t); >> + >> +struct xen_sysctl_xsplice_op { >> + uint32_t cmd; /* IN: XEN_SYSCTL_XSPLICE_*. */ >> + uint32_t pad; /* IN: Always zero. */ >> + union { >> + xen_sysctl_xsplice_upload_t upload; >> + xen_sysctl_xsplice_list_t list; >> + xen_sysctl_xsplice_summary_t get; >> + xen_sysctl_xsplice_action_t action; >> + } u; >> +}; >> +typedef struct xen_sysctl_xsplice_op xen_sysctl_xsplice_op_t; >> +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_op_t); >> + >> struct xen_sysctl { >> uint32_t cmd; >> #define XEN_SYSCTL_readconsole 1 >> @@ -791,6 +945,7 @@ struct xen_sysctl { >> #define XEN_SYSCTL_pcitopoinfo 22 >> #define XEN_SYSCTL_psr_cat_op 23 >> #define XEN_SYSCTL_tmem_op 24 >> +#define XEN_SYSCTL_xsplice_op 25 >> uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */ >> union { >> struct xen_sysctl_readconsole readconsole; >> @@ -816,6 +971,7 @@ struct xen_sysctl { >> struct xen_sysctl_psr_cmt_op psr_cmt_op; >> struct xen_sysctl_psr_cat_op psr_cat_op; >> struct xen_sysctl_tmem_op tmem_op; >> + struct xen_sysctl_xsplice_op xsplice; >> uint8_t pad[128]; >> } u; >> }; >> diff --git a/xen/include/xen/xsplice.h b/xen/include/xen/xsplice.h >> new file mode 100644 >> index 0000000..2cb2035 >> --- /dev/null >> +++ b/xen/include/xen/xsplice.h >> @@ -0,0 +1,7 @@ >> +#ifndef __XEN_XSPLICE_H__ >> +#define __XEN_XSPLICE_H__ >> + >> +struct xen_sysctl_xsplice_op; >> +int xsplice_control(struct xen_sysctl_xsplice_op *); >> + >> +#endif /* __XEN_XSPLICE_H__ */ >> diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c >> index 9b7de30..5346dcf 100644 >> --- a/xen/xsm/flask/hooks.c >> +++ b/xen/xsm/flask/hooks.c >> @@ -807,6 +807,12 @@ static int flask_sysctl(int cmd) >> case XEN_SYSCTL_tmem_op: >> return domain_has_xen(current->domain, XEN__TMEM_CONTROL); >> >> +#ifdef CONFIG_XSPLICE >> + case XEN_SYSCTL_xsplice_op: >> + return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2, >> + XEN2__XSPLICE_OP, NULL); >> +#endif >> + >> default: >> printk("flask_sysctl: Unknown op %d\n", cmd); >> return -EPERM; >> diff --git a/xen/xsm/flask/policy/access_vectors > b/xen/xsm/flask/policy/access_vectors >> index effb59f..5f08d05 100644 >> --- a/xen/xsm/flask/policy/access_vectors >> +++ b/xen/xsm/flask/policy/access_vectors >> @@ -93,6 +93,8 @@ class xen2 >> pmu_ctrl >> # PMU use (domains, including unprivileged ones, will be using this > operation) >> pmu_use >> +# XEN_SYSCTL_xsplice_op >> + xsplice_op >> } >> >> # Classes domain and domain2 consist of operations that a domain performs > on >> > > > -- > Doug Goldstein Please trim your replies. Thanks, Jan
diff --git a/tools/flask/policy/policy/modules/xen/xen.te b/tools/flask/policy/policy/modules/xen/xen.te index d35ae22..542c3e1 100644 --- a/tools/flask/policy/policy/modules/xen/xen.te +++ b/tools/flask/policy/policy/modules/xen/xen.te @@ -72,6 +72,7 @@ allow dom0_t xen_t:xen2 { allow dom0_t xen_t:xen2 { pmu_ctrl get_symbol + xsplice_op }; allow dom0_t xen_t:mmu memorymap; diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index 60e923c..3780949 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -23,6 +23,7 @@ config ARM select HAS_PASSTHROUGH select HAS_PDX select HAS_VIDEO + select HAS_XSPLICE config ARCH_DEFCONFIG string diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig index 4781b34..2b6c832 100644 --- a/xen/arch/x86/Kconfig +++ b/xen/arch/x86/Kconfig @@ -18,6 +18,7 @@ config X86 select HAS_PCI select HAS_PDX select HAS_VGA + select HAS_XSPLICE config ARCH_DEFCONFIG string diff --git a/xen/common/Kconfig b/xen/common/Kconfig index eadfc3b..aaf4053 100644 --- a/xen/common/Kconfig +++ b/xen/common/Kconfig @@ -51,6 +51,9 @@ config HAS_GDBSX config HAS_IOPORTS bool +config HAS_XSPLICE + bool + # Enable/Disable kexec support config KEXEC bool "kexec support" @@ -97,4 +100,15 @@ config XSM If unsure, say N. +# Enable/Disable xsplice support +config XSPLICE + bool "xsplice support" + default y + depends on HAS_XSPLICE + ---help--- + Allows a running Xen hypervisor to be patched without rebooting. + This is primarily used to patch an hypervisor with XSA fixes. + + If unsure, say Y. + endmenu diff --git a/xen/common/Makefile b/xen/common/Makefile index 9f8b214..6fdeccf 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -71,3 +71,5 @@ subdir-$(coverage) += gcov subdir-y += libelf subdir-$(CONFIG_HAS_DEVICE_TREE) += libfdt + +obj-$(CONFIG_XSPLICE) += xsplice.o diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c index a3007b8..55e6cfa 100644 --- a/xen/common/sysctl.c +++ b/xen/common/sysctl.c @@ -28,6 +28,7 @@ #include <xsm/xsm.h> #include <xen/pmstat.h> #include <xen/gcov.h> +#include <xen/xsplice.h> long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) { @@ -460,6 +461,13 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) ret = tmem_control(&op->u.tmem_op); break; +#ifdef CONFIG_XSPLICE + case XEN_SYSCTL_xsplice_op: + ret = xsplice_control(&op->u.xsplice); + copyback = 1; + break; +#endif + default: ret = arch_do_sysctl(op, u_sysctl); copyback = 0; diff --git a/xen/common/xsplice.c b/xen/common/xsplice.c new file mode 100644 index 0000000..3c6acc3 --- /dev/null +++ b/xen/common/xsplice.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. + * + */ + +#include <xen/guest_access.h> +#include <xen/keyhandler.h> +#include <xen/lib.h> +#include <xen/list.h> +#include <xen/mm.h> +#include <xen/sched.h> +#include <xen/smp.h> +#include <xen/spinlock.h> +#include <xen/xsplice.h> + +#include <asm/event.h> +#include <public/sysctl.h> + +static DEFINE_SPINLOCK(payload_list_lock); +static LIST_HEAD(payload_list); + +static unsigned int payload_cnt; +static unsigned int payload_version = 1; + +struct payload { + int32_t state; /* One of the XSPLICE_STATE_*. */ + int32_t rc; /* 0 or -XEN_EXX. */ + struct list_head list; /* Linked to 'payload_list'. */ + char name[XEN_XSPLICE_NAME_SIZE + 1];/* Name of it. */ +}; + +static const char *state2str(int32_t state) +{ +#define STATE(x) [XSPLICE_STATE_##x] = #x + static const char *const names[] = { + STATE(LOADED), + STATE(CHECKED), + STATE(APPLIED), + }; +#undef STATE + + if (state >= ARRAY_SIZE(names)) + return "unknown"; + + if (state < 0) + return "-EXX"; + + if (!names[state]) + return "unknown"; + + return names[state]; +} + +static void xsplice_printall(unsigned char key) +{ + struct payload *data; + + spin_lock(&payload_list_lock); + + list_for_each_entry ( data, &payload_list, list ) + printk(" name=%s state=%s(%d)\n", data->name, + state2str(data->state), data->state); + + spin_unlock(&payload_list_lock); +} + +static int verify_name(xen_xsplice_name_t *name) +{ + if ( name->size == 0 || name->size > XEN_XSPLICE_NAME_SIZE ) + return -EINVAL; + + if ( name->pad[0] || name->pad[1] || name->pad[2] ) + return -EINVAL; + + if ( !guest_handle_okay(name->name, name->size) ) + return -EINVAL; + + return 0; +} + +static int find_payload(xen_xsplice_name_t *name, bool_t need_lock, + struct payload **f) +{ + struct payload *data; + XEN_GUEST_HANDLE_PARAM(char) str; + char n[XEN_XSPLICE_NAME_SIZE + 1] = { 0 }; + int rc = -EINVAL; + + rc = verify_name(name); + if ( rc ) + return rc; + + str = guest_handle_cast(name->name, char); + if ( copy_from_guest(n, str, name->size) ) + return -EFAULT; + + if ( need_lock ) + spin_lock(&payload_list_lock); + + rc = -ENOENT; + list_for_each_entry ( data, &payload_list, list ) + { + if ( !strcmp(data->name, n) ) + { + *f = data; + rc = 0; + break; + } + } + + if ( need_lock ) + spin_unlock(&payload_list_lock); + + return rc; +} + +static int verify_payload(xen_sysctl_xsplice_upload_t *upload) +{ + if ( verify_name(&upload->name) ) + return -EINVAL; + + if ( upload->size == 0 ) + return -EINVAL; + + if ( !guest_handle_okay(upload->payload, upload->size) ) + return -EFAULT; + + return 0; +} + +/* + * We MUST be holding the payload_list_lock spinlock. + */ +static void free_payload(struct payload *data) +{ + list_del(&data->list); + payload_cnt--; + payload_version++; + xfree(data); +} + +static int xsplice_upload(xen_sysctl_xsplice_upload_t *upload) +{ + struct payload *data = NULL; + uint8_t *raw_data; + int rc; + + rc = verify_payload(upload); + if ( rc ) + return rc; + + rc = find_payload(&upload->name, 1 /* true. */, &data); + if ( rc == 0 /* Found. */ ) + return -EEXIST; + + if ( rc != -ENOENT ) + return rc; + + data = xzalloc(struct payload); + if ( !data ) + return -ENOMEM; + + memset(data, 0, sizeof *data); + rc = -EFAULT; + if ( copy_from_guest(data->name, upload->name.name, upload->name.size) ) + goto err_data; + + rc = -ENOMEM; + raw_data = alloc_xenheap_pages(get_order_from_bytes(upload->size), 0); + if ( !raw_data ) + goto err_data; + + rc = -EFAULT; + if ( copy_from_guest(raw_data, upload->payload, upload->size) ) + goto err_raw; + + data->state = XSPLICE_STATE_LOADED; + data->rc = 0; + INIT_LIST_HEAD(&data->list); + + spin_lock(&payload_list_lock); + list_add_tail(&data->list, &payload_list); + payload_cnt++; + payload_version++; + spin_unlock(&payload_list_lock); + + free_xenheap_pages(raw_data, get_order_from_bytes(upload->size)); + return 0; + + err_raw: + free_xenheap_pages(raw_data, get_order_from_bytes(upload->size)); + err_data: + xfree(data); + return rc; +} + +static int xsplice_get(xen_sysctl_xsplice_summary_t *summary) +{ + struct payload *data; + int rc; + + if ( summary->status.state ) + return -EINVAL; + + if ( summary->status.rc != 0 ) + return -EINVAL; + + rc = verify_name(&summary->name); + if ( rc ) + return rc; + + rc = find_payload(&summary->name, 1 /* true. */, &data); + if ( rc ) + return rc; + + summary->status.state = data->state; + summary->status.rc = data->rc; + + return 0; +} + +static int xsplice_list(xen_sysctl_xsplice_list_t *list) +{ + xen_xsplice_status_t status; + struct payload *data; + unsigned int idx = 0, i = 0; + int rc = 0; + + if ( list->nr > 1024 ) + return -E2BIG; + + if ( list->pad != 0 ) + return -EINVAL; + + if ( !guest_handle_okay(list->status, sizeof(status) * list->nr) || + !guest_handle_okay(list->name, XEN_XSPLICE_NAME_SIZE * list->nr) || + !guest_handle_okay(list->len, sizeof(uint32_t) * list->nr) ) + return -EINVAL; + + spin_lock(&payload_list_lock); + if ( list->idx > payload_cnt || !list->nr ) + { + spin_unlock(&payload_list_lock); + return -EINVAL; + } + + list_for_each_entry( data, &payload_list, list ) + { + uint32_t len; + + if ( list->idx > i++ ) + continue; + + status.state = data->state; + status.rc = data->rc; + len = strlen(data->name); + + /* N.B. 'idx' != 'i'. */ + if ( __copy_to_guest_offset(list->name, idx * XEN_XSPLICE_NAME_SIZE, + data->name, len) || + __copy_to_guest_offset(list->len, idx, &len, 1) || + __copy_to_guest_offset(list->status, idx, &status, 1) ) + { + rc = -EFAULT; + break; + } + idx++; + if ( hypercall_preempt_check() || (idx + 1 > list->nr) ) + break; + } + list->nr = payload_cnt - i; /* Remaining amount. */ + list->version = payload_version; + spin_unlock(&payload_list_lock); + + /* And how many we have processed. */ + return rc ? : idx; +} + +static int xsplice_action(xen_sysctl_xsplice_action_t *action) +{ + struct payload *data; + int rc; + + rc = verify_name(&action->name); + if ( rc ) + return rc; + + spin_lock(&payload_list_lock); + rc = find_payload(&action->name, 0 /* We are holding the lock. */, &data); + if ( rc ) + goto out; + + switch ( action->cmd ) + { + case XSPLICE_ACTION_CHECK: + if ( (data->state == XSPLICE_STATE_LOADED) || + (data->state == XSPLICE_STATE_CHECKED) ) + { + /* No implementation yet. */ + data->state = XSPLICE_STATE_CHECKED; + data->rc = 0; + rc = 0; + } + break; + case XSPLICE_ACTION_UNLOAD: + if ( (data->state == XSPLICE_STATE_LOADED) || + (data->state == XSPLICE_STATE_CHECKED) ) + { + free_payload(data); + /* No touching 'data' from here on! */ + rc = 0; + } + break; + case XSPLICE_ACTION_REVERT: + if ( data->state == XSPLICE_STATE_APPLIED ) + { + /* No implementation yet. */ + data->state = XSPLICE_STATE_CHECKED; + data->rc = 0; + rc = 0; + } + break; + case XSPLICE_ACTION_APPLY: + if ( (data->state == XSPLICE_STATE_CHECKED) ) + { + /* No implementation yet. */ + data->state = XSPLICE_STATE_APPLIED; + data->rc = 0; + rc = 0; + } + break; + case XSPLICE_ACTION_REPLACE: + if ( data->state == XSPLICE_STATE_CHECKED ) + { + /* No implementation yet. */ + data->state = XSPLICE_STATE_CHECKED; + data->rc = 0; + rc = 0; + } + break; + default: + rc = -EOPNOTSUPP; + break; + } + + out: + spin_unlock(&payload_list_lock); + + return rc; +} + +int xsplice_control(xen_sysctl_xsplice_op_t *xsplice) +{ + int rc; + + if ( xsplice->pad != 0 ) + return -EINVAL; + + switch ( xsplice->cmd ) + { + case XEN_SYSCTL_XSPLICE_UPLOAD: + rc = xsplice_upload(&xsplice->u.upload); + break; + case XEN_SYSCTL_XSPLICE_GET: + rc = xsplice_get(&xsplice->u.get); + break; + case XEN_SYSCTL_XSPLICE_LIST: + rc = xsplice_list(&xsplice->u.list); + break; + case XEN_SYSCTL_XSPLICE_ACTION: + rc = xsplice_action(&xsplice->u.action); + break; + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +static int __init xsplice_init(void) +{ + register_keyhandler('x', xsplice_printall, "print xsplicing info", 1); + return 0; +} +__initcall(xsplice_init); diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index 96680eb..0b0b879 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.h @@ -766,6 +766,160 @@ struct xen_sysctl_tmem_op { typedef struct xen_sysctl_tmem_op xen_sysctl_tmem_op_t; DEFINE_XEN_GUEST_HANDLE(xen_sysctl_tmem_op_t); +/* + * XEN_SYSCTL_XSPLICE_op + * + * Refer to the http://xenbits.xenproject.org/docs/unstable/misc/xsplice.html + * for the design details of this hyprcall. + */ + +/* + * Structure describing an ELF payload. Uniquely identifies the + * payload. Should be human readable. + * Recommended length is upto XEN_XSPLICE_NAME_SIZE. + */ +#define XEN_XSPLICE_NAME_SIZE 128 +struct xen_xsplice_name { + XEN_GUEST_HANDLE_64(char) name; /* IN: pointer to name. */ + uint16_t size; /* IN: size of name. May be upto + XEN_XSPLICE_NAME_SIZE. */ + uint16_t pad[3]; /* IN: MUST be zero. */ +}; +typedef struct xen_xsplice_name xen_xsplice_name_t; +DEFINE_XEN_GUEST_HANDLE(xen_xsplice_name_t); + +/* + * Upload a payload to the hypervisor. The payload is verified + * against basic checks and if there are any issues the proper return code + * will be returned. The payload is not applied at this time - that is + * controlled by XEN_SYSCTL_XSPLICE_ACTION. + * + * The return value is zero if the payload was succesfully uploaded. + * Otherwise an EXX return value is provided. Duplicate `name` are not + * supported. + * + * The payload at this point is verified against the basic checks. + * + * The `payload` is the ELF payload as mentioned in the `Payload format` + * section in the xSplice design document. + */ +#define XEN_SYSCTL_XSPLICE_UPLOAD 0 +struct xen_sysctl_xsplice_upload { + xen_xsplice_name_t name; /* IN, name of the patch. */ + uint64_t size; /* IN, size of the ELF file. */ + XEN_GUEST_HANDLE_64(uint8) payload; /* IN, the ELF file. */ +}; +typedef struct xen_sysctl_xsplice_upload xen_sysctl_xsplice_upload_t; +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_upload_t); + +/* + * Retrieve an status of an specific payload. + * + * Upon completion the `struct xen_xsplice_status` is updated. + * + * The return value is zero on success and XEN_EXX on failure. This operation + * is synchronous and does not require preemption. + */ +#define XEN_SYSCTL_XSPLICE_GET 1 + +struct xen_xsplice_status { +#define XSPLICE_STATE_LOADED 1 +#define XSPLICE_STATE_CHECKED 2 +#define XSPLICE_STATE_APPLIED 3 + int32_t state; /* OUT: XSPLICE_STATE_*. IN: MUST be zero. */ + int32_t rc; /* OUT: 0 if no error, otherwise -XEN_EXX. */ + /* IN: MUST be zero. */ +}; +typedef struct xen_xsplice_status xen_xsplice_status_t; +DEFINE_XEN_GUEST_HANDLE(xen_xsplice_status_t); + +struct xen_sysctl_xsplice_summary { + xen_xsplice_name_t name; /* IN, name of the payload. */ + xen_xsplice_status_t status; /* IN/OUT, state of it. */ +}; +typedef struct xen_sysctl_xsplice_summary xen_sysctl_xsplice_summary_t; +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_summary_t); + +/* + * Retrieve an array of abbreviated status and names of payloads that are + * loaded in the hypervisor. + * + * If the hypercall returns an positive number, it is the number (up to `nr`) + * of the payloads returned, along with `nr` updated with the number of remaining + * payloads, `version` updated (it may be the same across hypercalls. If it + * varies the data is stale and further calls could fail). The `status`, + * `name`, and `len`' are updated at their designed index value (`idx`) with + * the returned value of data. + * + * If the hypercall returns E2BIG the `nr` is too big and should be + * lowered. + * + * This operation can be preempted by the hypercall returning EAGAIN. + * Retry. + * + * Note that due to the asynchronous nature of hypercalls the domain might have + * added or removed the number of payloads making this information stale. It is + * the responsibility of the toolstack to use the `version` field to check + * between each invocation. if the version differs it should discard the stale + * data and start from scratch. It is OK for the toolstack to use the new + * `version` field. + */ +#define XEN_SYSCTL_XSPLICE_LIST 2 +struct xen_sysctl_xsplice_list { + uint32_t version; /* IN/OUT: Initially *MUST* be zero. + On subsequent calls reuse value. + If varies between calls, we are + * getting stale data. */ + uint32_t idx; /* IN/OUT: Index into array. */ + uint32_t nr; /* IN: How many status, id, and len + should fill out. + OUT: How many payloads left. */ + uint32_t pad; /* IN: Must be zero. */ + XEN_GUEST_HANDLE_64(xen_xsplice_status_t) status; /* OUT. Must have enough + space allocate for nr of them. */ + XEN_GUEST_HANDLE_64(char) name; /* OUT: Array of ids. Each member + MUST XEN_XSPLICE_NAME_SIZE in size. + Must have nr of them. */ + XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of ids. + Must have nr of them. */ +}; +typedef struct xen_sysctl_xsplice_list xen_sysctl_xsplice_list_t; +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_list_t); + +/* + * Perform an operation on the payload structure referenced by the `name` field. + * The operation request is asynchronous and the status should be retrieved + * by using either XEN_SYSCTL_XSPLICE_GET or XEN_SYSCTL_XSPLICE_LIST hypercall. + */ +#define XEN_SYSCTL_XSPLICE_ACTION 3 +struct xen_sysctl_xsplice_action { + xen_xsplice_name_t name; /* IN, name of the patch. */ +#define XSPLICE_ACTION_CHECK 1 +#define XSPLICE_ACTION_UNLOAD 2 +#define XSPLICE_ACTION_REVERT 3 +#define XSPLICE_ACTION_APPLY 4 +#define XSPLICE_ACTION_REPLACE 5 + uint32_t cmd; /* IN: XSPLICE_ACTION_*. */ + uint32_t timeout; /* IN: Zero if no timeout. */ + /* Or upper bound of time (ms) */ + /* for operation to take. */ +}; +typedef struct xen_sysctl_xsplice_action xen_sysctl_xsplice_action_t; +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_action_t); + +struct xen_sysctl_xsplice_op { + uint32_t cmd; /* IN: XEN_SYSCTL_XSPLICE_*. */ + uint32_t pad; /* IN: Always zero. */ + union { + xen_sysctl_xsplice_upload_t upload; + xen_sysctl_xsplice_list_t list; + xen_sysctl_xsplice_summary_t get; + xen_sysctl_xsplice_action_t action; + } u; +}; +typedef struct xen_sysctl_xsplice_op xen_sysctl_xsplice_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_op_t); + struct xen_sysctl { uint32_t cmd; #define XEN_SYSCTL_readconsole 1 @@ -791,6 +945,7 @@ struct xen_sysctl { #define XEN_SYSCTL_pcitopoinfo 22 #define XEN_SYSCTL_psr_cat_op 23 #define XEN_SYSCTL_tmem_op 24 +#define XEN_SYSCTL_xsplice_op 25 uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */ union { struct xen_sysctl_readconsole readconsole; @@ -816,6 +971,7 @@ struct xen_sysctl { struct xen_sysctl_psr_cmt_op psr_cmt_op; struct xen_sysctl_psr_cat_op psr_cat_op; struct xen_sysctl_tmem_op tmem_op; + struct xen_sysctl_xsplice_op xsplice; uint8_t pad[128]; } u; }; diff --git a/xen/include/xen/xsplice.h b/xen/include/xen/xsplice.h new file mode 100644 index 0000000..2cb2035 --- /dev/null +++ b/xen/include/xen/xsplice.h @@ -0,0 +1,7 @@ +#ifndef __XEN_XSPLICE_H__ +#define __XEN_XSPLICE_H__ + +struct xen_sysctl_xsplice_op; +int xsplice_control(struct xen_sysctl_xsplice_op *); + +#endif /* __XEN_XSPLICE_H__ */ diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c index 9b7de30..5346dcf 100644 --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -807,6 +807,12 @@ static int flask_sysctl(int cmd) case XEN_SYSCTL_tmem_op: return domain_has_xen(current->domain, XEN__TMEM_CONTROL); +#ifdef CONFIG_XSPLICE + case XEN_SYSCTL_xsplice_op: + return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2, + XEN2__XSPLICE_OP, NULL); +#endif + default: printk("flask_sysctl: Unknown op %d\n", cmd); return -EPERM; diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors index effb59f..5f08d05 100644 --- a/xen/xsm/flask/policy/access_vectors +++ b/xen/xsm/flask/policy/access_vectors @@ -93,6 +93,8 @@ class xen2 pmu_ctrl # PMU use (domains, including unprivileged ones, will be using this operation) pmu_use +# XEN_SYSCTL_xsplice_op + xsplice_op } # Classes domain and domain2 consist of operations that a domain performs on