@@ -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)
@@ -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);
/*
@@ -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;
@@ -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);
@@ -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. */
@@ -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. */
};
/*