diff --git a/docs/misc/livepatch.pandoc b/docs/misc/livepatch.pandoc index 43d0896aa8..2f3f95ed37 100644 --- a/docs/misc/livepatch.pandoc +++ b/docs/misc/livepatch.pandoc @@ -707,8 +707,8 @@ The structure is as follow: ### XEN_SYSCTL_LIVEPATCH_LIST (2) -Retrieve an array of abbreviated status and names of payloads that are loaded in the -hypervisor. +Retrieve an array of abbreviated status, names and metadata of payloads that are +loaded in the hypervisor. The caller provides: @@ -717,11 +717,13 @@ The caller provides: * `idx` Index iterator. The index into the hypervisor's payload count. It is recommended that on first invocation zero be used so that `nr` (which the hypervisor will update with the remaining payload count) be provided. - Also the hypervisor will provide `version` with the most current value and - calculated total size for all payloads' names. + Also the hypervisor will provide `version` with the most current value, + calculated total size of all payloads' names and calculated total size of + all payload's metadata. * `nr` The max number of entries to populate. Can be zero which will result in the hypercall being a probing one and return the number of payloads (and update the `version`). + * `pad` - *MUST* be zero. * `status` Virtual address of where to write `struct xen_livepatch_status` structures. Caller *MUST* allocate up to `nr` of them. * `name` - Virtual address of where to write the unique name of the payloads. @@ -733,14 +735,23 @@ The caller provides: * `len` - Virtual address of where to write the length of each unique name of the payload. Caller *MUST* allocate up to `nr` of them. Each *MUST* be of sizeof(uint32_t) (4 bytes). + * `metadata` - Virtual address of where to write the metadata of the payloads. + Caller *MUST* allocate enough space to be able to store all received data + (i.e. total allocated space *MUST* match the `metadata_total_size` value + provided by the hypervisor). Individual payload metadata string can be of + arbitrary length. The metadata string format is: key=value\0...key=value\0. + * `metadata_len` - Virtual address of where to write the length of each metadata + string of the payload. Caller *MUST* allocate up to `nr` of them. Each *MUST* + be of sizeof(uint32_t) (4 bytes). If the hypercall returns an positive number, it is the number (upto `nr` provided to the hypercall) 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) and the `name_total_size` containing total size of transferred data for -the array. The `status`, `name`, and `len` are updated at their designed index -value (`idx`) with the returned value of data. +fail), `name_total_size` and `metadata_total_size` containing total sizes of +transferred data for both the arrays. +The `status`, `name`, `len`, `metadata` and `metadata_len` are updated at their +designed index value (`idx`) with the returned value of data. If the hypercall returns -XEN_E2BIG the `nr` is too big and should be lowered. @@ -777,7 +788,9 @@ The structure is as follow: should be filled out. Can be zero to get amount of payloads and version. OUT: How many payloads left. */ + uint32_t pad; /* IN: Must be zero. */ uint32_t name_total_size; /* OUT: Total size of all transfer names */ + uint32_t metadata_total_size; /* OUT: Total size of all transfer metadata */ XEN_GUEST_HANDLE_64(xen_livepatch_status_t) status; /* OUT. Must have enough space allocate for nr of them. */ XEN_GUEST_HANDLE_64(char) name; /* OUT: Array of names. Each member @@ -786,6 +799,12 @@ The structure is as follow: nr of them. */ XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of name's. Must have nr of them. */ + XEN_GUEST_HANDLE_64(char) metadata; /* OUT: Array of metadata strings. Each + member may have an arbitrary length. + Must have nr of them. */ + XEN_GUEST_HANDLE_64(uint32) metadata_len; /* OUT: Array of lengths of metadata's. + Must have nr of them. */ + }; ### XEN_SYSCTL_LIVEPATCH_ACTION (3) diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h index f490a6debc..e549702fdd 100644 --- a/tools/libxc/include/xenctrl.h +++ b/tools/libxc/include/xenctrl.h @@ -2559,7 +2559,7 @@ int xc_livepatch_get(xc_interface *xch, /* * Get a number of available payloads and get actual total size of - * the payloads' name array. + * the payloads' name and metadata arrays. * * This functions is typically executed first before the xc_livepatch_list() * to obtain the sizes and correctly allocate all necessary data resources. @@ -2570,13 +2570,16 @@ int xc_livepatch_get(xc_interface *xch, * will contain the hypercall error code value. */ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, - uint32_t *name_total_size); + uint32_t *name_total_size, + uint32_t *metadata_total_size); /* * The heart of this function is to get an array of the following objects: * - xen_livepatch_status_t: states and return codes of payloads * - name: names of payloads * - len: lengths of corresponding payloads' names + * - metadata: payloads' metadata + * - metadata_len: lengths of corresponding payloads' metadata * * However it is complex because it has to deal with the hypervisor * returning some of the requested data or data being stale @@ -2589,12 +2592,13 @@ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, * * It is expected that the caller of this function will first issue the * xc_livepatch_list_get_sizes() in order to obtain total sizes of names - * as well as the current number of payload entries. - * The total sizes are required and supplied via the 'name_total_size' - * parameter. + * and all metadata as well as the current number of payload entries. + * The total sizes are required and supplied via the 'name_total_size' and + * 'metadata_total_size' parameters. * * The 'max' is to be provided by the caller with the maximum number of - * entries that 'info', 'name', 'len' arrays can be filled up with. + * entries that 'info', 'name', 'len', 'metadata' and 'metadata_len' arrays + * can be filled up with. * * Each entry in the 'info' array is expected to be of xen_livepatch_status_t * structure size. @@ -2603,6 +2607,10 @@ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, * * Each entry in the 'len' array is expected to be of uint32_t size. * + * Each entry in the 'metadata' array may have an arbitrary size. + * + * Each entry in the 'metadata_len' array is expected to be of uint32_t size. + * * The return value is zero if the hypercall completed successfully. * Note that the return value is _not_ the amount of entries filled * out - that is saved in 'done'. @@ -2617,6 +2625,8 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, struct xen_livepatch_status *info, char *name, uint32_t *len, const uint32_t name_total_size, + char *metadata, uint32_t *metadata_len, + const uint32_t metadata_total_size, unsigned int *done, unsigned int *left); /* diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c index 580d254593..0b21a6aca4 100644 --- a/tools/libxc/xc_misc.c +++ b/tools/libxc/xc_misc.c @@ -663,7 +663,7 @@ int xc_livepatch_get(xc_interface *xch, /* * Get a number of available payloads and get actual total size of - * the payloads' name array. + * the payloads' name and metadata arrays. * * This functions is typically executed first before the xc_livepatch_list() * to obtain the sizes and correctly allocate all necessary data resources. @@ -674,12 +674,13 @@ int xc_livepatch_get(xc_interface *xch, * will contain the hypercall error code value. */ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, - uint32_t *name_total_size) + uint32_t *name_total_size, + uint32_t *metadata_total_size) { DECLARE_SYSCTL; int rc; - if ( !nr || !name_total_size ) + if ( !nr || !name_total_size || !metadata_total_size ) { errno = EINVAL; return -1; @@ -695,6 +696,7 @@ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, *nr = sysctl.u.livepatch.u.list.nr; *name_total_size = sysctl.u.livepatch.u.list.name_total_size; + *metadata_total_size = sysctl.u.livepatch.u.list.metadata_total_size; return 0; } @@ -704,6 +706,8 @@ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, * - xen_livepatch_status_t: states and return codes of payloads * - name: names of payloads * - len: lengths of corresponding payloads' names + * - metadata: payloads' metadata + * - metadata_len: lengths of corresponding payloads' metadata * * However it is complex because it has to deal with the hypervisor * returning some of the requested data or data being stale @@ -716,12 +720,13 @@ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, * * It is expected that the caller of this function will first issue the * xc_livepatch_list_get_sizes() in order to obtain total sizes of names - * as well as the current number of payload entries. - * The total sizes are required and supplied via the 'name_total_size' - * parameter. + * and all metadata as well as the current number of payload entries. + * The total sizes are required and supplied via the 'name_total_size' and + * 'metadata_total_size' parameters. * * The 'max' is to be provided by the caller with the maximum number of - * entries that 'info', 'name', 'len' arrays can be filled up with. + * entries that 'info', 'name', 'len', 'metadata' and 'metadata_len' arrays + * can be filled up with. * * Each entry in the 'info' array is expected to be of xen_livepatch_status_t * structure size. @@ -730,6 +735,10 @@ int xc_livepatch_list_get_sizes(xc_interface *xch, unsigned int *nr, * * Each entry in the 'len' array is expected to be of uint32_t size. * + * Each entry in the 'metadata' array may have an arbitrary size. + * + * Each entry in the 'metadata_len' array is expected to be of uint32_t size. + * * The return value is zero if the hypercall completed successfully. * Note that the return value is _not_ the amount of entries filled * out - that is saved in 'done'. @@ -744,6 +753,8 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, struct xen_livepatch_status *info, char *name, uint32_t *len, const uint32_t name_total_size, + char *metadata, uint32_t *metadata_len, + const uint32_t metadata_total_size, unsigned int *done, unsigned int *left) { int rc; @@ -752,13 +763,16 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, DECLARE_HYPERCALL_BOUNCE(info, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(name, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(len, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); + DECLARE_HYPERCALL_BOUNCE(metadata, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); + DECLARE_HYPERCALL_BOUNCE(metadata_len, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); uint32_t max_batch_sz, nr; uint32_t version = 0, retries = 0; uint32_t adjust = 0; - uint32_t name_off = 0; - uint32_t name_sz; + uint32_t name_off = 0, metadata_off = 0; + uint32_t name_sz, metadata_sz; - if ( !max || !info || !name || !len || !done || !left ) + if ( !max || !info || !name || !len || + !metadata || !metadata_len || !done || !left ) { errno = EINVAL; return -1; @@ -777,10 +791,11 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, max_batch_sz = max; name_sz = name_total_size; + metadata_sz = metadata_total_size; *done = 0; *left = 0; do { - uint32_t _name_sz; + uint32_t _name_sz, _metadata_sz; /* * The first time we go in this loop our 'max' may be bigger @@ -803,10 +818,14 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, HYPERCALL_BOUNCE_SET_SIZE(info, nr * sizeof(*info)); HYPERCALL_BOUNCE_SET_SIZE(name, name_sz); HYPERCALL_BOUNCE_SET_SIZE(len, nr * sizeof(*len)); + HYPERCALL_BOUNCE_SET_SIZE(metadata, metadata_sz); + HYPERCALL_BOUNCE_SET_SIZE(metadata_len, nr * sizeof(*metadata_len)); /* Move the pointer to proper offset into 'info'. */ (HYPERCALL_BUFFER(info))->ubuf = info + *done; (HYPERCALL_BUFFER(name))->ubuf = name + name_off; (HYPERCALL_BUFFER(len))->ubuf = len + *done; + (HYPERCALL_BUFFER(metadata))->ubuf = metadata + metadata_off; + (HYPERCALL_BUFFER(metadata_len))->ubuf = metadata_len + *done; /* Allocate memory. */ rc = xc_hypercall_bounce_pre(xch, info); if ( rc ) @@ -820,9 +839,19 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, if ( rc ) break; + rc = xc_hypercall_bounce_pre(xch, metadata); + if ( rc ) + break; + + rc = xc_hypercall_bounce_pre(xch, metadata_len); + if ( rc ) + break; + set_xen_guest_handle(sysctl.u.livepatch.u.list.status, info); set_xen_guest_handle(sysctl.u.livepatch.u.list.name, name); set_xen_guest_handle(sysctl.u.livepatch.u.list.len, len); + set_xen_guest_handle(sysctl.u.livepatch.u.list.metadata, metadata); + set_xen_guest_handle(sysctl.u.livepatch.u.list.metadata_len, metadata_len); rc = do_sysctl(xch, &sysctl); /* @@ -839,6 +868,8 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, xc_hypercall_bounce_post(xch, info); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, len); + xc_hypercall_bounce_post(xch, metadata); + xc_hypercall_bounce_post(xch, metadata_len); continue; } else if ( rc < 0 ) /* For all other errors we bail out. */ @@ -863,6 +894,8 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, xc_hypercall_bounce_post(xch, info); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, len); + xc_hypercall_bounce_post(xch, metadata); + xc_hypercall_bounce_post(xch, metadata_len); continue; } @@ -875,17 +908,24 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, } *left = sysctl.u.livepatch.u.list.nr; /* Total remaining count. */ _name_sz = sysctl.u.livepatch.u.list.name_total_size; /* Total received name size. */ + _metadata_sz = sysctl.u.livepatch.u.list.metadata_total_size; /* Total received metadata size. */ /* Copy only up 'rc' of data' - we could add 'min(rc,nr) if desired. */ HYPERCALL_BOUNCE_SET_SIZE(info, (rc * sizeof(*info))); HYPERCALL_BOUNCE_SET_SIZE(name, _name_sz); HYPERCALL_BOUNCE_SET_SIZE(len, (rc * sizeof(*len))); + HYPERCALL_BOUNCE_SET_SIZE(metadata, _metadata_sz); + HYPERCALL_BOUNCE_SET_SIZE(metadata_len, (rc * sizeof(*metadata_len))); /* Bounce the data and free the bounce buffer. */ xc_hypercall_bounce_post(xch, info); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, len); + xc_hypercall_bounce_post(xch, metadata); + xc_hypercall_bounce_post(xch, metadata_len); name_sz -= _name_sz; name_off += _name_sz; + metadata_sz -= _metadata_sz; + metadata_off += _metadata_sz; /* And update how many elements of info we have copied into. */ *done += rc; @@ -898,6 +938,8 @@ int xc_livepatch_list(xc_interface *xch, const unsigned int max, xc_hypercall_bounce_post(xch, len); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, info); + xc_hypercall_bounce_post(xch, metadata); + xc_hypercall_bounce_post(xch, metadata_len); } return rc > 0 ? 0 : rc; diff --git a/tools/misc/xen-livepatch.c b/tools/misc/xen-livepatch.c index c93c50040c..16a4a47ab7 100644 --- a/tools/misc/xen-livepatch.c +++ b/tools/misc/xen-livepatch.c @@ -69,8 +69,10 @@ static int list_func(int argc, char *argv[]) unsigned int nr, done, left, i; xen_livepatch_status_t *info = NULL; char *name = NULL; + char *metadata = NULL; uint32_t *len = NULL; - uint32_t name_total_size, name_off; + uint32_t *metadata_len = NULL; + uint32_t name_total_size, metadata_total_size, name_off, metadata_off; int rc = ENOMEM; if ( argc ) @@ -80,7 +82,7 @@ static int list_func(int argc, char *argv[]) } done = left = 0; - rc = xc_livepatch_list_get_sizes(xch, &nr, &name_total_size); + rc = xc_livepatch_list_get_sizes(xch, &nr, &name_total_size, &metadata_total_size); if ( rc ) { rc = errno; @@ -108,12 +110,23 @@ static int list_func(int argc, char *argv[]) if ( !len ) goto error_len; + metadata = malloc(metadata_total_size * sizeof(*metadata) + 1); + if ( !metadata ) + goto error_metadata; + + metadata_len = malloc(nr * sizeof(*metadata_len)); + if ( !metadata_len ) + goto error_metadata_len; + memset(info, 'A', nr * sizeof(*info)); memset(name, 'B', name_total_size * sizeof(*name)); memset(len, 'C', nr * sizeof(*len)); - name_off = 0; + memset(metadata, 'D', metadata_total_size * sizeof(*metadata) + 1); + memset(metadata_len, 'E', nr * sizeof(*metadata_len)); + name_off = metadata_off = 0; - rc = xc_livepatch_list(xch, nr, 0, info, name, len, name_total_size, &done, &left); + rc = xc_livepatch_list(xch, nr, 0, info, name, len, name_total_size, + metadata, metadata_len, metadata_total_size, &done, &left); if ( rc || done != nr || left > 0) { rc = errno; @@ -123,23 +136,35 @@ static int list_func(int argc, char *argv[]) goto error; } - fprintf(stdout," ID | status\n" - "----------------------------------------+------------\n"); + fprintf(stdout," ID | status | metadata\n" + "----------------------------------------+------------+---------------\n"); for ( i = 0; i < done; i++ ) { + unsigned int j; char *name_str = name + name_off; + char *metadata_str = metadata + metadata_off; printf("%-40.*s| %s", len[i], name_str, state2str(info[i].state)); if ( info[i].rc ) - printf(" (%d, %s)\n", -info[i].rc, strerror(-info[i].rc)); + printf(" (%d, %s) | ", -info[i].rc, strerror(-info[i].rc)); else - puts(""); + printf(" | "); + + /* Replace all '\0' with semi-colons. */ + for ( j = 0; metadata_len[i] && j < metadata_len[i] - 1; j++ ) + metadata_str[j] = (metadata_str[j] ?: ';'); + printf("%.*s\n", metadata_len[i], metadata_str); name_off += len[i]; + metadata_off += metadata_len[i]; } error: + free(metadata_len); +error_metadata_len: + free(metadata); +error_metadata: free(len); error_len: free(name); diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c index bc643295d6..2a8ab13879 100644 --- a/xen/common/livepatch.c +++ b/xen/common/livepatch.c @@ -1159,9 +1159,13 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list) if ( list->nr > 1024 ) return -E2BIG; + if ( list->pad ) + return -EINVAL; + if ( list->nr && (!guest_handle_okay(list->status, list->nr) || - !guest_handle_okay(list->len, list->nr)) ) + !guest_handle_okay(list->len, list->nr) || + !guest_handle_okay(list->metadata_len, list->nr)) ) return -EINVAL; spin_lock(&payload_lock); @@ -1172,13 +1176,14 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list) } list->name_total_size = 0; + list->metadata_total_size = 0; if ( list->nr ) { - uint64_t name_offset = 0; + uint64_t name_offset = 0, metadata_offset = 0; list_for_each_entry( data, &payload_list, list ) { - uint32_t name_len; + uint32_t name_len, metadata_len; if ( list->idx > i++ ) continue; @@ -1189,8 +1194,13 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list) name_len = strlen(data->name) + 1; list->name_total_size += name_len; + metadata_len = data->metadata.len; + list->metadata_total_size += metadata_len; + if ( !guest_handle_subrange_okay(list->name, name_offset, - name_offset + name_len - 1) ) + name_offset + name_len - 1) || + !guest_handle_subrange_okay(list->metadata, metadata_offset, + metadata_offset + metadata_len - 1) ) { rc = -EINVAL; break; @@ -1200,7 +1210,10 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list) if ( __copy_to_guest_offset(list->name, name_offset, data->name, name_len) || __copy_to_guest_offset(list->len, idx, &name_len, 1) || - __copy_to_guest_offset(list->status, idx, &status, 1) ) + __copy_to_guest_offset(list->status, idx, &status, 1) || + __copy_to_guest_offset(list->metadata, metadata_offset, + data->metadata.data, metadata_len) || + __copy_to_guest_offset(list->metadata_len, idx, &metadata_len, 1) ) { rc = -EFAULT; break; @@ -1208,6 +1221,7 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list) idx++; name_offset += name_len; + metadata_offset += metadata_len; if ( (idx >= list->nr) || hypercall_preempt_check() ) break; @@ -1218,6 +1232,7 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list) list_for_each_entry( data, &payload_list, list ) { list->name_total_size += strlen(data->name) + 1; + list->metadata_total_size += data->metadata.len; } } list->nr = payload_cnt - i; /* Remaining amount. */ diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index 4bfd1475bf..7e43bfe1bd 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.h @@ -934,16 +934,17 @@ struct xen_sysctl_livepatch_get { }; /* - * Retrieve an array of abbreviated status and names of payloads that are - * loaded in the hypervisor. + * Retrieve an array of abbreviated status, names and metadata 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) and the name_total_size - * containing total size of transferred data for the array. - * The `status`, `name`, `len` are updated at their designed index value (`idx`) - * with the returned value of data. + * the data is stale and further calls could fail), `name_total_size` and + * `metadata_total_size` containing total sizes of transferred data for both the + * arrays. + * The `status`, `name`, `len`, `metadata` and `metadata_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. The upper limit of `nr` is left to the implemention. @@ -965,7 +966,9 @@ struct xen_sysctl_livepatch_list { should fill out. Can be zero to get amount of payloads and version. OUT: How many payloads left. */ + uint32_t pad; /* IN: Must be zero. */ uint32_t name_total_size; /* OUT: Total size of all transfer names */ + uint32_t metadata_total_size; /* OUT: Total size of all transfer metadata */ XEN_GUEST_HANDLE_64(xen_livepatch_status_t) status; /* OUT. Must have enough space allocate for nr of them. */ XEN_GUEST_HANDLE_64(char) name; /* OUT: Array of names. Each member @@ -974,6 +977,11 @@ struct xen_sysctl_livepatch_list { nr of them. */ XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of name's. Must have nr of them. */ + XEN_GUEST_HANDLE_64(char) metadata; /* OUT: Array of metadata strings. Each + member may have an arbitrary length. + Must have nr of them. */ + XEN_GUEST_HANDLE_64(uint32) metadata_len; /* OUT: Array of lengths of metadata's. + Must have nr of them. */ }; /*