mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
Merge branch 'livepatch.aws.v6' into staging
* livepatch.aws.v6: livepatch: Add metadata runtime retrieval mechanism livepatch: Handle arbitrary size names with the list operation livepatch: Add support for modules .modinfo section metadata livepatch: Add support for inline asm livepatching expectations livepatch: Add per-function applied/reverted state tracking marker livepatch: Do not enforce ELF_LIVEPATCH_FUNC section presence livepatch: Add support for apply|revert action replacement hooks livepatch: Implement pre-|post- apply|revert hooks livepatch: Export payload structure via livepatch_payload.h livepatch: Allow to override inter-modules buildid dependency livepatch: Always check hypervisor build ID upon livepatch upload Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
This commit is contained in:
commit
25164571fc
19 files changed
+2296
-287
No files matched your search
+2
-4
@@ -308,10 +308,8 @@ xen/include/xen/acm_policy.h
|
||||
xen/include/xen/compile.h
|
||||
xen/include/xen/lib/x86/cpuid-autogen.h
|
||||
xen/test/livepatch/config.h
|
||||
xen/test/livepatch/xen_bye_world.livepatch
|
||||
xen/test/livepatch/xen_hello_world.livepatch
|
||||
xen/test/livepatch/xen_nop.livepatch
|
||||
xen/test/livepatch/xen_replace_world.livepatch
|
||||
xen/test/livepatch/expect_config.h
|
||||
xen/test/livepatch/*.livepatch
|
||||
xen/tools/kconfig/.tmp_gtkcheck
|
||||
xen/tools/kconfig/.tmp_qtcheck
|
||||
xen/tools/symbols
|
||||
|
||||
+221
-27
@@ -1,4 +1,4 @@
|
||||
# Xen Live Patching Design v1
|
||||
# Xen Live Patching Design v2
|
||||
|
||||
## Rationale
|
||||
|
||||
@@ -23,6 +23,9 @@ The document is split in four sections:
|
||||
* payload - telemetries of the old code along with binary blob of the new
|
||||
function (if needed).
|
||||
* reloc - telemetries contained in the payload to construct proper trampoline.
|
||||
* hook - an auxiliary function being called before, during or after payload
|
||||
application or revert.
|
||||
* quiescing zone - period when all CPUs are lock-step with each other.
|
||||
|
||||
## History
|
||||
|
||||
@@ -270,6 +273,13 @@ like what the Linux kernel module loader does.
|
||||
The payload contains at least three sections:
|
||||
|
||||
* `.livepatch.funcs` - which is an array of livepatch_func structures.
|
||||
and/or any of:
|
||||
* `.livepatch.hooks.{preapply,postapply,prerevert,postrevert}'
|
||||
* `.livepatch.hooks.{apply,revert}`
|
||||
- which are a pointer to a hook function pointer.
|
||||
|
||||
* `.livepatch.xen_depends` - which is an ELF Note that describes what Xen
|
||||
build-id the payload depends on. **MUST** have one.
|
||||
* `.livepatch.depends` - which is an ELF Note that describes what the payload
|
||||
depends on. **MUST** have one.
|
||||
* `.note.gnu.build-id` - the build-id of this payload. **MUST** have one.
|
||||
@@ -287,10 +297,15 @@ which describe the functions to be patched:
|
||||
uint32_t old_size;
|
||||
uint8_t version;
|
||||
uint8_t opaque[31];
|
||||
/* Added to livepatch payload version 2: */
|
||||
uint8_t applied;
|
||||
uint8_t _pad[7];
|
||||
livepatch_expectation_t expect;
|
||||
};
|
||||
|
||||
The size of the structure is 64 bytes on 64-bit hypervisors. It will be
|
||||
52 on 32-bit hypervisors.
|
||||
The size of the structure is 104 bytes on 64-bit hypervisors. It will be
|
||||
92 on 32-bit hypervisors.
|
||||
The version 2 of the payload adds additional 8 bytes to the structure size.
|
||||
|
||||
* `name` is the symbol name of the old function. Only used if `old_addr` is
|
||||
zero, otherwise will be used during dynamic linking (when hypervisor loads
|
||||
@@ -314,9 +329,37 @@ The size of the structure is 64 bytes on 64-bit hypervisors. It will be
|
||||
* If the value of `new_addr` is zero then `new_size` determines how many
|
||||
instruction bytes to NOP (up to opaque size modulo smallest platform
|
||||
instruction - 1 byte x86 and 4 bytes on ARM).
|
||||
* `version` is to be one.
|
||||
* `version` indicates version of the generated payload.
|
||||
* `opaque` **MUST** be zero.
|
||||
|
||||
The version 2 of the payload adds the following fields to the structure:
|
||||
|
||||
* `applied` tracks function's applied/reverted state. It has a boolean type
|
||||
either LIVEPATCH_FUNC_NOT_APPLIED or LIVEPATCH_FUNC_APPLIED.
|
||||
* `_pad[7]` adds padding to align to 8 bytes.
|
||||
* `expect` is an optional structure containing expected to-be-replaced data
|
||||
(mostly for inline asm patching). The `expect` structure format is:
|
||||
|
||||
struct livepatch_expectation {
|
||||
uint8_t enabled : 1;
|
||||
uint8_t len : 5;
|
||||
uint8_t rsv: 2;
|
||||
uint8_t data[LIVEPATCH_OPAQUE_SIZE]; /* Same size as opaque[] buffer of
|
||||
struct livepatch_func. This is the
|
||||
max number of bytes to be patched */
|
||||
};
|
||||
typedef struct livepatch_expectation livepatch_expectation_t;
|
||||
|
||||
* `enabled` allows to enable the expectation check for given function.
|
||||
Default state is disabled.
|
||||
* `len` specifies the number of valid bytes in `data` array. 5 bits is
|
||||
enough to specify values up to 32 (of bytes), which is above the array
|
||||
size.
|
||||
* `rsv` reserved bitfields. **MUST** be zero.
|
||||
* `data` contains expected bytes of content to be replaced. Same size as
|
||||
`opaque` buffer of `struct livepatch_func` (max number of bytes to be
|
||||
patched).
|
||||
|
||||
The size of the `livepatch_func` array is determined from the ELF section
|
||||
size.
|
||||
|
||||
@@ -328,12 +371,32 @@ When reverting a patch, the hypervisor iterates over each `livepatch_func`
|
||||
and the core code copies the data from the undo buffer (private internal copy)
|
||||
to `old_addr`.
|
||||
|
||||
It optionally may contain the address of functions to be called right before
|
||||
being applied and after being reverted:
|
||||
It optionally may contain the address of hooks to be called right before
|
||||
being applied and after being reverted (while all CPUs are still in quiescing
|
||||
zone). These hooks do not have access to payload structure.
|
||||
|
||||
* `.livepatch.hooks.load` - an array of function pointers.
|
||||
* `.livepatch.hooks.unload` - an array of function pointers.
|
||||
|
||||
It optionally may also contain the address of pre- and post- vetoing hooks to
|
||||
be called before (pre) or after (post) apply and revert payload actions (while
|
||||
all CPUs are already released from quiescing zone). These hooks do have
|
||||
access to payload structure. The pre-apply hook can prevent from loading the
|
||||
payload if encoded in it condition is not met. Accordingly, the pre-revert
|
||||
hook can prevent from unloading the livepatch if encoded in it condition is not
|
||||
met.
|
||||
|
||||
* `.livepatch.hooks.{preapply,postapply}`
|
||||
* `.livepatch.hooks.{prerevert,postrevert}`
|
||||
- which are a pointer to a single hook function pointer.
|
||||
|
||||
Finally, it optionally may also contain the address of apply or revert action
|
||||
hooks to be called instead of the default apply and revert payload actions
|
||||
(while all CPUs are kept in quiescing zone). These hooks do have access to
|
||||
payload structure.
|
||||
|
||||
* `.livepatch.hooks.{apply,revert}`
|
||||
- which are a pointer to a single hook function pointer.
|
||||
|
||||
### Example of .livepatch.funcs
|
||||
|
||||
@@ -348,6 +411,10 @@ A simple example of what a payload file can be:
|
||||
uint32_t old_size;
|
||||
uint8_t version;
|
||||
uint8_t pad[31];
|
||||
/* Added to livepatch payload version 2: */
|
||||
uint8_t applied;
|
||||
uint8_t _pad[7];
|
||||
livepatch_expectation_t expect;
|
||||
};
|
||||
|
||||
/* Our replacement function for xen_extra_version. */
|
||||
@@ -365,11 +432,20 @@ A simple example of what a payload file can be:
|
||||
.old_addr = (void *)0xffff82d08013963c, /* Extracted from xen-syms. */
|
||||
.new_size = 13, /* To be be computed by scripts. */
|
||||
.old_size = 13, /* -----------""--------------- */
|
||||
/* Added to livepatch payload version 2: */
|
||||
.expect = { /* All fields to be filled manually */
|
||||
.enabled = 1,
|
||||
.len = 5,
|
||||
.rsv = 0,
|
||||
.data = { 0x48, 0x8d, 0x05, 0x33, 0x1C }
|
||||
},
|
||||
} __attribute__((__section__(".livepatch.funcs")));
|
||||
|
||||
Code must be compiled with `-fPIC`.
|
||||
|
||||
### .livepatch.hooks.load and .livepatch.hooks.unload
|
||||
### Hooks
|
||||
|
||||
#### .livepatch.hooks.load and .livepatch.hooks.unload
|
||||
|
||||
This section contains an array of function pointers to be executed
|
||||
before payload is being applied (.livepatch.funcs) or after reverting
|
||||
@@ -383,16 +459,93 @@ The type definition of the function are as follow:
|
||||
typedef void (*livepatch_loadcall_t)(void);
|
||||
typedef void (*livepatch_unloadcall_t)(void);
|
||||
|
||||
### .livepatch.depends and .note.gnu.build-id
|
||||
#### .livepatch.hooks.preapply
|
||||
|
||||
This section contains a pointer to a single function pointer to be executed
|
||||
before apply action is scheduled (and thereby before CPUs are put into
|
||||
quiescing zone). This is useful to prevent from applying a payload when
|
||||
certain expected conditions aren't met or when mutating actions implemented
|
||||
in the hook fail or cannot be executed.
|
||||
This type of hooks do have access to payload structure.
|
||||
|
||||
Each entry in this array is eight bytes.
|
||||
|
||||
The type definition of the function are as follow:
|
||||
|
||||
typedef int livepatch_precall_t(livepatch_payload_t *arg);
|
||||
|
||||
#### .livepatch.hooks.postapply
|
||||
|
||||
This section contains a pointer to a single function pointer to be executed
|
||||
after apply action has finished and after all CPUs left the quiescing zone.
|
||||
This is useful to provide an ability to follow up on actions performed by
|
||||
the preapply hook. Especially, when module application was successful or to
|
||||
be able to undo certain preparation steps of the preapply hook in case of a
|
||||
failure. The success/failure error code is provided to the postapply hooks
|
||||
via the `rc` field of the payload structure.
|
||||
This type of hooks do have access to payload structure.
|
||||
|
||||
Each entry in this array is eight bytes.
|
||||
|
||||
The type definition of the function are as follow:
|
||||
|
||||
typedef void livepatch_postcall_t(livepatch_payload_t *arg);
|
||||
|
||||
#### .livepatch.hooks.prerevert
|
||||
|
||||
This section contains a pointer to a single function pointer to be executed
|
||||
before revert action is scheduled (and thereby before CPUs are put into
|
||||
quiescing zone). This is useful to prevent from reverting a payload when
|
||||
certain expected conditions aren't met or when mutating actions implemented
|
||||
in the hook fail or cannot be executed.
|
||||
This type of hooks do have access to payload structure.
|
||||
|
||||
Each entry in this array is eight bytes.
|
||||
|
||||
The type definition of the function are as follow:
|
||||
|
||||
typedef int livepatch_precall_t(livepatch_payload_t *arg);
|
||||
|
||||
#### .livepatch.hooks.postrevert
|
||||
|
||||
This section contains a pointer to a single function pointer to be executed
|
||||
after revert action has finished and after all CPUs left the quiescing zone.
|
||||
This is useful to provide an ability to perform cleanup of all previously
|
||||
executed mutating actions in order to restore the original system state from
|
||||
before the current payload application. The success/failure error code is
|
||||
provided to the postrevert hook via the `rc` field of the payload structure.
|
||||
This type of hooks do have access to payload structure.
|
||||
|
||||
Each entry in this array is eight bytes.
|
||||
|
||||
The type definition of the function are as follow:
|
||||
|
||||
typedef void livepatch_postcall_t(livepatch_payload_t *arg);
|
||||
|
||||
#### .livepatch.hooks.apply and .livepatch.hooks.revert
|
||||
|
||||
This section contains a pointer to a single function pointer to be executed
|
||||
instead of a default apply (or revert) action function. This is useful to
|
||||
replace or augment default behavior of the apply (or revert) action that
|
||||
requires all CPUs to be in the quiescing zone.
|
||||
This type of hooks do have access to payload structure.
|
||||
|
||||
Each entry in this array is eight bytes.
|
||||
|
||||
The type definition of the function are as follow:
|
||||
|
||||
typedef int livepatch_actioncall_t(livepatch_payload_t *arg);
|
||||
|
||||
### .livepatch.xen_depends, .livepatch.depends and .note.gnu.build-id
|
||||
|
||||
To support dependencies checking and safe loading (to load the
|
||||
appropiate payload against the right hypervisor) there is a need
|
||||
to embbed an build-id dependency.
|
||||
|
||||
This is done by the payload containing an section `.livepatch.depends`
|
||||
which follows the format of an ELF Note. The contents of this
|
||||
(name, and description) are specific to the linker utilized to
|
||||
build the hypevisor and payload.
|
||||
This is done by the payload containing sections `.livepatch.xen_depends`
|
||||
and `.livepatch.depends` which follow the format of an ELF Note.
|
||||
The contents of these (name, and description) are specific to the linker
|
||||
utilized to build the hypevisor and payload.
|
||||
|
||||
If GNU linker is used then the name is `GNU` and the description
|
||||
is a NT_GNU_BUILD_ID type ID. The description can be an SHA1
|
||||
@@ -400,6 +553,13 @@ checksum, MD5 checksum or any unique value.
|
||||
|
||||
The size of these structures varies with the `--build-id` linker option.
|
||||
|
||||
There are two kinds of build-id dependencies:
|
||||
|
||||
* Xen build-id dependency (.livepatch.xen_depends section)
|
||||
* previous payload build-id dependency (.livepatch.depends section)
|
||||
|
||||
See "Live patch interdependencies" for more information.
|
||||
|
||||
## Hypercalls
|
||||
|
||||
We will employ the sub operations of the system management hypercall (sysctl).
|
||||
@@ -547,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:
|
||||
|
||||
@@ -557,27 +717,41 @@ 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.
|
||||
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 payload.
|
||||
Caller *MUST* allocate up to `nr` of them. Each *MUST* be of
|
||||
**XEN_LIVEPATCH_NAME_SIZE** size. Note that **XEN_LIVEPATCH_NAME_SIZE** includes
|
||||
the NUL terminator.
|
||||
* `name` - Virtual address of where to write the unique name of the payloads.
|
||||
Caller *MUST* allocate enough space to be able to store all received data
|
||||
(i.e. total allocated space *MUST* match the `name_total_size` value
|
||||
provided by the hypervisor). Individual payload name cannot be longer than
|
||||
**XEN_LIVEPATCH_NAME_SIZE** bytes. Note that **XEN_LIVEPATCH_NAME_SIZE**
|
||||
includes the NUL terminator.
|
||||
* `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). 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.
|
||||
@@ -615,13 +789,22 @@ The structure is as follow:
|
||||
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) id; /* OUT: Array of names. Each member
|
||||
MUST XEN_LIVEPATCH_NAME_SIZE in size.
|
||||
Must have nr of them. */
|
||||
XEN_GUEST_HANDLE_64(char) name; /* OUT: Array of names. Each member
|
||||
may have an arbitrary length up to
|
||||
XEN_LIVEPATCH_NAME_SIZE bytes. Must have
|
||||
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)
|
||||
@@ -650,6 +833,10 @@ The caller provides:
|
||||
* `time` The upper bound of time (ns) the cmd should take. Zero means to use
|
||||
the hypervisor default. If within the time the operation does not succeed
|
||||
the operation would go in error state.
|
||||
* `flags` provides additional parameters for an action:
|
||||
* *LIVEPATCH_ACTION_APPLY_NODEPS* (1) Apply action ignores inter-module
|
||||
buildid dependency. Checks only if module is built for given hypervisor by
|
||||
comparing buildid.
|
||||
* `pad` - *MUST* be zero.
|
||||
|
||||
The return value will be zero unless the provided fields are incorrect.
|
||||
@@ -667,6 +854,10 @@ The structure is as follow:
|
||||
/* hypervisor default. */
|
||||
/* Or upper bound of time (ns) */
|
||||
/* for operation to take. */
|
||||
uint32_t flags; /* IN: action flags. */
|
||||
/* Provide additional parameters */
|
||||
/* for an action. */
|
||||
uint32_t pad; /* IN: Always zero. */
|
||||
};
|
||||
|
||||
|
||||
@@ -894,13 +1085,16 @@ but is more complex to implement.
|
||||
The second option which requires an build-id of the hypervisor
|
||||
is implemented in the Xen hypervisor.
|
||||
|
||||
Specifically each payload has two build-id ELF notes:
|
||||
Specifically each payload has three build-id ELF notes:
|
||||
* The build-id of the payload itself (generated via --build-id).
|
||||
* The build-id of the Xen hypervisor it depends on (extracted from the
|
||||
hypervisor during build time).
|
||||
* The build-id of the payload it depends on (extracted from the
|
||||
the previous payload or hypervisor during build time).
|
||||
|
||||
This means that the very first payload depends on the hypervisor
|
||||
build-id.
|
||||
This means that every payload depends on the hypervisor build-id and on
|
||||
the build-id of the previous payload in the stack.
|
||||
The very first payload depends on the hypervisor build-id only.
|
||||
|
||||
# Not Yet Done
|
||||
|
||||
|
||||
@@ -2558,7 +2558,28 @@ int xc_livepatch_get(xc_interface *xch,
|
||||
xen_livepatch_status_t *status);
|
||||
|
||||
/*
|
||||
* The heart of this function is to get an array of xen_livepatch_status_t.
|
||||
* Get a number of available payloads and get actual total size of
|
||||
* 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.
|
||||
*
|
||||
* The return value is zero if the hypercall completed successfully.
|
||||
*
|
||||
* If there was an error performing the sysctl operation, the return value
|
||||
* 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 *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
|
||||
@@ -2569,23 +2590,27 @@ int xc_livepatch_get(xc_interface *xch,
|
||||
* 'left' are also updated with the number of entries filled out
|
||||
* and respectively the number of entries left to get from hypervisor.
|
||||
*
|
||||
* It is expected that the caller of this function will take the
|
||||
* 'left' and use the value for 'start'. This way we have an
|
||||
* cursor in the array. Note that the 'info','name', and 'len' will
|
||||
* be updated at the subsequent calls.
|
||||
* 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
|
||||
* 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', and 'len' arrays can
|
||||
* be filled up with.
|
||||
*
|
||||
* Each entry in the 'name' array is expected to be of XEN_LIVEPATCH_NAME_SIZE
|
||||
* length.
|
||||
* The 'max' is to be provided by the caller with the maximum number of
|
||||
* 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.
|
||||
*
|
||||
* Each entry in the 'name' array may have an arbitrary size.
|
||||
*
|
||||
* 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'.
|
||||
@@ -2595,21 +2620,26 @@ int xc_livepatch_get(xc_interface *xch,
|
||||
* will contain the number of entries that had been succesfully
|
||||
* retrieved (if any).
|
||||
*/
|
||||
int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
xen_livepatch_status_t *info, char *name,
|
||||
uint32_t *len, unsigned int *done,
|
||||
unsigned int *left);
|
||||
int xc_livepatch_list(xc_interface *xch, const unsigned int max,
|
||||
const unsigned int start,
|
||||
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);
|
||||
|
||||
/*
|
||||
* The operations are asynchronous and the hypervisor may take a while
|
||||
* to complete them. The `timeout` offers an option to expire the
|
||||
* operation if it could not be completed within the specified time
|
||||
* (in ns). Value of 0 means let hypervisor decide the best timeout.
|
||||
* The `flags` allows to pass extra parameters to the actions.
|
||||
*/
|
||||
int xc_livepatch_apply(xc_interface *xch, char *name, uint32_t timeout);
|
||||
int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout);
|
||||
int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout);
|
||||
int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout);
|
||||
int xc_livepatch_apply(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
|
||||
int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
|
||||
int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
|
||||
int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
|
||||
|
||||
/*
|
||||
* Ensure cache coherency after memory modifications. A call to this function
|
||||
|
||||
+130
-33
@@ -662,7 +662,52 @@ int xc_livepatch_get(xc_interface *xch,
|
||||
}
|
||||
|
||||
/*
|
||||
* The heart of this function is to get an array of xen_livepatch_status_t.
|
||||
* Get a number of available payloads and get actual total size of
|
||||
* 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.
|
||||
*
|
||||
* The return value is zero if the hypercall completed successfully.
|
||||
*
|
||||
* If there was an error performing the sysctl operation, the return value
|
||||
* 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 *metadata_total_size)
|
||||
{
|
||||
DECLARE_SYSCTL;
|
||||
int rc;
|
||||
|
||||
if ( !nr || !name_total_size || !metadata_total_size )
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&sysctl, 0, sizeof(sysctl));
|
||||
sysctl.cmd = XEN_SYSCTL_livepatch_op;
|
||||
sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_LIST;
|
||||
|
||||
rc = do_sysctl(xch, &sysctl);
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
*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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -673,23 +718,27 @@ int xc_livepatch_get(xc_interface *xch,
|
||||
* 'left' are also updated with the number of entries filled out
|
||||
* and respectively the number of entries left to get from hypervisor.
|
||||
*
|
||||
* It is expected that the caller of this function will take the
|
||||
* 'left' and use the value for 'start'. This way we have an
|
||||
* cursor in the array. Note that the 'info','name', and 'len' will
|
||||
* be updated at the subsequent calls.
|
||||
* 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
|
||||
* 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', and 'len' arrays can
|
||||
* be filled up with.
|
||||
*
|
||||
* Each entry in the 'name' array is expected to be of XEN_LIVEPATCH_NAME_SIZE
|
||||
* length.
|
||||
* The 'max' is to be provided by the caller with the maximum number of
|
||||
* 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.
|
||||
*
|
||||
* Each entry in the 'name' array may have an arbitrary size.
|
||||
*
|
||||
* 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'.
|
||||
@@ -699,11 +748,14 @@ int xc_livepatch_get(xc_interface *xch,
|
||||
* will contain the number of entries that had been succesfully
|
||||
* retrieved (if any).
|
||||
*/
|
||||
int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
int xc_livepatch_list(xc_interface *xch, const unsigned int max,
|
||||
const unsigned int start,
|
||||
struct xen_livepatch_status *info,
|
||||
char *name, uint32_t *len,
|
||||
unsigned int *done,
|
||||
unsigned int *left)
|
||||
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;
|
||||
DECLARE_SYSCTL;
|
||||
@@ -711,30 +763,40 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
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;
|
||||
ssize_t sz;
|
||||
uint32_t name_off = 0, metadata_off = 0;
|
||||
uint32_t name_sz, metadata_sz;
|
||||
|
||||
if ( !max || !info || !name || !len )
|
||||
if ( !max || !info || !name || !len ||
|
||||
!metadata || !metadata_len || !done || !left )
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( name_total_size == 0 )
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&sysctl, 0, sizeof(sysctl));
|
||||
sysctl.cmd = XEN_SYSCTL_livepatch_op;
|
||||
sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_LIST;
|
||||
sysctl.u.livepatch.pad = 0;
|
||||
sysctl.u.livepatch.u.list.version = 0;
|
||||
sysctl.u.livepatch.u.list.idx = start;
|
||||
sysctl.u.livepatch.u.list.pad = 0;
|
||||
|
||||
max_batch_sz = max;
|
||||
/* Convience value. */
|
||||
sz = sizeof(*name) * XEN_LIVEPATCH_NAME_SIZE;
|
||||
name_sz = name_total_size;
|
||||
metadata_sz = metadata_total_size;
|
||||
*done = 0;
|
||||
*left = 0;
|
||||
do {
|
||||
uint32_t _name_sz, _metadata_sz;
|
||||
|
||||
/*
|
||||
* The first time we go in this loop our 'max' may be bigger
|
||||
* than what the hypervisor is comfortable with - hence the first
|
||||
@@ -754,12 +816,16 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
sysctl.u.livepatch.u.list.nr = nr;
|
||||
/* Fix the size (may vary between hypercalls). */
|
||||
HYPERCALL_BOUNCE_SET_SIZE(info, nr * sizeof(*info));
|
||||
HYPERCALL_BOUNCE_SET_SIZE(name, nr * nr);
|
||||
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 + (sz * *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 )
|
||||
@@ -773,9 +839,19 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
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);
|
||||
/*
|
||||
@@ -792,6 +868,8 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
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. */
|
||||
@@ -816,6 +894,8 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -827,14 +907,26 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
break;
|
||||
}
|
||||
*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, (rc * sz));
|
||||
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;
|
||||
/* Update idx. */
|
||||
@@ -846,6 +938,8 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
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;
|
||||
@@ -854,7 +948,8 @@ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start,
|
||||
static int _xc_livepatch_action(xc_interface *xch,
|
||||
char *name,
|
||||
unsigned int action,
|
||||
uint32_t timeout)
|
||||
uint32_t timeout,
|
||||
uint32_t flags)
|
||||
{
|
||||
int rc;
|
||||
DECLARE_SYSCTL;
|
||||
@@ -880,6 +975,8 @@ static int _xc_livepatch_action(xc_interface *xch,
|
||||
sysctl.u.livepatch.pad = 0;
|
||||
sysctl.u.livepatch.u.action.cmd = action;
|
||||
sysctl.u.livepatch.u.action.timeout = timeout;
|
||||
sysctl.u.livepatch.u.action.flags = flags;
|
||||
sysctl.u.livepatch.u.action.pad = 0;
|
||||
|
||||
sysctl.u.livepatch.u.action.name = def_name;
|
||||
set_xen_guest_handle(sysctl.u.livepatch.u.action.name.name, name);
|
||||
@@ -891,24 +988,24 @@ static int _xc_livepatch_action(xc_interface *xch,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int xc_livepatch_apply(xc_interface *xch, char *name, uint32_t timeout)
|
||||
int xc_livepatch_apply(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags)
|
||||
{
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_APPLY, timeout);
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_APPLY, timeout, flags);
|
||||
}
|
||||
|
||||
int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout)
|
||||
int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags)
|
||||
{
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REVERT, timeout);
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REVERT, timeout, flags);
|
||||
}
|
||||
|
||||
int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout)
|
||||
int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags)
|
||||
{
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_UNLOAD, timeout);
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_UNLOAD, timeout, flags);
|
||||
}
|
||||
|
||||
int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout)
|
||||
int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags)
|
||||
{
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REPLACE, timeout);
|
||||
return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REPLACE, timeout, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+185
-70
@@ -23,18 +23,23 @@ void show_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"xen-livepatch: live patching tool\n"
|
||||
"Usage: xen-livepatch <command> [args]\n"
|
||||
"Usage: xen-livepatch <command> [args] [command-flags]\n"
|
||||
" <name> An unique name of payload. Up to %d characters.\n"
|
||||
"Commands:\n"
|
||||
" help display this help\n"
|
||||
" upload <name> <file> upload file <file> with <name> name\n"
|
||||
" list list payloads uploaded.\n"
|
||||
" apply <name> apply <name> patch.\n"
|
||||
" apply <name> [flags] apply <name> patch.\n"
|
||||
" Supported flags:\n"
|
||||
" --nodeps Disable inter-module buildid dependency check.\n"
|
||||
" Check only against hypervisor buildid.\n"
|
||||
" revert <name> revert name <name> patch.\n"
|
||||
" replace <name> apply <name> patch and revert all others.\n"
|
||||
" unload <name> unload name <name> patch.\n"
|
||||
" load <file> upload and apply <file>.\n"
|
||||
" name is the <file> name\n",
|
||||
" load <file> [flags] upload and apply <file> with name as the <file> name\n"
|
||||
" Supported flags:\n"
|
||||
" --nodeps Disable inter-module buildid dependency check.\n"
|
||||
" Check only against hypervisor buildid.\n",
|
||||
XEN_LIVEPATCH_NAME_SIZE);
|
||||
}
|
||||
|
||||
@@ -59,14 +64,15 @@ static const char *state2str(unsigned int state)
|
||||
return names[state];
|
||||
}
|
||||
|
||||
/* This value was choosen adhoc. It could be 42 too. */
|
||||
#define MAX_LEN 11
|
||||
static int list_func(int argc, char *argv[])
|
||||
{
|
||||
unsigned int idx, done, left, i;
|
||||
unsigned int nr, done, left, i;
|
||||
xen_livepatch_status_t *info = NULL;
|
||||
char *name = NULL;
|
||||
char *metadata = NULL;
|
||||
uint32_t *len = NULL;
|
||||
uint32_t *metadata_len = NULL;
|
||||
uint32_t name_total_size, metadata_total_size, name_off, metadata_off;
|
||||
int rc = ENOMEM;
|
||||
|
||||
if ( argc )
|
||||
@@ -74,65 +80,96 @@ static int list_func(int argc, char *argv[])
|
||||
show_help();
|
||||
return -1;
|
||||
}
|
||||
idx = left = 0;
|
||||
info = malloc(sizeof(*info) * MAX_LEN);
|
||||
done = left = 0;
|
||||
|
||||
rc = xc_livepatch_list_get_sizes(xch, &nr, &name_total_size, &metadata_total_size);
|
||||
if ( rc )
|
||||
{
|
||||
rc = errno;
|
||||
fprintf(stderr, "Failed to get list sizes.\n"
|
||||
"Error %d: %s\n",
|
||||
rc, strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ( nr == 0 )
|
||||
{
|
||||
fprintf(stdout, "Nothing to list\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
info = malloc(nr * sizeof(*info));
|
||||
if ( !info )
|
||||
return rc;
|
||||
name = malloc(sizeof(*name) * XEN_LIVEPATCH_NAME_SIZE * MAX_LEN);
|
||||
|
||||
name = malloc(name_total_size * sizeof(*name));
|
||||
if ( !name )
|
||||
goto error_name;
|
||||
|
||||
len = malloc(nr * sizeof(*len));
|
||||
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));
|
||||
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,
|
||||
metadata, metadata_len, metadata_total_size, &done, &left);
|
||||
if ( rc || done != nr || left > 0)
|
||||
{
|
||||
free(info);
|
||||
return rc;
|
||||
}
|
||||
len = malloc(sizeof(*len) * MAX_LEN);
|
||||
if ( !len ) {
|
||||
free(name);
|
||||
free(info);
|
||||
return rc;
|
||||
rc = errno;
|
||||
fprintf(stderr, "Failed to list %d/%d.\n"
|
||||
"Error %d: %s\n",
|
||||
left, nr, rc, strerror(rc));
|
||||
goto error;
|
||||
}
|
||||
|
||||
do {
|
||||
done = 0;
|
||||
/* The memset is done to catch errors. */
|
||||
memset(info, 'A', sizeof(*info) * MAX_LEN);
|
||||
memset(name, 'B', sizeof(*name) * MAX_LEN * XEN_LIVEPATCH_NAME_SIZE);
|
||||
memset(len, 'C', sizeof(*len) * MAX_LEN);
|
||||
rc = xc_livepatch_list(xch, MAX_LEN, idx, info, name, len, &done, &left);
|
||||
if ( rc )
|
||||
{
|
||||
rc = errno;
|
||||
fprintf(stderr, "Failed to list %d/%d.\n"
|
||||
"Error %d: %s\n",
|
||||
idx, left, rc, strerror(rc));
|
||||
break;
|
||||
}
|
||||
if ( !idx )
|
||||
fprintf(stdout," ID | status\n"
|
||||
"----------------------------------------+------------\n");
|
||||
fprintf(stdout," ID | status | metadata\n"
|
||||
"----------------------------------------+------------+---------------\n");
|
||||
|
||||
for ( i = 0; i < done; i++ )
|
||||
{
|
||||
unsigned int j;
|
||||
uint32_t sz;
|
||||
char *str;
|
||||
for ( i = 0; i < done; i++ )
|
||||
{
|
||||
unsigned int j;
|
||||
char *name_str = name + name_off;
|
||||
char *metadata_str = metadata + metadata_off;
|
||||
|
||||
sz = len[i];
|
||||
str = name + (i * XEN_LIVEPATCH_NAME_SIZE);
|
||||
for ( j = sz; j < XEN_LIVEPATCH_NAME_SIZE; j++ )
|
||||
str[j] = '\0';
|
||||
printf("%-40.*s| %s", len[i], name_str, state2str(info[i].state));
|
||||
if ( info[i].rc )
|
||||
printf(" (%d, %s) | ", -info[i].rc, strerror(-info[i].rc));
|
||||
else
|
||||
printf(" | ");
|
||||
|
||||
printf("%-40s| %s", str, state2str(info[i].state));
|
||||
if ( info[i].rc )
|
||||
printf(" (%d, %s)\n", -info[i].rc, strerror(-info[i].rc));
|
||||
else
|
||||
puts("");
|
||||
}
|
||||
idx += done;
|
||||
} while ( left );
|
||||
/* 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);
|
||||
|
||||
free(name);
|
||||
free(info);
|
||||
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);
|
||||
error_name:
|
||||
free(info);
|
||||
return rc;
|
||||
}
|
||||
#undef MAX_LEN
|
||||
@@ -225,12 +262,13 @@ static int upload_func(int argc, char *argv[])
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* These MUST match to the 'action_options[]' array slots. */
|
||||
/* These MUST match to the 'action_options[]' and 'flag_options[]' array slots. */
|
||||
enum {
|
||||
ACTION_APPLY = 0,
|
||||
ACTION_REVERT = 1,
|
||||
ACTION_UNLOAD = 2,
|
||||
ACTION_REPLACE = 3,
|
||||
ACTION_NUM
|
||||
};
|
||||
|
||||
struct {
|
||||
@@ -238,7 +276,7 @@ struct {
|
||||
int expected; /* The state to be in after the function. */
|
||||
const char *name;
|
||||
const char *verb;
|
||||
int (*function)(xc_interface *xch, char *name, uint32_t timeout);
|
||||
int (*function)(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
|
||||
} action_options[] = {
|
||||
{ .allow = LIVEPATCH_STATE_CHECKED,
|
||||
.expected = LIVEPATCH_STATE_APPLIED,
|
||||
@@ -266,6 +304,66 @@ struct {
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure defines supported flag options for actions.
|
||||
* It defines entries for each action and supports up to 32
|
||||
* flags per action.
|
||||
*/
|
||||
struct {
|
||||
const char *name;
|
||||
const uint32_t flag;
|
||||
} flag_options[ACTION_NUM][8 * sizeof(uint32_t)] = {
|
||||
{ /* ACTION_APPLY */
|
||||
{ .name = "--nodeps",
|
||||
.flag = LIVEPATCH_ACTION_APPLY_NODEPS,
|
||||
},
|
||||
},
|
||||
{ /* ACTION_REVERT */
|
||||
},
|
||||
{ /* ACTION_UNLOAD */
|
||||
},
|
||||
{ /* ACTION_REPLACE */
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Parse user provided action flags.
|
||||
* This function expects to only receive an array of input parameters being flags.
|
||||
* Expected action is specified via idx paramater (index of flag_options[]).
|
||||
*/
|
||||
static int get_flags(int argc, char *argv[], unsigned int idx, uint32_t *flags)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if ( !flags || idx >= ARRAY_SIZE(flag_options) )
|
||||
return -1;
|
||||
|
||||
*flags = 0;
|
||||
for ( i = 0; i < argc; i++ )
|
||||
{
|
||||
for ( j = 0; j < ARRAY_SIZE(flag_options[idx]); j++ )
|
||||
{
|
||||
if ( !flag_options[idx][j].name )
|
||||
goto error;
|
||||
|
||||
if ( !strcmp(flag_options[idx][j].name, argv[i]) )
|
||||
{
|
||||
*flags |= flag_options[idx][j].flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( j == ARRAY_SIZE(flag_options[idx]) )
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
fprintf(stderr, "Unsupported flag: %s.\n", argv[i]);
|
||||
errno = EINVAL;
|
||||
return errno;
|
||||
}
|
||||
|
||||
/* The hypervisor timeout for the live patching operation is 30 msec,
|
||||
* but it could take some time for the operation to start, so wait twice
|
||||
* that period. */
|
||||
@@ -291,8 +389,9 @@ int action_func(int argc, char *argv[], unsigned int idx)
|
||||
char name[XEN_LIVEPATCH_NAME_SIZE];
|
||||
int rc;
|
||||
xen_livepatch_status_t status;
|
||||
uint32_t flags;
|
||||
|
||||
if ( argc != 1 )
|
||||
if ( argc < 1 )
|
||||
{
|
||||
show_help();
|
||||
return -1;
|
||||
@@ -301,7 +400,10 @@ int action_func(int argc, char *argv[], unsigned int idx)
|
||||
if ( idx >= ARRAY_SIZE(action_options) )
|
||||
return -1;
|
||||
|
||||
if ( get_name(argc, argv, name) )
|
||||
if ( get_name(argc--, argv++, name) )
|
||||
return EINVAL;
|
||||
|
||||
if ( get_flags(argc, argv, idx, &flags) )
|
||||
return EINVAL;
|
||||
|
||||
/* Check initial status. */
|
||||
@@ -332,7 +434,7 @@ int action_func(int argc, char *argv[], unsigned int idx)
|
||||
if ( action_options[idx].allow & status.state )
|
||||
{
|
||||
printf("%s %s... ", action_options[idx].verb, name);
|
||||
rc = action_options[idx].function(xch, name, HYPERVISOR_TIMEOUT_NS);
|
||||
rc = action_options[idx].function(xch, name, HYPERVISOR_TIMEOUT_NS, flags);
|
||||
if ( rc )
|
||||
{
|
||||
int saved_errno = errno;
|
||||
@@ -394,17 +496,23 @@ int action_func(int argc, char *argv[], unsigned int idx)
|
||||
|
||||
static int load_func(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
char *new_argv[2];
|
||||
char *path, *name, *lastdot;
|
||||
int i, rc = ENOMEM;
|
||||
char *upload_argv[2];
|
||||
char **apply_argv, *path, *name, *lastdot;
|
||||
|
||||
if ( argc != 1 )
|
||||
if ( argc < 1 )
|
||||
{
|
||||
show_help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* apply action has <id> [flags] input requirement, which must be constructed */
|
||||
apply_argv = (char **) malloc(argc * sizeof(*apply_argv));
|
||||
if ( !apply_argv )
|
||||
return rc;
|
||||
|
||||
/* <file> */
|
||||
new_argv[1] = argv[0];
|
||||
upload_argv[1] = argv[0];
|
||||
|
||||
/* Synthesize the <id> */
|
||||
path = strdup(argv[0]);
|
||||
@@ -413,16 +521,23 @@ static int load_func(int argc, char *argv[])
|
||||
lastdot = strrchr(name, '.');
|
||||
if ( lastdot != NULL )
|
||||
*lastdot = '\0';
|
||||
new_argv[0] = name;
|
||||
upload_argv[0] = name;
|
||||
apply_argv[0] = name;
|
||||
|
||||
rc = upload_func(2 /* <id> <file> */, new_argv);
|
||||
/* Fill in all user provided flags */
|
||||
for ( i = 1; i < argc; i++ )
|
||||
apply_argv[i] = argv[i];
|
||||
|
||||
rc = upload_func(2 /* <id> <file> */, upload_argv);
|
||||
if ( rc )
|
||||
return rc;
|
||||
goto error;
|
||||
|
||||
rc = action_func(1 /* only <id> */, new_argv, ACTION_APPLY);
|
||||
rc = action_func(argc, apply_argv, ACTION_APPLY);
|
||||
if ( rc )
|
||||
action_func(1, new_argv, ACTION_UNLOAD);
|
||||
action_func(1 /* only <id> */, upload_argv, ACTION_UNLOAD);
|
||||
|
||||
error:
|
||||
free(apply_argv);
|
||||
free(path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+560
-114
@@ -28,6 +28,8 @@
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/event.h>
|
||||
|
||||
#define is_hook_enabled(hook) ({ (hook) && *(hook); })
|
||||
|
||||
/*
|
||||
* Protects against payload_list operations and also allows only one
|
||||
* caller in schedule_work.
|
||||
@@ -45,42 +47,6 @@ static LIST_HEAD(applied_list);
|
||||
static unsigned int payload_cnt;
|
||||
static unsigned int payload_version = 1;
|
||||
|
||||
/* To contain the ELF Note header. */
|
||||
struct livepatch_build_id {
|
||||
const void *p;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
struct payload {
|
||||
uint32_t state; /* One of the LIVEPATCH_STATE_*. */
|
||||
int32_t rc; /* 0 or -XEN_EXX. */
|
||||
bool reverted; /* Whether it was reverted. */
|
||||
bool safe_to_reapply; /* Can apply safely after revert. */
|
||||
struct list_head list; /* Linked to 'payload_list'. */
|
||||
const void *text_addr; /* Virtual address of .text. */
|
||||
size_t text_size; /* .. and its size. */
|
||||
const void *rw_addr; /* Virtual address of .data. */
|
||||
size_t rw_size; /* .. and its size (if any). */
|
||||
const void *ro_addr; /* Virtual address of .rodata. */
|
||||
size_t ro_size; /* .. and its size (if any). */
|
||||
unsigned int pages; /* Total pages for [text,rw,ro]_addr */
|
||||
struct list_head applied_list; /* Linked to 'applied_list'. */
|
||||
struct livepatch_func *funcs; /* The array of functions to patch. */
|
||||
unsigned int nfuncs; /* Nr of functions to patch. */
|
||||
const struct livepatch_symbol *symtab; /* All symbols. */
|
||||
const char *strtab; /* Pointer to .strtab. */
|
||||
struct virtual_region region; /* symbol, bug.frame patching and
|
||||
exception table (x86). */
|
||||
unsigned int nsyms; /* Nr of entries in .strtab and symbols. */
|
||||
struct livepatch_build_id id; /* ELFNOTE_DESC(.note.gnu.build-id) of the payload. */
|
||||
struct livepatch_build_id dep; /* ELFNOTE_DESC(.livepatch.depends). */
|
||||
livepatch_loadcall_t *const *load_funcs; /* The array of funcs to call after */
|
||||
livepatch_unloadcall_t *const *unload_funcs;/* load and unload of the payload. */
|
||||
unsigned int n_load_funcs; /* Nr of the funcs to load and execute. */
|
||||
unsigned int n_unload_funcs; /* Nr of funcs to call durung unload. */
|
||||
char name[XEN_LIVEPATCH_NAME_SIZE]; /* Name of it. */
|
||||
};
|
||||
|
||||
/* Defines an outstanding patching action. */
|
||||
struct livepatch_work
|
||||
{
|
||||
@@ -476,11 +442,33 @@ static bool section_ok(const struct livepatch_elf *elf,
|
||||
return true;
|
||||
}
|
||||
|
||||
static int xen_build_id_dep(const struct payload *payload)
|
||||
{
|
||||
const void *id = NULL;
|
||||
unsigned int len = 0;
|
||||
int rc;
|
||||
|
||||
ASSERT(payload->xen_dep.len);
|
||||
ASSERT(payload->xen_dep.p);
|
||||
|
||||
rc = xen_build_id(&id, &len);
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
if ( payload->xen_dep.len != len || memcmp(id, payload->xen_dep.p, len) ) {
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: check against hypervisor build-id failed\n",
|
||||
payload->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_special_sections(const struct livepatch_elf *elf)
|
||||
{
|
||||
unsigned int i;
|
||||
static const char *const names[] = { ELF_LIVEPATCH_FUNC,
|
||||
ELF_LIVEPATCH_DEPENDS,
|
||||
static const char *const names[] = { ELF_LIVEPATCH_DEPENDS,
|
||||
ELF_LIVEPATCH_XEN_DEPENDS,
|
||||
ELF_BUILD_ID_NOTE};
|
||||
DECLARE_BITMAP(found, ARRAY_SIZE(names)) = { 0 };
|
||||
|
||||
@@ -514,6 +502,148 @@ static int check_special_sections(const struct livepatch_elf *elf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_patching_sections(const struct livepatch_elf *elf)
|
||||
{
|
||||
unsigned int i;
|
||||
static const char *const names[] = { ELF_LIVEPATCH_FUNC,
|
||||
ELF_LIVEPATCH_LOAD_HOOKS,
|
||||
ELF_LIVEPATCH_UNLOAD_HOOKS,
|
||||
ELF_LIVEPATCH_PREAPPLY_HOOK,
|
||||
ELF_LIVEPATCH_APPLY_HOOK,
|
||||
ELF_LIVEPATCH_POSTAPPLY_HOOK,
|
||||
ELF_LIVEPATCH_PREREVERT_HOOK,
|
||||
ELF_LIVEPATCH_REVERT_HOOK,
|
||||
ELF_LIVEPATCH_POSTREVERT_HOOK};
|
||||
DECLARE_BITMAP(found, ARRAY_SIZE(names)) = { 0 };
|
||||
|
||||
/*
|
||||
* The patching sections are optional, but at least one
|
||||
* must be present. Otherwise, there is nothing to do.
|
||||
* All the existing sections must not be empty and must
|
||||
* be present at most once.
|
||||
*/
|
||||
for ( i = 0; i < ARRAY_SIZE(names); i++ )
|
||||
{
|
||||
const struct livepatch_elf_sec *sec;
|
||||
|
||||
sec = livepatch_elf_sec_by_name(elf, names[i]);
|
||||
if ( !sec )
|
||||
{
|
||||
dprintk(XENLOG_DEBUG, LIVEPATCH "%s: %s is missing\n",
|
||||
elf->name, names[i]);
|
||||
continue; /* This section is optional */
|
||||
}
|
||||
|
||||
if ( !sec->sec->sh_size )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: %s is empty\n",
|
||||
elf->name, names[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ( test_and_set_bit(i, found) )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: %s was seen more than once\n",
|
||||
elf->name, names[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Checking if at least one section is present. */
|
||||
if ( bitmap_empty(found, ARRAY_SIZE(names)) )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: Nothing to patch. Aborting...\n",
|
||||
elf->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int livepatch_verify_expectation_fn(const struct livepatch_func *func)
|
||||
{
|
||||
const livepatch_expectation_t *exp = &func->expect;
|
||||
|
||||
/* Ignore disabled expectations. */
|
||||
if ( !exp->enabled )
|
||||
return 0;
|
||||
|
||||
/* There is nothing to expect */
|
||||
if ( !func->old_addr )
|
||||
return -EFAULT;
|
||||
|
||||
if ( exp->len > sizeof(exp->data))
|
||||
return -EOVERFLOW;
|
||||
|
||||
if ( exp->rsv )
|
||||
return -EINVAL;
|
||||
|
||||
/* Incorrect expectation */
|
||||
if ( func->old_size < exp->len )
|
||||
return -ERANGE;
|
||||
|
||||
if ( memcmp(func->old_addr, exp->data, exp->len) )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: expectation failed: expected:%*phN, actual:%*phN\n",
|
||||
func->name, exp->len, exp->data, exp->len, func->old_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int livepatch_check_expectations(const struct payload *payload)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Verifying enabled expectations for all functions\n",
|
||||
payload->name);
|
||||
|
||||
for ( i = 0; i < payload->nfuncs; i++ )
|
||||
{
|
||||
const struct livepatch_func *func = &(payload->funcs[i]);
|
||||
|
||||
rc = livepatch_verify_expectation_fn(func);
|
||||
if ( rc )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: expectations of %s failed (rc=%d), aborting!\n",
|
||||
payload->name, func->name ?: "unknown", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup specified section and when exists assign its address to a specified hook.
|
||||
* Perform section pointer and size validation: single hook sections must contain a
|
||||
* single pointer only.
|
||||
*/
|
||||
#define LIVEPATCH_ASSIGN_SINGLE_HOOK(elf, hook, section_name) do { \
|
||||
const struct livepatch_elf_sec *__sec = livepatch_elf_sec_by_name(elf, section_name); \
|
||||
if ( !__sec ) \
|
||||
break; \
|
||||
if ( !section_ok(elf, __sec, sizeof(*hook)) || __sec->sec->sh_size != sizeof(*hook) ) \
|
||||
return -EINVAL; \
|
||||
hook = __sec->load_addr; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Lookup specified section and when exists assign its address to a specified hook.
|
||||
* Perform section pointer and size validation: multi hook sections must contain an
|
||||
* array whose size must be a multiple of the array's items size.
|
||||
*/
|
||||
#define LIVEPATCH_ASSIGN_MULTI_HOOK(elf, hook, nhooks, section_name) do { \
|
||||
const struct livepatch_elf_sec *__sec = livepatch_elf_sec_by_name(elf, section_name); \
|
||||
if ( !__sec ) \
|
||||
break; \
|
||||
if ( !section_ok(elf, __sec, sizeof(*hook)) ) \
|
||||
return -EINVAL; \
|
||||
hook = __sec->load_addr; \
|
||||
nhooks = __sec->sec->sh_size / sizeof(*hook); \
|
||||
} while (0)
|
||||
|
||||
static int prepare_payload(struct payload *payload,
|
||||
struct livepatch_elf *elf)
|
||||
{
|
||||
@@ -524,66 +654,60 @@ static int prepare_payload(struct payload *payload,
|
||||
const Elf_Note *n;
|
||||
|
||||
sec = livepatch_elf_sec_by_name(elf, ELF_LIVEPATCH_FUNC);
|
||||
ASSERT(sec);
|
||||
if ( !section_ok(elf, sec, sizeof(*payload->funcs)) )
|
||||
return -EINVAL;
|
||||
|
||||
payload->funcs = sec->load_addr;
|
||||
payload->nfuncs = sec->sec->sh_size / sizeof(*payload->funcs);
|
||||
|
||||
for ( i = 0; i < payload->nfuncs; i++ )
|
||||
{
|
||||
int rc;
|
||||
|
||||
f = &(payload->funcs[i]);
|
||||
|
||||
if ( f->version != LIVEPATCH_PAYLOAD_VERSION )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: Wrong version (%u). Expected %d\n",
|
||||
elf->name, f->version, LIVEPATCH_PAYLOAD_VERSION);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* 'old_addr', 'new_addr', 'new_size' can all be zero. */
|
||||
if ( !f->old_size )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: Address or size fields are zero\n",
|
||||
elf->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = arch_livepatch_verify_func(f);
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
rc = resolve_old_address(f, elf);
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
rc = livepatch_verify_distance(f);
|
||||
if ( rc )
|
||||
return rc;
|
||||
}
|
||||
|
||||
sec = livepatch_elf_sec_by_name(elf, ".livepatch.hooks.load");
|
||||
if ( sec )
|
||||
{
|
||||
if ( !section_ok(elf, sec, sizeof(*payload->load_funcs)) )
|
||||
if ( !section_ok(elf, sec, sizeof(*payload->funcs)) )
|
||||
return -EINVAL;
|
||||
|
||||
payload->load_funcs = sec->load_addr;
|
||||
payload->n_load_funcs = sec->sec->sh_size / sizeof(*payload->load_funcs);
|
||||
payload->funcs = sec->load_addr;
|
||||
payload->nfuncs = sec->sec->sh_size / sizeof(*payload->funcs);
|
||||
|
||||
for ( i = 0; i < payload->nfuncs; i++ )
|
||||
{
|
||||
int rc;
|
||||
|
||||
f = &(payload->funcs[i]);
|
||||
|
||||
if ( f->version != LIVEPATCH_PAYLOAD_VERSION )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: Wrong version (%u). Expected %d\n",
|
||||
elf->name, f->version, LIVEPATCH_PAYLOAD_VERSION);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* 'old_addr', 'new_addr', 'new_size' can all be zero. */
|
||||
if ( !f->old_size )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: Address or size fields are zero\n",
|
||||
elf->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = arch_livepatch_verify_func(f);
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
rc = resolve_old_address(f, elf);
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
rc = livepatch_verify_distance(f);
|
||||
if ( rc )
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
sec = livepatch_elf_sec_by_name(elf, ".livepatch.hooks.unload");
|
||||
if ( sec )
|
||||
{
|
||||
if ( !section_ok(elf, sec, sizeof(*payload->unload_funcs)) )
|
||||
return -EINVAL;
|
||||
LIVEPATCH_ASSIGN_MULTI_HOOK(elf, payload->load_funcs, payload->n_load_funcs, ELF_LIVEPATCH_LOAD_HOOKS);
|
||||
LIVEPATCH_ASSIGN_MULTI_HOOK(elf, payload->unload_funcs, payload->n_unload_funcs, ELF_LIVEPATCH_UNLOAD_HOOKS);
|
||||
|
||||
LIVEPATCH_ASSIGN_SINGLE_HOOK(elf, payload->hooks.apply.pre, ELF_LIVEPATCH_PREAPPLY_HOOK);
|
||||
LIVEPATCH_ASSIGN_SINGLE_HOOK(elf, payload->hooks.apply.action, ELF_LIVEPATCH_APPLY_HOOK);
|
||||
LIVEPATCH_ASSIGN_SINGLE_HOOK(elf, payload->hooks.apply.post, ELF_LIVEPATCH_POSTAPPLY_HOOK);
|
||||
|
||||
LIVEPATCH_ASSIGN_SINGLE_HOOK(elf, payload->hooks.revert.pre, ELF_LIVEPATCH_PREREVERT_HOOK);
|
||||
LIVEPATCH_ASSIGN_SINGLE_HOOK(elf, payload->hooks.revert.action, ELF_LIVEPATCH_REVERT_HOOK);
|
||||
LIVEPATCH_ASSIGN_SINGLE_HOOK(elf, payload->hooks.revert.post, ELF_LIVEPATCH_POSTREVERT_HOOK);
|
||||
|
||||
payload->unload_funcs = sec->load_addr;
|
||||
payload->n_unload_funcs = sec->sec->sh_size / sizeof(*payload->unload_funcs);
|
||||
}
|
||||
sec = livepatch_elf_sec_by_name(elf, ELF_BUILD_ID_NOTE);
|
||||
if ( sec )
|
||||
{
|
||||
@@ -632,6 +756,22 @@ static int prepare_payload(struct payload *payload,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sec = livepatch_elf_sec_by_name(elf, ELF_LIVEPATCH_XEN_DEPENDS);
|
||||
if ( sec )
|
||||
{
|
||||
n = sec->load_addr;
|
||||
|
||||
if ( sec->sec->sh_size <= sizeof(*n) )
|
||||
return -EINVAL;
|
||||
|
||||
if ( xen_build_id_check(n, sec->sec->sh_size,
|
||||
&payload->xen_dep.p, &payload->xen_dep.len) )
|
||||
return -EINVAL;
|
||||
|
||||
if ( !payload->xen_dep.len || !payload->xen_dep.p )
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Setup the virtual region with proper data. */
|
||||
region = &payload->region;
|
||||
|
||||
@@ -713,6 +853,23 @@ static int prepare_payload(struct payload *payload,
|
||||
#endif
|
||||
}
|
||||
|
||||
sec = livepatch_elf_sec_by_name(elf, ".modinfo");
|
||||
if ( sec )
|
||||
{
|
||||
if ( !section_ok(elf, sec, sizeof(*payload->metadata.data)) )
|
||||
return -EINVAL;
|
||||
|
||||
payload->metadata.data = sec->load_addr;
|
||||
payload->metadata.len = sec->sec->sh_size;
|
||||
|
||||
/* The metadata is required to consists of null terminated strings. */
|
||||
if ( payload->metadata.data[payload->metadata.len - 1] != '\0' )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: Incorrect metadata format detected\n", payload->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -760,8 +917,6 @@ static int build_symbol_table(struct payload *payload,
|
||||
struct livepatch_symbol *symtab;
|
||||
char *strtab;
|
||||
|
||||
ASSERT(payload->nfuncs);
|
||||
|
||||
/* Recall that section @0 is always NULL. */
|
||||
for ( i = 1; i < elf->nsym; i++ )
|
||||
{
|
||||
@@ -878,10 +1033,18 @@ static int load_payload_data(struct payload *payload, void *raw, size_t len)
|
||||
if ( rc )
|
||||
goto out;
|
||||
|
||||
rc = check_patching_sections(&elf);
|
||||
if ( rc )
|
||||
goto out;
|
||||
|
||||
rc = prepare_payload(payload, &elf);
|
||||
if ( rc )
|
||||
goto out;
|
||||
|
||||
rc = xen_build_id_dep(payload);
|
||||
if ( rc )
|
||||
goto out;
|
||||
|
||||
rc = build_symbol_table(payload, &elf);
|
||||
if ( rc )
|
||||
goto out;
|
||||
@@ -1001,8 +1164,8 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list)
|
||||
|
||||
if ( list->nr &&
|
||||
(!guest_handle_okay(list->status, list->nr) ||
|
||||
!guest_handle_okay(list->name, XEN_LIVEPATCH_NAME_SIZE * 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);
|
||||
@@ -1012,35 +1175,66 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list->name_total_size = 0;
|
||||
list->metadata_total_size = 0;
|
||||
if ( list->nr )
|
||||
{
|
||||
uint64_t name_offset = 0, metadata_offset = 0;
|
||||
|
||||
list_for_each_entry( data, &payload_list, list )
|
||||
{
|
||||
uint32_t len;
|
||||
uint32_t name_len, metadata_len;
|
||||
|
||||
if ( list->idx > i++ )
|
||||
continue;
|
||||
|
||||
status.state = data->state;
|
||||
status.rc = data->rc;
|
||||
len = strlen(data->name) + 1;
|
||||
|
||||
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) ||
|
||||
!guest_handle_subrange_okay(list->metadata, metadata_offset,
|
||||
metadata_offset + metadata_len - 1) )
|
||||
{
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* N.B. 'idx' != 'i'. */
|
||||
if ( __copy_to_guest_offset(list->name, idx * XEN_LIVEPATCH_NAME_SIZE,
|
||||
data->name, len) ||
|
||||
__copy_to_guest_offset(list->len, idx, &len, 1) ||
|
||||
__copy_to_guest_offset(list->status, idx, &status, 1) )
|
||||
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->metadata, metadata_offset,
|
||||
data->metadata.data, metadata_len) ||
|
||||
__copy_to_guest_offset(list->metadata_len, idx, &metadata_len, 1) )
|
||||
{
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
idx++;
|
||||
name_offset += name_len;
|
||||
metadata_offset += metadata_len;
|
||||
|
||||
if ( (idx >= list->nr) || hypercall_preempt_check() )
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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. */
|
||||
list->version = payload_version;
|
||||
spin_unlock(&payload_lock);
|
||||
@@ -1055,6 +1249,19 @@ static int livepatch_list(struct xen_sysctl_livepatch_list *list)
|
||||
* for XEN_SYSCTL_LIVEPATCH_ACTION operation (see livepatch_action).
|
||||
*/
|
||||
|
||||
static inline void livepatch_display_metadata(const struct livepatch_metadata *metadata)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if ( metadata && metadata->data && metadata->len > 0 )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "module metadata:\n");
|
||||
for ( str = metadata->data; str < (metadata->data + metadata->len); str += (strlen(str) + 1) )
|
||||
printk(XENLOG_INFO LIVEPATCH " %s\n", str);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int apply_payload(struct payload *data)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -1091,10 +1298,17 @@ static int apply_payload(struct payload *data)
|
||||
ASSERT(!local_irq_is_enabled());
|
||||
|
||||
for ( i = 0; i < data->nfuncs; i++ )
|
||||
arch_livepatch_apply(&data->funcs[i]);
|
||||
common_livepatch_apply(&data->funcs[i]);
|
||||
|
||||
arch_livepatch_revive();
|
||||
|
||||
livepatch_display_metadata(&data->metadata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void apply_payload_tail(struct payload *data)
|
||||
{
|
||||
/*
|
||||
* We need RCU variant (which has barriers) in case we crash here.
|
||||
* The applied_list is iterated by the trap code.
|
||||
@@ -1102,7 +1316,7 @@ static int apply_payload(struct payload *data)
|
||||
list_add_tail_rcu(&data->applied_list, &applied_list);
|
||||
register_virtual_region(&data->region);
|
||||
|
||||
return 0;
|
||||
data->state = LIVEPATCH_STATE_APPLIED;
|
||||
}
|
||||
|
||||
static int revert_payload(struct payload *data)
|
||||
@@ -1120,7 +1334,7 @@ static int revert_payload(struct payload *data)
|
||||
}
|
||||
|
||||
for ( i = 0; i < data->nfuncs; i++ )
|
||||
arch_livepatch_revert(&data->funcs[i]);
|
||||
common_livepatch_revert(&data->funcs[i]);
|
||||
|
||||
/*
|
||||
* Since we are running with IRQs disabled and the hooks may call common
|
||||
@@ -1135,6 +1349,11 @@ static int revert_payload(struct payload *data)
|
||||
ASSERT(!local_irq_is_enabled());
|
||||
|
||||
arch_livepatch_revive();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void revert_payload_tail(struct payload *data)
|
||||
{
|
||||
|
||||
/*
|
||||
* We need RCU variant (which has barriers) in case we crash here.
|
||||
@@ -1144,7 +1363,30 @@ static int revert_payload(struct payload *data)
|
||||
unregister_virtual_region(&data->region);
|
||||
|
||||
data->reverted = true;
|
||||
return 0;
|
||||
data->state = LIVEPATCH_STATE_CHECKED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if an action has applied the same state to all payload's functions consistently.
|
||||
*/
|
||||
static inline bool was_action_consistent(const struct payload *data, livepatch_func_state_t expected_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < data->nfuncs; i++ )
|
||||
{
|
||||
struct livepatch_func *f = &(data->funcs[i]);
|
||||
|
||||
if ( f->applied != expected_state )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: Payload has a function: '%s' with inconsistent applied state.\n",
|
||||
data->name, f->name ?: "noname");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1164,15 +1406,37 @@ static void livepatch_do_action(void)
|
||||
switch ( livepatch_work.cmd )
|
||||
{
|
||||
case LIVEPATCH_ACTION_APPLY:
|
||||
rc = apply_payload(data);
|
||||
if ( is_hook_enabled(data->hooks.apply.action) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling apply action hook function\n", data->name);
|
||||
|
||||
rc = (*data->hooks.apply.action)(data);
|
||||
}
|
||||
else
|
||||
rc = apply_payload(data);
|
||||
|
||||
if ( !was_action_consistent(data, rc ? LIVEPATCH_FUNC_NOT_APPLIED : LIVEPATCH_FUNC_APPLIED) )
|
||||
panic("livepatch: partially applied payload '%s'!\n", data->name);
|
||||
|
||||
if ( rc == 0 )
|
||||
data->state = LIVEPATCH_STATE_APPLIED;
|
||||
apply_payload_tail(data);
|
||||
break;
|
||||
|
||||
case LIVEPATCH_ACTION_REVERT:
|
||||
rc = revert_payload(data);
|
||||
if ( is_hook_enabled(data->hooks.revert.action) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling revert action hook function\n", data->name);
|
||||
|
||||
rc = (*data->hooks.revert.action)(data);
|
||||
}
|
||||
else
|
||||
rc = revert_payload(data);
|
||||
|
||||
if ( !was_action_consistent(data, rc ? LIVEPATCH_FUNC_APPLIED : LIVEPATCH_FUNC_NOT_APPLIED) )
|
||||
panic("livepatch: partially reverted payload '%s'!\n", data->name);
|
||||
|
||||
if ( rc == 0 )
|
||||
data->state = LIVEPATCH_STATE_CHECKED;
|
||||
revert_payload_tail(data);
|
||||
break;
|
||||
|
||||
case LIVEPATCH_ACTION_REPLACE:
|
||||
@@ -1183,9 +1447,20 @@ static void livepatch_do_action(void)
|
||||
*/
|
||||
list_for_each_entry_safe_reverse ( other, tmp, &applied_list, applied_list )
|
||||
{
|
||||
other->rc = revert_payload(other);
|
||||
if ( is_hook_enabled(other->hooks.revert.action) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling revert action hook function\n", other->name);
|
||||
|
||||
other->rc = (*other->hooks.revert.action)(other);
|
||||
}
|
||||
else
|
||||
other->rc = revert_payload(other);
|
||||
|
||||
if ( !was_action_consistent(other, rc ? LIVEPATCH_FUNC_APPLIED : LIVEPATCH_FUNC_NOT_APPLIED) )
|
||||
panic("livepatch: partially reverted payload '%s'!\n", other->name);
|
||||
|
||||
if ( other->rc == 0 )
|
||||
other->state = LIVEPATCH_STATE_CHECKED;
|
||||
revert_payload_tail(other);
|
||||
else
|
||||
{
|
||||
rc = -EINVAL;
|
||||
@@ -1195,9 +1470,34 @@ static void livepatch_do_action(void)
|
||||
|
||||
if ( rc == 0 )
|
||||
{
|
||||
rc = apply_payload(data);
|
||||
/*
|
||||
* Make sure all expectation requirements are met.
|
||||
* Beware all the payloads are reverted at this point.
|
||||
* If expectations are not met the system is left in a
|
||||
* completely UNPATCHED state!
|
||||
*/
|
||||
rc = livepatch_check_expectations(data);
|
||||
if ( rc )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: SYSTEM MIGHT BE INSECURE: "
|
||||
"Replace action has been aborted after reverting ALL payloads!\n", data->name);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( is_hook_enabled(data->hooks.apply.action) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling apply action hook function\n", data->name);
|
||||
|
||||
rc = (*data->hooks.apply.action)(data);
|
||||
}
|
||||
else
|
||||
rc = apply_payload(data);
|
||||
|
||||
if ( !was_action_consistent(data, rc ? LIVEPATCH_FUNC_NOT_APPLIED : LIVEPATCH_FUNC_APPLIED) )
|
||||
panic("livepatch: partially applied payload '%s'!\n", data->name);
|
||||
|
||||
if ( rc == 0 )
|
||||
data->state = LIVEPATCH_STATE_APPLIED;
|
||||
apply_payload_tail(data);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1218,6 +1518,39 @@ static bool_t is_work_scheduled(const struct payload *data)
|
||||
return livepatch_work.do_work && livepatch_work.data == data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if payload has any of the vetoing, non-atomic hooks assigned.
|
||||
* A vetoing, non-atmic hook may perform an operation that changes the
|
||||
* hypervisor state and may not be guaranteed to succeed. Result of
|
||||
* such operation may be returned and may change the livepatch workflow.
|
||||
* Such hooks may require additional cleanup actions performed by other
|
||||
* hooks. Thus they are not suitable for replace action.
|
||||
*/
|
||||
static inline bool has_payload_any_vetoing_hooks(const struct payload *payload)
|
||||
{
|
||||
return is_hook_enabled(payload->hooks.apply.pre) ||
|
||||
is_hook_enabled(payload->hooks.apply.post) ||
|
||||
is_hook_enabled(payload->hooks.revert.pre) ||
|
||||
is_hook_enabled(payload->hooks.revert.post);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if any of the already applied livepatches has any vetoing,
|
||||
* non-atomic hooks assigned.
|
||||
*/
|
||||
static inline bool livepatch_applied_have_vetoing_hooks(void)
|
||||
{
|
||||
struct payload *p;
|
||||
|
||||
list_for_each_entry ( p, &applied_list, applied_list )
|
||||
{
|
||||
if ( has_payload_any_vetoing_hooks(p) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int schedule_work(struct payload *data, uint32_t cmd, uint32_t timeout)
|
||||
{
|
||||
ASSERT(spin_is_locked(&payload_lock));
|
||||
@@ -1318,6 +1651,7 @@ void check_for_livepatch_work(void)
|
||||
{
|
||||
struct payload *p;
|
||||
unsigned int cpus;
|
||||
bool action_done = false;
|
||||
|
||||
p = livepatch_work.data;
|
||||
if ( !get_cpu_maps() )
|
||||
@@ -1370,6 +1704,7 @@ void check_for_livepatch_work(void)
|
||||
livepatch_do_action();
|
||||
/* Serialize and flush out the CPU via CPUID instruction (on x86). */
|
||||
arch_livepatch_post_action();
|
||||
action_done = true;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
@@ -1382,6 +1717,43 @@ void check_for_livepatch_work(void)
|
||||
/* put_cpu_maps has an barrier(). */
|
||||
put_cpu_maps();
|
||||
|
||||
if ( action_done )
|
||||
{
|
||||
switch ( livepatch_work.cmd )
|
||||
{
|
||||
case LIVEPATCH_ACTION_REVERT:
|
||||
if ( is_hook_enabled(p->hooks.revert.post) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling post-revert hook function with rc=%d\n",
|
||||
p->name, p->rc);
|
||||
|
||||
(*p->hooks.revert.post)(p);
|
||||
}
|
||||
break;
|
||||
|
||||
case LIVEPATCH_ACTION_APPLY:
|
||||
if ( is_hook_enabled(p->hooks.apply.post) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling post-apply hook function with rc=%d\n",
|
||||
p->name, p->rc);
|
||||
|
||||
(*p->hooks.apply.post)(p);
|
||||
}
|
||||
break;
|
||||
|
||||
case LIVEPATCH_ACTION_REPLACE:
|
||||
if ( has_payload_any_vetoing_hooks(p) )
|
||||
{
|
||||
/* It should be impossible to get here since livepatch_action() guards against that. */
|
||||
panic(LIVEPATCH "%s: REPLACE action is not supported on livepatches with vetoing hooks!\n",
|
||||
p->name);
|
||||
ASSERT_UNREACHABLE();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printk(XENLOG_INFO LIVEPATCH "%s finished %s with rc=%d\n",
|
||||
p->name, names[livepatch_work.cmd], p->rc);
|
||||
}
|
||||
@@ -1466,6 +1838,9 @@ static int livepatch_action(struct xen_sysctl_livepatch_action *action)
|
||||
char n[XEN_LIVEPATCH_NAME_SIZE];
|
||||
int rc;
|
||||
|
||||
if ( action->pad )
|
||||
return -EINVAL;
|
||||
|
||||
rc = get_name(&action->name, n);
|
||||
if ( rc )
|
||||
return rc;
|
||||
@@ -1517,6 +1892,21 @@ static int livepatch_action(struct xen_sysctl_livepatch_action *action)
|
||||
rc = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( is_hook_enabled(data->hooks.revert.pre) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling pre-revert hook function\n", data->name);
|
||||
|
||||
rc = (*data->hooks.revert.pre)(data);
|
||||
if ( rc )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: pre-revert hook failed (rc=%d), aborting!\n",
|
||||
data->name, rc);
|
||||
data->rc = rc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data->rc = -EAGAIN;
|
||||
rc = schedule_work(data, action->cmd, action->timeout);
|
||||
}
|
||||
@@ -1539,9 +1929,36 @@ static int livepatch_action(struct xen_sysctl_livepatch_action *action)
|
||||
break;
|
||||
}
|
||||
|
||||
rc = build_id_dep(data, !!list_empty(&applied_list));
|
||||
/*
|
||||
* Check if action is issued with nodeps flags to ignore module
|
||||
* stack dependencies.
|
||||
*/
|
||||
if ( !(action->flags & LIVEPATCH_ACTION_APPLY_NODEPS) )
|
||||
{
|
||||
rc = build_id_dep(data, !!list_empty(&applied_list));
|
||||
if ( rc )
|
||||
break;
|
||||
}
|
||||
|
||||
/* Make sure all expectation requirements are met. */
|
||||
rc = livepatch_check_expectations(data);
|
||||
if ( rc )
|
||||
break;
|
||||
|
||||
if ( is_hook_enabled(data->hooks.apply.pre) )
|
||||
{
|
||||
printk(XENLOG_INFO LIVEPATCH "%s: Calling pre-apply hook function\n", data->name);
|
||||
|
||||
rc = (*data->hooks.apply.pre)(data);
|
||||
if ( rc )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: pre-apply hook failed (rc=%d), aborting!\n",
|
||||
data->name, rc);
|
||||
data->rc = rc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data->rc = -EAGAIN;
|
||||
rc = schedule_work(data, action->cmd, action->timeout);
|
||||
}
|
||||
@@ -1553,6 +1970,30 @@ static int livepatch_action(struct xen_sysctl_livepatch_action *action)
|
||||
rc = build_id_dep(data, 1 /* against hypervisor. */);
|
||||
if ( rc )
|
||||
break;
|
||||
|
||||
/*
|
||||
* REPLACE action is not supported on livepatches with vetoing hooks.
|
||||
* Vetoing hooks usually perform mutating actions on the system and
|
||||
* typically exist in pairs (pre- hook doing an action and post- hook
|
||||
* undoing the action). Coalescing all hooks from all applied modules
|
||||
* cannot be performed without inspecting potential dependencies between
|
||||
* the mutating hooks and hence cannot be performed automatically by
|
||||
* the replace action. Also, the replace action cannot safely assume a
|
||||
* successful revert of all the module with vetoing hooks. When one
|
||||
* of the hooks fails due to not meeting certain conditions the whole
|
||||
* replace operation must have been reverted with all previous pre- and
|
||||
* post- hooks re-executed (which cannot be guaranteed to succeed).
|
||||
* The simplest response to this complication is disallow replace
|
||||
* action on modules with vetoing hooks.
|
||||
*/
|
||||
if ( has_payload_any_vetoing_hooks(data) || livepatch_applied_have_vetoing_hooks() )
|
||||
{
|
||||
printk(XENLOG_ERR LIVEPATCH "%s: REPLACE action is not supported on livepatches with vetoing hooks!\n",
|
||||
data->name);
|
||||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
data->rc = -EAGAIN;
|
||||
rc = schedule_work(data, action->cmd, action->timeout);
|
||||
}
|
||||
@@ -1641,6 +2082,8 @@ static void livepatch_printall(unsigned char key)
|
||||
data->name, state2str(data->state), data->state, data->text_addr,
|
||||
data->rw_addr, data->ro_addr, data->pages);
|
||||
|
||||
livepatch_display_metadata(&data->metadata);
|
||||
|
||||
for ( i = 0; i < data->nfuncs; i++ )
|
||||
{
|
||||
struct livepatch_func *f = &(data->funcs[i]);
|
||||
@@ -1663,6 +2106,9 @@ static void livepatch_printall(unsigned char key)
|
||||
|
||||
if ( data->dep.len )
|
||||
printk("depend-on=%*phN\n", data->dep.len, data->dep.p);
|
||||
|
||||
if ( data->xen_dep.len )
|
||||
printk("depend-on-xen=%*phN\n", data->xen_dep.len, data->xen_dep.p);
|
||||
}
|
||||
|
||||
spin_unlock(&payload_lock);
|
||||
|
||||
+52
-11
@@ -35,7 +35,7 @@
|
||||
#include "domctl.h"
|
||||
#include "physdev.h"
|
||||
|
||||
#define XEN_SYSCTL_INTERFACE_VERSION 0x00000012
|
||||
#define XEN_SYSCTL_INTERFACE_VERSION 0x00000013
|
||||
|
||||
/*
|
||||
* Read console content from Xen buffer ring.
|
||||
@@ -832,7 +832,7 @@ struct xen_sysctl_cpu_featureset {
|
||||
* If zero exit with success.
|
||||
*/
|
||||
|
||||
#define LIVEPATCH_PAYLOAD_VERSION 1
|
||||
#define LIVEPATCH_PAYLOAD_VERSION 2
|
||||
/*
|
||||
* .livepatch.funcs structure layout defined in the `Payload format`
|
||||
* section in the Live Patch design document.
|
||||
@@ -840,6 +840,24 @@ struct xen_sysctl_cpu_featureset {
|
||||
* We guard this with __XEN__ as toolstacks SHOULD not use it.
|
||||
*/
|
||||
#ifdef __XEN__
|
||||
#define LIVEPATCH_OPAQUE_SIZE 31
|
||||
|
||||
struct livepatch_expectation {
|
||||
uint8_t enabled : 1;
|
||||
uint8_t len : 5; /* Length of data up to LIVEPATCH_OPAQUE_SIZE
|
||||
(5 bits is enough for now) */
|
||||
uint8_t rsv : 2; /* Reserved. Zero value */
|
||||
uint8_t data[LIVEPATCH_OPAQUE_SIZE]; /* Same size as opaque[] buffer of
|
||||
struct livepatch_func. This is the
|
||||
max number of bytes to be patched */
|
||||
};
|
||||
typedef struct livepatch_expectation livepatch_expectation_t;
|
||||
|
||||
typedef enum livepatch_func_state {
|
||||
LIVEPATCH_FUNC_NOT_APPLIED,
|
||||
LIVEPATCH_FUNC_APPLIED
|
||||
} livepatch_func_state_t;
|
||||
|
||||
struct livepatch_func {
|
||||
const char *name; /* Name of function to be patched. */
|
||||
void *new_addr;
|
||||
@@ -847,7 +865,10 @@ struct livepatch_func {
|
||||
uint32_t new_size;
|
||||
uint32_t old_size;
|
||||
uint8_t version; /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
|
||||
uint8_t opaque[31];
|
||||
uint8_t opaque[LIVEPATCH_OPAQUE_SIZE];
|
||||
uint8_t applied;
|
||||
uint8_t _pad[7];
|
||||
livepatch_expectation_t expect;
|
||||
};
|
||||
typedef struct livepatch_func livepatch_func_t;
|
||||
#endif
|
||||
@@ -913,15 +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). The `status`,
|
||||
* `name`, and `len`' are updated at their designed index value (`idx`) with
|
||||
* the returned value of data.
|
||||
* payloads, `version` updated (it may be the same across hypercalls. If it varies
|
||||
* 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.
|
||||
@@ -944,13 +967,21 @@ struct xen_sysctl_livepatch_list {
|
||||
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
|
||||
MUST XEN_LIVEPATCH_NAME_SIZE in size.
|
||||
Must have nr of them. */
|
||||
may have an arbitrary length up to
|
||||
XEN_LIVEPATCH_NAME_SIZE bytes. Must have
|
||||
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. */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -970,6 +1001,16 @@ struct xen_sysctl_livepatch_action {
|
||||
/* hypervisor default. */
|
||||
/* Or upper bound of time (ns) */
|
||||
/* for operation to take. */
|
||||
|
||||
/*
|
||||
* Override default inter-module buildid dependency chain enforcement.
|
||||
* Check only if module is built for given hypervisor by comparing buildid.
|
||||
*/
|
||||
#define LIVEPATCH_ACTION_APPLY_NODEPS (1 << 0)
|
||||
uint32_t flags; /* IN: action flags. */
|
||||
/* Provide additional parameters */
|
||||
/* for an action. */
|
||||
uint32_t pad; /* IN: Always zero. */
|
||||
};
|
||||
|
||||
struct xen_sysctl_livepatch_op {
|
||||
|
||||
@@ -29,9 +29,18 @@ struct xen_sysctl_livepatch_op;
|
||||
/* Convenience define for printk. */
|
||||
#define LIVEPATCH "livepatch: "
|
||||
/* ELF payload special section names. */
|
||||
#define ELF_LIVEPATCH_FUNC ".livepatch.funcs"
|
||||
#define ELF_LIVEPATCH_DEPENDS ".livepatch.depends"
|
||||
#define ELF_BUILD_ID_NOTE ".note.gnu.build-id"
|
||||
#define ELF_LIVEPATCH_FUNC ".livepatch.funcs"
|
||||
#define ELF_LIVEPATCH_DEPENDS ".livepatch.depends"
|
||||
#define ELF_LIVEPATCH_XEN_DEPENDS ".livepatch.xen_depends"
|
||||
#define ELF_BUILD_ID_NOTE ".note.gnu.build-id"
|
||||
#define ELF_LIVEPATCH_LOAD_HOOKS ".livepatch.hooks.load"
|
||||
#define ELF_LIVEPATCH_UNLOAD_HOOKS ".livepatch.hooks.unload"
|
||||
#define ELF_LIVEPATCH_PREAPPLY_HOOK ".livepatch.hooks.preapply"
|
||||
#define ELF_LIVEPATCH_APPLY_HOOK ".livepatch.hooks.apply"
|
||||
#define ELF_LIVEPATCH_POSTAPPLY_HOOK ".livepatch.hooks.postapply"
|
||||
#define ELF_LIVEPATCH_PREREVERT_HOOK ".livepatch.hooks.prerevert"
|
||||
#define ELF_LIVEPATCH_REVERT_HOOK ".livepatch.hooks.revert"
|
||||
#define ELF_LIVEPATCH_POSTREVERT_HOOK ".livepatch.hooks.postrevert"
|
||||
/* Arbitrary limit for payload size and .bss section size. */
|
||||
#define LIVEPATCH_MAX_SIZE MB(2)
|
||||
|
||||
@@ -114,6 +123,34 @@ void arch_livepatch_post_action(void);
|
||||
|
||||
void arch_livepatch_mask(void);
|
||||
void arch_livepatch_unmask(void);
|
||||
|
||||
static inline void common_livepatch_apply(struct livepatch_func *func)
|
||||
{
|
||||
/* If the action has been already executed on this function, do nothing. */
|
||||
if ( func->applied == LIVEPATCH_FUNC_APPLIED )
|
||||
{
|
||||
printk(XENLOG_WARNING LIVEPATCH "%s: %s has been already applied before\n",
|
||||
__func__, func->name);
|
||||
return;
|
||||
}
|
||||
|
||||
arch_livepatch_apply(func);
|
||||
func->applied = LIVEPATCH_FUNC_APPLIED;
|
||||
}
|
||||
|
||||
static inline void common_livepatch_revert(struct livepatch_func *func)
|
||||
{
|
||||
/* If the apply action hasn't been executed on this function, do nothing. */
|
||||
if ( !func->old_addr || func->applied == LIVEPATCH_FUNC_NOT_APPLIED )
|
||||
{
|
||||
printk(XENLOG_WARNING LIVEPATCH "%s: %s has not been applied before\n",
|
||||
__func__, func->name);
|
||||
return;
|
||||
}
|
||||
|
||||
arch_livepatch_revert(func);
|
||||
func->applied = LIVEPATCH_FUNC_NOT_APPLIED;
|
||||
}
|
||||
#else
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
|
||||
#ifndef __XEN_LIVEPATCH_PAYLOAD_H__
|
||||
#define __XEN_LIVEPATCH_PAYLOAD_H__
|
||||
#include <xen/virtual_region.h>
|
||||
|
||||
/* To contain the ELF Note header. */
|
||||
struct livepatch_build_id {
|
||||
const void *p;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
typedef struct payload livepatch_payload_t;
|
||||
|
||||
/*
|
||||
* The following definitions are to be used in patches. They are taken
|
||||
@@ -12,6 +21,56 @@
|
||||
typedef void livepatch_loadcall_t(void);
|
||||
typedef void livepatch_unloadcall_t(void);
|
||||
|
||||
typedef int livepatch_precall_t(livepatch_payload_t *arg);
|
||||
typedef int livepatch_actioncall_t(livepatch_payload_t *arg);
|
||||
typedef void livepatch_postcall_t(livepatch_payload_t *arg);
|
||||
|
||||
struct livepatch_hooks {
|
||||
struct {
|
||||
livepatch_precall_t *const *pre;
|
||||
livepatch_actioncall_t *const *action;
|
||||
livepatch_postcall_t *const *post;
|
||||
} apply, revert;
|
||||
};
|
||||
|
||||
struct livepatch_metadata {
|
||||
const char *data; /* Ptr to .modinfo section with ASCII data. */
|
||||
uint32_t len; /* Length of the metadata section. */
|
||||
};
|
||||
|
||||
struct payload {
|
||||
uint32_t state; /* One of the LIVEPATCH_STATE_*. */
|
||||
int32_t rc; /* 0 or -XEN_EXX. */
|
||||
bool reverted; /* Whether it was reverted. */
|
||||
bool safe_to_reapply; /* Can apply safely after revert. */
|
||||
struct list_head list; /* Linked to 'payload_list'. */
|
||||
const void *text_addr; /* Virtual address of .text. */
|
||||
size_t text_size; /* .. and its size. */
|
||||
const void *rw_addr; /* Virtual address of .data. */
|
||||
size_t rw_size; /* .. and its size (if any). */
|
||||
const void *ro_addr; /* Virtual address of .rodata. */
|
||||
size_t ro_size; /* .. and its size (if any). */
|
||||
unsigned int pages; /* Total pages for [text,rw,ro]_addr */
|
||||
struct list_head applied_list; /* Linked to 'applied_list'. */
|
||||
struct livepatch_func *funcs; /* The array of functions to patch. */
|
||||
unsigned int nfuncs; /* Nr of functions to patch. */
|
||||
const struct livepatch_symbol *symtab; /* All symbols. */
|
||||
const char *strtab; /* Pointer to .strtab. */
|
||||
struct virtual_region region; /* symbol, bug.frame patching and
|
||||
exception table (x86). */
|
||||
unsigned int nsyms; /* Nr of entries in .strtab and symbols. */
|
||||
struct livepatch_build_id id; /* ELFNOTE_DESC(.note.gnu.build-id) of the payload. */
|
||||
struct livepatch_build_id dep; /* ELFNOTE_DESC(.livepatch.depends). */
|
||||
struct livepatch_build_id xen_dep; /* ELFNOTE_DESC(.livepatch.xen_depends). */
|
||||
livepatch_loadcall_t *const *load_funcs; /* The array of funcs to call after */
|
||||
livepatch_unloadcall_t *const *unload_funcs;/* load and unload of the payload. */
|
||||
struct livepatch_hooks hooks; /* Pre and post hooks for apply and revert */
|
||||
unsigned int n_load_funcs; /* Nr of the funcs to load and execute. */
|
||||
unsigned int n_unload_funcs; /* Nr of funcs to call durung unload. */
|
||||
char name[XEN_LIVEPATCH_NAME_SIZE]; /* Name of it. */
|
||||
struct livepatch_metadata metadata; /* Module meta data record */
|
||||
};
|
||||
|
||||
/*
|
||||
* LIVEPATCH_LOAD_HOOK macro
|
||||
*
|
||||
@@ -36,6 +95,30 @@ typedef void livepatch_unloadcall_t(void);
|
||||
livepatch_unloadcall_t *__weak \
|
||||
const livepatch_unload_data_##_fn __section(".livepatch.hooks.unload") = _fn;
|
||||
|
||||
#define LIVEPATCH_PREAPPLY_HOOK(_fn) \
|
||||
livepatch_precall_t *__attribute__((weak, used)) \
|
||||
const livepatch_preapply_data_##_fn __section(".livepatch.hooks.preapply") = _fn;
|
||||
|
||||
#define LIVEPATCH_APPLY_HOOK(_fn) \
|
||||
livepatch_actioncall_t *__attribute__((weak, used)) \
|
||||
const livepatch_apply_data_##_fn __section(".livepatch.hooks.apply") = _fn;
|
||||
|
||||
#define LIVEPATCH_POSTAPPLY_HOOK(_fn) \
|
||||
livepatch_postcall_t *__attribute__((weak, used)) \
|
||||
const livepatch_postapply_data_##_fn __section(".livepatch.hooks.postapply") = _fn;
|
||||
|
||||
#define LIVEPATCH_PREREVERT_HOOK(_fn) \
|
||||
livepatch_precall_t *__attribute__((weak, used)) \
|
||||
const livepatch_prerevert_data_##_fn __section(".livepatch.hooks.prerevert") = _fn;
|
||||
|
||||
#define LIVEPATCH_REVERT_HOOK(_fn) \
|
||||
livepatch_actioncall_t *__attribute__((weak, used)) \
|
||||
const livepatch_revert_data_##_fn __section(".livepatch.hooks.revert") = _fn;
|
||||
|
||||
#define LIVEPATCH_POSTREVERT_HOOK(_fn) \
|
||||
livepatch_postcall_t *__attribute__((weak, used)) \
|
||||
const livepatch_postrevert_data_##_fn __section(".livepatch.hooks.postrevert") = _fn;
|
||||
|
||||
#endif /* __XEN_LIVEPATCH_PAYLOAD_H__ */
|
||||
|
||||
/*
|
||||
|
||||
+115
-6
@@ -19,11 +19,31 @@ LIVEPATCH := xen_hello_world.livepatch
|
||||
LIVEPATCH_BYE := xen_bye_world.livepatch
|
||||
LIVEPATCH_REPLACE := xen_replace_world.livepatch
|
||||
LIVEPATCH_NOP := xen_nop.livepatch
|
||||
LIVEPATCH_NO_XEN_BUILDID := xen_no_xen_buildid.livepatch
|
||||
LIVEPATCH_PREPOST_HOOKS := xen_prepost_hooks.livepatch
|
||||
LIVEPATCH_PREPOST_HOOKS_FAIL := xen_prepost_hooks_fail.livepatch
|
||||
LIVEPATCH_ACTION_HOOKS := xen_action_hooks.livepatch
|
||||
LIVEPATCH_ACTION_HOOKS_NOFUNC := xen_action_hooks_nofunc.livepatch
|
||||
LIVEPATCH_ACTION_HOOKS_MARKER:= xen_action_hooks_marker.livepatch
|
||||
LIVEPATCH_ACTION_HOOKS_NOAPPLY:= xen_action_hooks_noapply.livepatch
|
||||
LIVEPATCH_ACTION_HOOKS_NOREVERT:= xen_action_hooks_norevert.livepatch
|
||||
LIVEPATCH_EXPECTATIONS:= xen_expectations.livepatch
|
||||
LIVEPATCH_EXPECTATIONS_FAIL:= xen_expectations_fail.livepatch
|
||||
|
||||
LIVEPATCHES += $(LIVEPATCH)
|
||||
LIVEPATCHES += $(LIVEPATCH_BYE)
|
||||
LIVEPATCHES += $(LIVEPATCH_REPLACE)
|
||||
LIVEPATCHES += $(LIVEPATCH_NOP)
|
||||
LIVEPATCHES += $(LIVEPATCH_NO_XEN_BUILDID)
|
||||
LIVEPATCHES += $(LIVEPATCH_PREPOST_HOOKS)
|
||||
LIVEPATCHES += $(LIVEPATCH_PREPOST_HOOKS_FAIL)
|
||||
LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS)
|
||||
LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOFUNC)
|
||||
LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_MARKER)
|
||||
LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOAPPLY)
|
||||
LIVEPATCHES += $(LIVEPATCH_ACTION_HOOKS_NOREVERT)
|
||||
LIVEPATCHES += $(LIVEPATCH_EXPECTATIONS)
|
||||
LIVEPATCHES += $(LIVEPATCH_EXPECTATIONS_FAIL)
|
||||
|
||||
LIVEPATCH_DEBUG_DIR ?= $(DEBUG_DIR)/xen-livepatch
|
||||
|
||||
@@ -38,7 +58,7 @@ uninstall:
|
||||
|
||||
.PHONY: clean
|
||||
clean::
|
||||
rm -f *.o .*.o.d *.livepatch config.h
|
||||
rm -f *.o .*.o.d *.livepatch config.h expect_config.h
|
||||
|
||||
#
|
||||
# To compute these values we need the binary files: xen-syms
|
||||
@@ -59,9 +79,17 @@ config.h: xen_hello_world_func.o
|
||||
xen_hello_world.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH)
|
||||
$(LIVEPATCH): xen_hello_world_func.o xen_hello_world.o note.o
|
||||
$(LIVEPATCH): xen_hello_world_func.o xen_hello_world.o note.o xen_note.o modinfo.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH) $^
|
||||
|
||||
.PHONY: modinfo.o
|
||||
modinfo.o:
|
||||
(set -e; \
|
||||
printf "LIVEPATCH_RULEZ\0") > $@.bin
|
||||
$(OBJCOPY) $(OBJCOPY_MAGIC) \
|
||||
--rename-section=.data=.modinfo,alloc,load,readonly,data,contents -S $@.bin $@
|
||||
#rm -f $@.bin
|
||||
|
||||
#
|
||||
# This target is only accessible if CONFIG_LIVEPATCH is defined, which
|
||||
# depends on $(build_id_linker) being available. Hence we do not
|
||||
@@ -78,6 +106,17 @@ note.o:
|
||||
--rename-section=.data=.livepatch.depends,alloc,load,readonly,data,contents -S $@.bin $@
|
||||
rm -f $@.bin
|
||||
|
||||
#
|
||||
# Append .livepatch.xen_depends section
|
||||
# with Xen build-id derived from xen-syms.
|
||||
#
|
||||
.PHONY: xen_note.o
|
||||
xen_note.o:
|
||||
$(OBJCOPY) -O binary --only-section=.note.gnu.build-id $(BASEDIR)/xen-syms $@.bin
|
||||
$(OBJCOPY) $(OBJCOPY_MAGIC) \
|
||||
--rename-section=.data=.livepatch.xen_depends,alloc,load,readonly,data,contents -S $@.bin $@
|
||||
rm -f $@.bin
|
||||
|
||||
#
|
||||
# Extract the build-id of the xen_hello_world.livepatch
|
||||
# (which xen_bye_world will depend on).
|
||||
@@ -92,20 +131,90 @@ hello_world_note.o: $(LIVEPATCH)
|
||||
xen_bye_world.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_BYE)
|
||||
$(LIVEPATCH_BYE): xen_bye_world_func.o xen_bye_world.o hello_world_note.o
|
||||
$(LIVEPATCH_BYE): xen_bye_world_func.o xen_bye_world.o hello_world_note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_BYE) $^
|
||||
|
||||
xen_replace_world.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_REPLACE)
|
||||
$(LIVEPATCH_REPLACE): xen_replace_world_func.o xen_replace_world.o note.o
|
||||
$(LIVEPATCH_REPLACE): xen_replace_world_func.o xen_replace_world.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_REPLACE) $^
|
||||
|
||||
xen_nop.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_NOP)
|
||||
$(LIVEPATCH_NOP): xen_nop.o note.o
|
||||
$(LIVEPATCH_NOP): xen_nop.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_NOP) $^
|
||||
|
||||
# This one always fails upon upload, because it deliberately
|
||||
# does not have a .livepatch.xen_depends (xen_note.o) section.
|
||||
xen_no_xen_buildid.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_NO_XEN_BUILDID)
|
||||
$(LIVEPATCH_NO_XEN_BUILDID): xen_nop.o note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_NO_XEN_BUILDID) $^
|
||||
|
||||
xen_prepost_hooks.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_PREPOST_HOOKS)
|
||||
$(LIVEPATCH_PREPOST_HOOKS): xen_prepost_hooks.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_PREPOST_HOOKS) $^
|
||||
|
||||
xen_prepost_hooks_fail.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_PREPOST_HOOKS_FAIL)
|
||||
$(LIVEPATCH_PREPOST_HOOKS_FAIL): xen_prepost_hooks_fail.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_PREPOST_HOOKS_FAIL) $^
|
||||
|
||||
xen_actions_hooks.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_ACTION_HOOKS)
|
||||
$(LIVEPATCH_ACTION_HOOKS): xen_action_hooks.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_ACTION_HOOKS) $^
|
||||
|
||||
.PHONY: $(LIVEPATCH_ACTION_HOOKS_NOFUNC)
|
||||
$(LIVEPATCH_ACTION_HOOKS_NOFUNC): xen_action_hooks_nofunc.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_ACTION_HOOKS_NOFUNC) $^
|
||||
|
||||
xen_actions_hooks_marker.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_ACTION_HOOKS_MARKER)
|
||||
$(LIVEPATCH_ACTION_HOOKS_MARKER): xen_action_hooks_marker.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_ACTION_HOOKS_MARKER) $^
|
||||
|
||||
xen_actions_hooks_noapply.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_ACTION_HOOKS_NOAPPLY)
|
||||
$(LIVEPATCH_ACTION_HOOKS_NOAPPLY): xen_action_hooks_marker.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_ACTION_HOOKS_NOAPPLY) $^
|
||||
|
||||
xen_actions_hooks_norevert.o: config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_ACTION_HOOKS_NOREVERT)
|
||||
$(LIVEPATCH_ACTION_HOOKS_NOREVERT): xen_action_hooks_marker.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_ACTION_HOOKS_NOREVERT) $^
|
||||
|
||||
EXPECT_BYTES_COUNT := 8
|
||||
CODE_GET_EXPECT=$(shell $(OBJDUMP) -d --insn-width=1 $(1) | sed -n -e '/<'$(2)'>:$$/,/^$$/ p' | tail -n +2 | head -n $(EXPECT_BYTES_COUNT) | awk '{$$0=$$2; printf "%s", substr($$0,length-1)}' | sed 's/.\{2\}/0x&,/g' | sed 's/^/{/;s/,$$/}/g')
|
||||
.PHONY: expect_config.h
|
||||
expect_config.h: EXPECT_BYTES=$(call CODE_GET_EXPECT,$(BASEDIR)/xen-syms,xen_extra_version)
|
||||
expect_config.h: xen_expectations.o
|
||||
(set -e; \
|
||||
echo "#define EXPECT_BYTES $(EXPECT_BYTES)"; \
|
||||
echo "#define EXPECT_BYTES_COUNT $(EXPECT_BYTES_COUNT)") > $@
|
||||
|
||||
xen_expectations.o: expect_config.h
|
||||
|
||||
.PHONY: $(LIVEPATCH_EXPECTATIONS)
|
||||
$(LIVEPATCH_EXPECTATIONS): xen_expectations.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_EXPECTATIONS) $^
|
||||
|
||||
.PHONY: $(LIVEPATCH_EXPECTATIONS_FAIL)
|
||||
$(LIVEPATCH_EXPECTATIONS_FAIL): xen_expectations_fail.o xen_hello_world_func.o note.o xen_note.o
|
||||
$(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_EXPECTATIONS_FAIL) $^
|
||||
|
||||
.PHONY: livepatch
|
||||
livepatch: $(LIVEPATCH) $(LIVEPATCH_BYE) $(LIVEPATCH_REPLACE) $(LIVEPATCH_NOP)
|
||||
livepatch: $(LIVEPATCH) $(LIVEPATCH_BYE) $(LIVEPATCH_REPLACE) $(LIVEPATCH_NOP) $(LIVEPATCH_NO_XEN_BUILDID) \
|
||||
$(LIVEPATCH_PREPOST_HOOKS) $(LIVEPATCH_PREPOST_HOOKS_FAIL) $(LIVEPATCH_ACTION_HOOKS) \
|
||||
$(LIVEPATCH_ACTION_HOOKS_NOFUNC) $(LIVEPATCH_ACTION_HOOKS_MARKER) $(LIVEPATCH_ACTION_HOOKS_NOAPPLY) \
|
||||
$(LIVEPATCH_ACTION_HOOKS_NOREVERT) $(LIVEPATCH_EXPECTATIONS) $(LIVEPATCH_EXPECTATIONS_FAIL)
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char hello_world_patch_this_fnc[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
static unsigned int apply_cnt;
|
||||
static unsigned int revert_cnt;
|
||||
|
||||
static int apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
func->applied = LIVEPATCH_FUNC_APPLIED;
|
||||
apply_cnt++;
|
||||
|
||||
printk(KERN_DEBUG "%s: applying: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
func->applied = LIVEPATCH_FUNC_NOT_APPLIED;
|
||||
revert_cnt++;
|
||||
|
||||
printk(KERN_DEBUG "%s: reverting: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void post_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
printk(KERN_DEBUG "%s: reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
BUG_ON(apply_cnt != 1 || revert_cnt != 1);
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
LIVEPATCH_APPLY_HOOK(apply_hook);
|
||||
LIVEPATCH_REVERT_HOOK(revert_hook);
|
||||
|
||||
LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = hello_world_patch_this_fnc,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = NEW_CODE_SZ,
|
||||
.old_size = OLD_CODE_SZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char hello_world_patch_this_fnc[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
static int pre_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: pre applied: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void post_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: post applied: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
static int pre_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: pre reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void post_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: post reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
|
||||
LIVEPATCH_POSTAPPLY_HOOK(post_apply_hook);
|
||||
LIVEPATCH_PREREVERT_HOOK(pre_revert_hook);
|
||||
LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = hello_world_patch_this_fnc,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = NEW_CODE_SZ,
|
||||
.old_size = OLD_CODE_SZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char hello_world_patch_this_fnc[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
static unsigned int apply_cnt;
|
||||
|
||||
static int pre_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: pre applied: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
apply_cnt++;
|
||||
printk(KERN_DEBUG "%s: applying: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return -EINVAL; /* Mark action as inconsistent */
|
||||
}
|
||||
|
||||
static void post_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(apply_cnt != 1);
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: post applied: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
static int pre_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: pre reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void post_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: post reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
LIVEPATCH_APPLY_HOOK(apply_hook);
|
||||
|
||||
LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
|
||||
LIVEPATCH_POSTAPPLY_HOOK(post_apply_hook);
|
||||
LIVEPATCH_PREREVERT_HOOK(pre_revert_hook);
|
||||
LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = hello_world_patch_this_fnc,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = NEW_CODE_SZ,
|
||||
.old_size = OLD_CODE_SZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static unsigned int apply_cnt;
|
||||
static unsigned int revert_cnt;
|
||||
|
||||
static int apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
apply_cnt++;
|
||||
printk(KERN_DEBUG "%s: applying: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
revert_cnt++;
|
||||
printk(KERN_DEBUG "%s: reverting: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void post_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
printk(KERN_DEBUG "%s: reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
BUG_ON(apply_cnt > 0 || revert_cnt > 0);
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
LIVEPATCH_APPLY_HOOK(apply_hook);
|
||||
LIVEPATCH_REVERT_HOOK(revert_hook);
|
||||
|
||||
LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char hello_world_patch_this_fnc[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
static unsigned int revert_cnt;
|
||||
|
||||
static int pre_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: pre applied: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void post_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: post applied: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
static int pre_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
|
||||
printk(KERN_DEBUG "%s: pre reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
revert_cnt++;
|
||||
printk(KERN_DEBUG "%s: reverting: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return -EINVAL; /* Mark action as inconsistent */
|
||||
}
|
||||
|
||||
static void post_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
BUG_ON(revert_cnt != 1);
|
||||
BUG_ON(func->applied != LIVEPATCH_FUNC_APPLIED);
|
||||
|
||||
/* Outside of quiesce zone: MAY TRIGGER HOST CRASH/UNDEFINED BEHAVIOR */
|
||||
arch_livepatch_quiesce();
|
||||
common_livepatch_revert(payload);
|
||||
arch_livepatch_revive();
|
||||
BUG_ON(func->applied == LIVEPATCH_FUNC_APPLIED);
|
||||
|
||||
printk(KERN_DEBUG "%s: post reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
LIVEPATCH_APPLY_HOOK(revert_hook);
|
||||
|
||||
LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
|
||||
LIVEPATCH_POSTAPPLY_HOOK(post_apply_hook);
|
||||
LIVEPATCH_PREREVERT_HOOK(pre_revert_hook);
|
||||
LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = hello_world_patch_this_fnc,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = NEW_CODE_SZ,
|
||||
.old_size = OLD_CODE_SZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "expect_config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char livepatch_exceptions_str[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_exceptions = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = livepatch_exceptions_str,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = EXPECT_BYTES_COUNT,
|
||||
.old_size = EXPECT_BYTES_COUNT,
|
||||
.expect = {
|
||||
.enabled = 1,
|
||||
.len = EXPECT_BYTES_COUNT,
|
||||
.data = EXPECT_BYTES
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char livepatch_exceptions_str[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
#define EXPECT_BYTES_COUNT 6
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_exceptions = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = livepatch_exceptions_str,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = EXPECT_BYTES_COUNT,
|
||||
.old_size = EXPECT_BYTES_COUNT,
|
||||
.expect = {
|
||||
.enabled = 1,
|
||||
.len = EXPECT_BYTES_COUNT,
|
||||
.data = { 0xDE, 0xAD, 0xC0, 0xDE, 0xBA, 0xBE }
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char hello_world_patch_this_fnc[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
static unsigned int pre_apply_cnt;
|
||||
static unsigned int post_apply_cnt;
|
||||
static unsigned int pre_revert_cnt;
|
||||
static unsigned int post_revert_cnt;
|
||||
|
||||
static unsigned int pre_revert_retry = 1;
|
||||
|
||||
static int pre_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
pre_apply_cnt++;
|
||||
printk(KERN_DEBUG "%s: applying: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void post_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
post_apply_cnt++;
|
||||
printk(KERN_DEBUG "%s: applied: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
static int pre_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
pre_revert_cnt++;
|
||||
printk(KERN_DEBUG "%s: reverting: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
/* First revert attempt always fails. Second attempt succeeds. */
|
||||
return -(pre_revert_retry--);
|
||||
}
|
||||
|
||||
static void post_revert_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
post_revert_cnt++;
|
||||
printk(KERN_DEBUG "%s: reverted: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
BUG_ON(pre_apply_cnt != 1 || post_apply_cnt != 1);
|
||||
BUG_ON(pre_revert_cnt != 2 || post_revert_cnt != 1);
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
|
||||
LIVEPATCH_POSTAPPLY_HOOK(post_apply_hook);
|
||||
LIVEPATCH_PREREVERT_HOOK(pre_revert_hook);
|
||||
LIVEPATCH_POSTREVERT_HOOK(post_revert_hook);
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = hello_world_patch_this_fnc,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = NEW_CODE_SZ,
|
||||
.old_size = OLD_CODE_SZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Amazon.com, Inc. or its affiliates. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/livepatch.h>
|
||||
#include <xen/livepatch_payload.h>
|
||||
|
||||
#include <public/sysctl.h>
|
||||
|
||||
static const char hello_world_patch_this_fnc[] = "xen_extra_version";
|
||||
extern const char *xen_hello_world(void);
|
||||
|
||||
/* This hook always fail and should prevent from loading the livepatch. */
|
||||
static int pre_apply_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
|
||||
for (i = 0; i < payload->nfuncs; i++)
|
||||
{
|
||||
struct livepatch_func *func = &payload->funcs[i];
|
||||
|
||||
printk(KERN_DEBUG "%s: pre applying: %s\n", __func__, func->name);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int unreachable_pre_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
BUG();
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void unreachable_post_hook(livepatch_payload_t *payload)
|
||||
{
|
||||
printk(KERN_DEBUG "%s: Hook starting.\n", __func__);
|
||||
BUG();
|
||||
printk(KERN_DEBUG "%s: Hook done.\n", __func__);
|
||||
}
|
||||
|
||||
LIVEPATCH_PREAPPLY_HOOK(pre_apply_hook);
|
||||
LIVEPATCH_POSTAPPLY_HOOK(unreachable_post_hook);
|
||||
LIVEPATCH_PREREVERT_HOOK(unreachable_pre_hook);
|
||||
LIVEPATCH_POSTREVERT_HOOK(unreachable_post_hook);
|
||||
|
||||
struct livepatch_func __section(".livepatch.funcs") livepatch_xen_hello_world = {
|
||||
.version = LIVEPATCH_PAYLOAD_VERSION,
|
||||
.name = hello_world_patch_this_fnc,
|
||||
.new_addr = xen_hello_world,
|
||||
.old_addr = xen_extra_version,
|
||||
.new_size = NEW_CODE_SZ,
|
||||
.old_size = OLD_CODE_SZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||