tools/misc/xen-cpuid
tools/misc/xen-detect
tools/misc/xen-tmem-list-parse
-tools/misc/xen-xsplice
+tools/misc/xen-livepatch
tools/misc/xenperf
tools/misc/xenpm
tools/misc/xen-hvmctx
xen/arch/x86/efi/disabled
xen/arch/x86/efi/mkreloc
xen/arch/x86/test/config.h
-xen/arch/x86/test/xen_hello_world.xsplice
-xen/arch/x86/test/xen_bye_world.xsplice
-xen/arch/x86/test/xen_replace_world.xsplice
+xen/arch/x86/test/xen_hello_world.livepatch
+xen/arch/x86/test/xen_bye_world.livepatch
+xen/arch/x86/test/xen_replace_world.livepatch
xen/arch/*/efi/boot.c
xen/arch/*/efi/compat.c
xen/arch/*/efi/efi.h
F: xen/arch/x86/machine_kexec.c
F: xen/arch/x86/x86_64/kexec_reloc.S
+LIVEPATCH
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+M: Ross Lagerwall <ross.lagerwall@citrix.com>
+S: Supported
+F: docs/misc/livepatch.markdown
+F: tools/misc/xen-livepatch.c
+F: xen/arch/*/livepatch*
+F: xen/common/livepatch*
+F: xen/include/xen/livepatch*
+
MACHINE CHECK (MCA) & RAS
M: Christoph Egger <chegger@amazon.de>
M: Liu Jinsong <jinsong.liu@alibaba-inc.com>
F: xen/xsm/
F: docs/misc/xsm-flask.txt
-XSPLICE
-M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
-M: Ross Lagerwall <ross.lagerwall@citrix.com>
-S: Supported
-F: docs/misc/xsplice.markdown
-F: tools/misc/xen-xsplice.c
-F: xen/arch/*/xsplice*
-F: xen/common/xsplice*
-F: xen/include/xen/xsplice*
-
THE REST
M: Andrew Cooper <andrew.cooper3@citrix.com>
M: George Dunlap <George.Dunlap@eu.citrix.com>
--- /dev/null
+# Xen Live Patching Design v1
+
+## Rationale
+
+A mechanism is required to binarily patch the running hypervisor with new
+opcodes that have come about due to primarily security updates.
+
+This document describes the design of the API that would allow us to
+upload to the hypervisor binary patches.
+
+The document is split in four sections:
+
+ * Detailed descriptions of the problem statement.
+ * Design of the data structures.
+ * Design of the hypercalls.
+ * Implementation notes that should be taken into consideration.
+
+
+## Glossary
+
+ * splice - patch in the binary code with new opcodes
+ * trampoline - a jump to a new instruction.
+ * 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.
+
+## History
+
+The document has gone under various reviews and only covers v1 design.
+
+The end of the document has a section titled `Not Yet Done` which
+outlines ideas and design for the future version of this work.
+
+## Multiple ways to patch
+
+The mechanism needs to be flexible to patch the hypervisor in multiple ways
+and be as simple as possible. The compiled code is contiguous in memory with
+no gaps - so we have no luxury of 'moving' existing code and must either
+insert a trampoline to the new code to be executed - or only modify in-place
+the code if there is sufficient space. The placement of new code has to be done
+by hypervisor and the virtual address for the new code is allocated dynamically.
+
+This implies that the hypervisor must compute the new offsets when splicing
+in the new trampoline code. Where the trampoline is added (inside
+the function we are patching or just the callers?) is also important.
+
+To lessen the amount of code in hypervisor, the consumer of the API
+is responsible for identifying which mechanism to employ and how many locations
+to patch. Combinations of modifying in-place code, adding trampoline, etc
+has to be supported. The API should allow read/write any memory within
+the hypervisor virtual address space.
+
+We must also have a mechanism to query what has been applied and a mechanism
+to revert it if needed.
+
+## Workflow
+
+The expected workflows of higher-level tools that manage multiple patches
+on production machines would be:
+
+ * The first obvious task is loading all available / suggested
+ hotpatches when they are available.
+ * Whenever new hotpatches are installed, they should be loaded too.
+ * One wants to query which modules have been loaded at runtime.
+ * If unloading is deemed safe (see unloading below), one may want to
+ support a workflow where a specific hotpatch is marked as bad and
+ unloaded.
+
+## Patching code
+
+The first mechanism to patch that comes in mind is in-place replacement.
+That is replace the affected code with new code. Unfortunately the x86
+ISA is variable size which places limits on how much space we have available
+to replace the instructions. That is not a problem if the change is smaller
+than the original opcode and we can fill it with nops. Problems will
+appear if the replacement code is longer.
+
+The second mechanism is by ti replace the call or jump to the
+old function with the address of the new function.
+
+A third mechanism is to add a jump to the new function at the
+start of the old function. N.B. The Xen hypervisor implements the third
+mechanism. See `Trampoline (e9 opcode)` section for more details.
+
+### Example of trampoline and in-place splicing
+
+As example we will assume the hypervisor does not have XSA-132 (see
+*domctl/sysctl: don't leak hypervisor stack to toolstacks*
+4ff3449f0e9d175ceb9551d3f2aecb59273f639d) and we would like to binary patch
+the hypervisor with it. The original code looks as so:
+
+<pre>
+ 48 89 e0 mov %rsp,%rax
+ 48 25 00 80 ff ff and $0xffffffffffff8000,%rax
+</pre>
+
+while the new patched hypervisor would be:
+
+<pre>
+ 48 c7 45 b8 00 00 00 00 movq $0x0,-0x48(%rbp)
+ 48 c7 45 c0 00 00 00 00 movq $0x0,-0x40(%rbp)
+ 48 c7 45 c8 00 00 00 00 movq $0x0,-0x38(%rbp)
+ 48 89 e0 mov %rsp,%rax
+ 48 25 00 80 ff ff and $0xffffffffffff8000,%rax
+</pre>
+
+This is inside the arch_do_domctl. This new change adds 21 extra
+bytes of code which alters all the offsets inside the function. To alter
+these offsets and add the extra 21 bytes of code we might not have enough
+space in .text to squeeze this in.
+
+As such we could simplify this problem by only patching the site
+which calls arch_do_domctl:
+
+<pre>
+do_domctl:
+ e8 4b b1 05 00 callq ffff82d08015fbb9 <arch_do_domctl>
+</pre>
+
+with a new address for where the new `arch_do_domctl` would be (this
+area would be allocated dynamically).
+
+Astute readers will wonder what we need to do if we were to patch `do_domctl`
+- which is not called directly by hypervisor but on behalf of the guests via
+the `compat_hypercall_table` and `hypercall_table`.
+Patching the offset in `hypercall_table` for `do_domctl:
+(ffff82d080103079 <do_domctl>:)
+
+<pre>
+
+ ffff82d08024d490: 79 30
+ ffff82d08024d492: 10 80 d0 82 ff ff
+
+</pre>
+
+with the new address where the new `do_domctl` is possible. The other
+place where it is used is in `hvm_hypercall64_table` which would need
+to be patched in a similar way. This would require an in-place splicing
+of the new virtual address of `arch_do_domctl`.
+
+In summary this example patched the callee of the affected function by
+ * allocating memory for the new code to live in,
+ * changing the virtual address in all the functions which called the old
+ code (computing the new offset, patching the callq with a new callq).
+ * changing the function pointer tables with the new virtual address of
+ the function (splicing in the new virtual address). Since this table
+ resides in the .rodata section we would need to temporarily change the
+ page table permissions during this part.
+
+However it has drawbacks - the safety checks which have to make sure
+the function is not on the stack - must also check every caller. For some
+patches this could mean - if there were an sufficient large amount of
+callers - that we would never be able to apply the update.
+
+Having the patching done at predetermined instances where the stacks
+are not deep mostly solves this problem.
+
+### Example of different trampoline patching.
+
+An alternative mechanism exists where we can insert a trampoline in the
+existing function to be patched to jump directly to the new code. This
+lessens the locations to be patched to one but it puts pressure on the
+CPU branching logic (I-cache, but it is just one unconditional jump).
+
+For this example we will assume that the hypervisor has not been compiled
+with fe2e079f642effb3d24a6e1a7096ef26e691d93e (XSA-125: *pre-fill structures
+for certain HYPERVISOR_xen_version sub-ops*) which mem-sets an structure
+in `xen_version` hypercall. This function is not called **anywhere** in
+the hypervisor (it is called by the guest) but referenced in the
+`compat_hypercall_table` and `hypercall_table` (and indirectly called
+from that). Patching the offset in `hypercall_table` for the old
+`do_xen_version` (ffff82d080112f9e <do_xen_version>)
+
+</pre>
+ ffff82d08024b270 <hypercall_table>:
+ ...
+ ffff82d08024b2f8: 9e 2f 11 80 d0 82 ff ff
+
+</pre>
+
+with the new address where the new `do_xen_version` is possible. The other
+place where it is used is in `hvm_hypercall64_table` which would need
+to be patched in a similar way. This would require an in-place splicing
+of the new virtual address of `do_xen_version`.
+
+An alternative solution would be to patch insert a trampoline in the
+old `do_xen_version' function to directly jump to the new `do_xen_version`.
+
+<pre>
+ ffff82d080112f9e do_xen_version:
+ ffff82d080112f9e: 48 c7 c0 da ff ff ff mov $0xffffffffffffffda,%rax
+ ffff82d080112fa5: 83 ff 09 cmp $0x9,%edi
+ ffff82d080112fa8: 0f 87 24 05 00 00 ja ffff82d0801134d2 ; do_xen_version+0x534
+</pre>
+
+with:
+
+<pre>
+ ffff82d080112f9e do_xen_version:
+ ffff82d080112f9e: e9 XX YY ZZ QQ jmpq [new do_xen_version]
+</pre>
+
+which would lessen the amount of patching to just one location.
+
+In summary this example patched the affected function to jump to the
+new replacement function which required:
+ * allocating memory for the new code to live in,
+ * inserting trampoline with new offset in the old function to point to the
+ new function.
+ * Optionally we can insert in the old function a trampoline jump to an function
+ providing an BUG_ON to catch errant code.
+
+The disadvantage of this are that the unconditional jump will consume a small
+I-cache penalty. However the simplicity of the patching and higher chance
+of passing safety checks make this a worthwhile option.
+
+This patching has a similar drawback as inline patching - the safety
+checks have to make sure the function is not on the stack. However
+since we are replacing at a higher level (a full function as opposed
+to various offsets within functions) the checks are simpler.
+
+Having the patching done at predetermined instances where the stacks
+are not deep mostly solves this problem as well.
+
+### Security
+
+With this method we can re-write the hypervisor - and as such we **MUST** be
+diligent in only allowing certain guests to perform this operation.
+
+Furthermore with SecureBoot or tboot, we **MUST** also verify the signature
+of the payload to be certain it came from a trusted source and integrity
+was intact.
+
+As such the hypercall **MUST** support an XSM policy to limit what the guest
+is allowed to invoke. If the system is booted with signature checking the
+signature checking will be enforced.
+
+## Design of payload format
+
+The payload **MUST** contain enough data to allow us to apply the update
+and also safely reverse it. As such we **MUST** know:
+
+ * The locations in memory to be patched. This can be determined dynamically
+ via symbols or via virtual addresses.
+ * The new code that will be patched in.
+
+This binary format can be constructed using an custom binary format but
+there are severe disadvantages of it:
+
+ * The format might need to be changed and we need an mechanism to accommodate
+ that.
+ * It has to be platform agnostic.
+ * Easily constructed using existing tools.
+
+As such having the payload in an ELF file is the sensible way. We would be
+carrying the various sets of structures (and data) in the ELF sections under
+different names and with definitions.
+
+Note that every structure has padding. This is added so that the hypervisor
+can re-use those fields as it sees fit.
+
+Earlier design attempted to ineptly explain the relations of the ELF sections
+to each other without using proper ELF mechanism (sh_info, sh_link, data
+structures using Elf types, etc). This design will explain the structures
+and how they are used together and not dig in the ELF format - except mention
+that the section names should match the structure names.
+
+The Xen Live Patch payload is a relocatable ELF binary. A typical binary would have:
+
+ * One or more .text sections.
+ * Zero or more read-only data sections.
+ * Zero or more data sections.
+ * Relocations for each of these sections.
+
+It may also have some architecture-specific sections. For example:
+
+ * Alternatives instructions.
+ * Bug frames.
+ * Exception tables.
+ * Relocations for each of these sections.
+
+The Xen Live Patch core code loads the payload as a standard ELF binary, relocates it
+and handles the architecture-specifc sections as needed. This process is much
+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.
+ * `.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.
+
+### .livepatch.funcs
+
+The `.livepatch.funcs` contains an array of livepatch_func structures
+which describe the functions to be patched:
+
+<pre>
+struct livepatch_func {
+ const char *name;
+ void *new_addr;
+ void *old_addr;
+ uint32_t new_size;
+ uint32_t old_size;
+ uint8_t version;
+ uint8_t opaque[31];
+};
+</pre>
+
+The size of the structure is 64 bytes on 64-bit hypervisors. It will be
+52 on 32-bit hypervisors.
+
+* `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
+ the payload).
+
+* `old_addr` is the address of the function to be patched and is filled in at
+ payload generation time if hypervisor function address is known. If unknown,
+ the value *MUST* be zero and the hypervisor will attempt to resolve the address.
+
+* `new_addr` is the address of the function that is replacing the old
+ function. The address is filled in during relocation. The value **MUST** be
+ the address of the new function in the file.
+
+* `old_size` and `new_size` contain the sizes of the respective functions in bytes.
+ The value of `old_size` **MUST** not be zero.
+
+* `version` is to be one.
+
+* `opaque` **MUST** be zero.
+
+The size of the `livepatch_func` array is determined from the ELF section
+size.
+
+When applying the patch the hypervisor iterates over each `livepatch_func`
+structure and the core code inserts a trampoline at `old_addr` to `new_addr`.
+The `new_addr` is altered when the ELF payload is loaded.
+
+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`.
+
+### Example of .livepatch.funcs
+
+A simple example of what a payload file can be:
+
+<pre>
+/* MUST be in sync with hypervisor. */
+struct livepatch_func {
+ const char *name;
+ void *new_addr;
+ void *old_addr;
+ uint32_t new_size;
+ uint32_t old_size;
+ uint8_t version;
+ uint8_t pad[31];
+};
+
+/* Our replacement function for xen_extra_version. */
+const char *xen_hello_world(void)
+{
+ return "Hello World";
+}
+
+static unsigned char patch_this_fnc[] = "xen_extra_version";
+
+struct livepatch_func livepatch_hello_world = {
+ .version = LIVEPATCH_PAYLOAD_VERSION,
+ .name = patch_this_fnc,
+ .new_addr = xen_hello_world,
+ .old_addr = (void *)0xffff82d08013963c, /* Extracted from xen-syms. */
+ .new_size = 13, /* To be be computed by scripts. */
+ .old_size = 13, /* -----------""--------------- */
+} __attribute__((__section__(".livepatch.funcs")));
+
+</pre>
+
+Code must be compiled with -fPIC.
+
+### .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.
+
+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
+checksum, MD5 checksum or any unique value.
+
+The size of these structures varies with the --build-id linker option.
+
+## Hypercalls
+
+We will employ the sub operations of the system management hypercall (sysctl).
+There are to be four sub-operations:
+
+ * upload the payloads.
+ * listing of payloads summary uploaded and their state.
+ * getting an particular payload summary and its state.
+ * command to apply, delete, or revert the payload.
+
+Most of the actions are asynchronous therefore the caller is responsible
+to verify that it has been applied properly by retrieving the summary of it
+and verifying that there are no error codes associated with the payload.
+
+We **MUST** make some of them asynchronous due to the nature of patching
+it requires every physical CPU to be lock-step with each other.
+The patching mechanism while an implementation detail, is not an short
+operation and as such the design **MUST** assume it will be an long-running
+operation.
+
+The sub-operations will spell out how preemption is to be handled (if at all).
+
+Furthermore it is possible to have multiple different payloads for the same
+function. As such an unique name per payload has to be visible to allow proper manipulation.
+
+The hypercall is part of the `xen_sysctl`. The top level structure contains
+one uint32_t to determine the sub-operations and one padding field which
+*MUST* always be zero.
+
+<pre>
+struct xen_sysctl_livepatch_op {
+ uint32_t cmd; /* IN: XEN_SYSCTL_LIVEPATCH_*. */
+ uint32_t pad; /* IN: Always zero. */
+ union {
+ ... see below ...
+ } u;
+};
+
+</pre>
+while the rest of hypercall specific structures are part of the this structure.
+
+### Basic type: struct xen_livepatch_name
+
+Most of the hypercalls employ an shared structure called `struct xen_livepatch_name`
+which contains:
+
+ * `name` - pointer where the string for the name is located.
+ * `size` - the size of the string
+ * `pad` - padding - to be zero.
+
+The structure is as follow:
+
+<pre>
+/*
+ * Uniquely identifies the payload. Should be human readable.
+ * Includes the NUL terminator
+ */
+#define XEN_LIVEPATCH_NAME_SIZE 128
+struct xen_livepatch_name {
+ XEN_GUEST_HANDLE_64(char) name; /* IN, pointer to name. */
+ uint16_t size; /* IN, size of name. May be upto
+ XEN_LIVEPATCH_NAME_SIZE. */
+ uint16_t pad[3]; /* IN: MUST be zero. */
+};
+</pre>
+
+### XEN_SYSCTL_LIVEPATCH_UPLOAD (0)
+
+Upload a payload to the hypervisor. The payload is verified
+against basic checks and if there are any issues the proper return code
+will be returned. The payload is not applied at this time - that is
+controlled by *XEN_SYSCTL_LIVEPATCH_ACTION*.
+
+The caller provides:
+
+ * A `struct xen_livepatch_name` called `name` which has the unique name.
+ * `size` the size of the ELF payload (in bytes).
+ * `payload` the virtual address of where the ELF payload is.
+
+The `name` could be an UUID that stays fixed forever for a given
+payload. It can be embedded into the ELF payload at creation time
+and extracted by tools.
+
+The return value is zero if the payload was succesfully uploaded.
+Otherwise an -XEN_EXX return value is provided. Duplicate `name` are not supported.
+
+The `payload` is the ELF payload as mentioned in the `Payload format` section.
+
+The structure is as follow:
+
+<pre>
+struct xen_sysctl_livepatch_upload {
+ xen_livepatch_name_t name; /* IN, name of the patch. */
+ uint64_t size; /* IN, size of the ELF file. */
+ XEN_GUEST_HANDLE_64(uint8) payload; /* IN: ELF file. */
+};
+</pre>
+
+### XEN_SYSCTL_LIVEPATCH_GET (1)
+
+Retrieve an status of an specific payload. This caller provides:
+
+ * A `struct xen_livepatch_name` called `name` which has the unique name.
+ * A `struct xen_livepatch_status` structure. The member values will
+ be over-written upon completion.
+
+Upon completion the `struct xen_livepatch_status` is updated.
+
+ * `status` - indicates the current status of the payload:
+ * *LIVEPATCH_STATUS_CHECKED* (1) loaded and the ELF payload safety checks passed.
+ * *LIVEPATCH_STATUS_APPLIED* (2) loaded, checked, and applied.
+ * No other value is possible.
+ * `rc` - -XEN_EXX type errors encountered while performing the last
+ LIVEPATCH_ACTION_* operation. The normal values can be zero or -XEN_EAGAIN which
+ respectively mean: success or operation in progress. Other values
+ imply an error occurred. If there is an error in `rc`, `status` will **NOT**
+ have changed.
+
+The return value of the hypercall is zero on success and -XEN_EXX on failure.
+(Note that the `rc`` value can be different from the return value, as in
+rc=-XEN_EAGAIN and return value can be 0).
+
+For example, supposing there is an payload:
+
+<pre>
+ status: LIVEPATCH_STATUS_CHECKED
+ rc: 0
+</pre>
+
+We apply an action - LIVEPATCH_ACTION_REVERT - to revert it (which won't work
+as we have not even applied it. Afterwards we will have:
+
+<pre>
+ status: LIVEPATCH_STATUS_CHECKED
+ rc: -XEN_EINVAL
+</pre>
+
+It has failed but it remains loaded.
+
+This operation is synchronous and does not require preemption.
+
+The structure is as follow:
+
+<pre>
+struct xen_livepatch_status {
+#define LIVEPATCH_STATUS_CHECKED 1
+#define LIVEPATCH_STATUS_APPLIED 2
+ uint32_t state; /* OUT: LIVEPATCH_STATE_*. */
+ int32_t rc; /* OUT: 0 if no error, otherwise -XEN_EXX. */
+};
+
+struct xen_sysctl_livepatch_get {
+ xen_livepatch_name_t name; /* IN, the name of the payload. */
+ xen_livepatch_status_t status; /* IN/OUT: status of the payload. */
+};
+</pre>
+
+### XEN_SYSCTL_LIVEPATCH_LIST (2)
+
+Retrieve an array of abbreviated status and names of payloads that are loaded in the
+hypervisor.
+
+The caller provides:
+
+ * `version`. Version of the payload. Caller should re-use the field provided by
+ the hypervisor. If the value differs the data is stale.
+ * `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.
+ * `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.
+ * `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).
+
+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.
+
+If the hypercall returns -XEN_E2BIG the `nr` is too big and should be
+lowered.
+
+If the hypercall returns an zero value there are no more payloads.
+
+Note that due to the asynchronous nature of hypercalls the control domain might
+have added or removed a number of payloads making this information stale. It is
+the responsibility of the toolstack to use the `version` field to check
+between each invocation. if the version differs it should discard the stale
+data and start from scratch. It is OK for the toolstack to use the new
+`version` field.
+
+The `struct xen_livepatch_status` structure contains an status of payload which includes:
+
+ * `status` - indicates the current status of the payload:
+ * *LIVEPATCH_STATUS_CHECKED* (1) loaded and the ELF payload safety checks passed.
+ * *LIVEPATCH_STATUS_APPLIED* (2) loaded, checked, and applied.
+ * No other value is possible.
+ * `rc` - -XEN_EXX type errors encountered while performing the last
+ LIVEPATCH_ACTION_* operation. The normal values can be zero or -XEN_EAGAIN which
+ respectively mean: success or operation in progress. Other values
+ imply an error occurred. If there is an error in `rc`, `status` will **NOT**
+ have changed.
+
+The structure is as follow:
+
+<pre>
+struct xen_sysctl_livepatch_list {
+ uint32_t version; /* OUT: Hypervisor stamps value.
+ If varies between calls, we are
+ getting stale data. */
+ uint32_t idx; /* IN: Index into hypervisor list. */
+ uint32_t nr; /* IN: How many status, names, and len
+ should be filled out. Can be zero to get
+ amount of payloads and version.
+ OUT: How many payloads left. */
+ uint32_t pad; /* IN: Must be zero. */
+ 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(uint32) len; /* OUT: Array of lengths of name's.
+ Must have nr of them. */
+};
+</pre>
+
+### XEN_SYSCTL_LIVEPATCH_ACTION (3)
+
+Perform an operation on the payload structure referenced by the `name` field.
+The operation request is asynchronous and the status should be retrieved
+by using either **XEN_SYSCTL_LIVEPATCH_GET** or **XEN_SYSCTL_LIVEPATCH_LIST** hypercall.
+
+The caller provides:
+
+ * A 'struct xen_livepatch_name` `name` containing the unique name.
+ * `cmd` the command requested:
+ * *LIVEPATCH_ACTION_UNLOAD* (1) unload the payload.
+ Any further hypercalls against the `name` will result in failure unless
+ **XEN_SYSCTL_LIVEPATCH_UPLOAD** hypercall is perfomed with same `name`.
+ * *LIVEPATCH_ACTION_REVERT* (2) revert the payload. If the operation takes
+ more time than the upper bound of time the `rc` in `xen_livepatch_status'
+ retrieved via **XEN_SYSCTL_LIVEPATCH_GET** will be -XEN_EBUSY.
+ * *LIVEPATCH_ACTION_APPLY* (3) apply the payload. If the operation takes
+ more time than the upper bound of time the `rc` in `xen_livepatch_status'
+ retrieved via **XEN_SYSCTL_LIVEPATCH_GET** will be -XEN_EBUSY.
+ * *LIVEPATCH_ACTION_REPLACE* (4) revert all applied payloads and apply this
+ payload. If the operation takes more time than the upper bound of time
+ the `rc` in `xen_livepatch_status' retrieved via **XEN_SYSCTL_LIVEPATCH_GET**
+ will be -XEN_EBUSY.
+ * `time` the upper bound of time (ms) the cmd should take. Zero means infinite.
+ If within the time the operation does not succeed the operation would go in
+ error state.
+ * `pad` - *MUST* be zero.
+
+The return value will be zero unless the provided fields are incorrect.
+
+The structure is as follow:
+
+<pre>
+#define LIVEPATCH_ACTION_UNLOAD 1
+#define LIVEPATCH_ACTION_REVERT 2
+#define LIVEPATCH_ACTION_APPLY 3
+#define LIVEPATCH_ACTION_REPLACE 4
+struct xen_sysctl_livepatch_action {
+ xen_livepatch_name_t name; /* IN, name of the patch. */
+ uint32_t cmd; /* IN: LIVEPATCH_ACTION_* */
+ uint32_t time; /* IN: Zero if no timeout. */
+ /* Or upper bound of time (ms) */
+ /* for operation to take. */
+};
+
+</pre>
+
+## State diagrams of LIVEPATCH_ACTION commands.
+
+There is a strict ordering state of what the commands can be.
+The LIVEPATCH_ACTION prefix has been dropped to easy reading and
+does not include the LIVEPATCH_STATES:
+
+<pre>
+ /->\
+ \ /
+ UNLOAD <--- CHECK ---> REPLACE|APPLY --> REVERT --\
+ \ |
+ \-------------------<-------------/
+
+</pre>
+## State transition table of LIVEPATCH_ACTION commands and LIVEPATCH_STATUS.
+
+Note that:
+
+ - The CHECKED state is the starting one achieved with *XEN_SYSCTL_LIVEPATCH_UPLOAD* hypercall.
+ - The REVERT operation on success will automatically move to the CHECKED state.
+ - There are two STATES: CHECKED and APPLIED.
+ - There are four actions (aka commands): APPLY, REPLACE, REVERT, and UNLOAD.
+
+The state transition table of valid states and action states:
+
+<pre>
+
++---------+---------+--------------------------------+-------+--------+
+| ACTION | Current | Result | Next STATE: |
+| ACTION | STATE | |CHECKED|APPLIED |
++---------+----------+-------------------------------+-------+--------+
+| UNLOAD | CHECKED | Unload payload. Always works. | | |
+| | | No next states. | | |
++---------+---------+--------------------------------+-------+--------+
+| APPLY | CHECKED | Apply payload (success). | | x |
++---------+---------+--------------------------------+-------+--------+
+| APPLY | CHECKED | Apply payload (error|timeout) | x | |
++---------+---------+--------------------------------+-------+--------+
+| REPLACE | CHECKED | Revert payloads and apply new | | x |
+| | | payload with success. | | |
++---------+---------+--------------------------------+-------+--------+
+| REPLACE | CHECKED | Revert payloads and apply new | x | |
+| | | payload with error. | | |
++---------+---------+--------------------------------+-------+--------+
+| REVERT | APPLIED | Revert payload (success). | x | |
++---------+---------+--------------------------------+-------+--------+
+| REVERT | APPLIED | Revert payload (error|timeout) | | x |
++---------+---------+--------------------------------+-------+--------+
+</pre>
+
+All the other state transitions are invalid.
+
+## Sequence of events.
+
+The normal sequence of events is to:
+
+ 1. *XEN_SYSCTL_LIVEPATCH_UPLOAD* to upload the payload. If there are errors *STOP* here.
+ 2. *XEN_SYSCTL_LIVEPATCH_GET* to check the `->rc`. If *-XEN_EAGAIN* spin. If zero go to next step.
+ 3. *XEN_SYSCTL_LIVEPATCH_ACTION* with *LIVEPATCH_ACTION_APPLY* to apply the patch.
+ 4. *XEN_SYSCTL_LIVEPATCH_GET* to check the `->rc`. If in *-XEN_EAGAIN* spin. If zero exit with success.
+
+
+## Addendum
+
+Implementation quirks should not be discussed in a design document.
+
+However these observations can provide aid when developing against this
+document.
+
+
+### Alternative assembler
+
+Alternative assembler is a mechanism to use different instructions depending
+on what the CPU supports. This is done by providing multiple streams of code
+that can be patched in - or if the CPU does not support it - padded with
+`nop` operations. The alternative assembler macros cause the compiler to
+expand the code to place a most generic code in place - emit a special
+ELF .section header to tag this location. During run-time the hypervisor
+can leave the areas alone or patch them with an better suited opcodes.
+
+Note that patching functions that copy to or from guest memory requires
+to support alternative support. For example this can be due to SMAP
+(specifically *stac* and *clac* operations) which is enabled on Broadwell
+and later architectures. It may be related to other alternative instructions.
+
+### When to patch
+
+During the discussion on the design two candidates bubbled where
+the call stack for each CPU would be deterministic. This would
+minimize the chance of the patch not being applied due to safety
+checks failing. Safety checks such as not patching code which
+is on the stack - which can lead to corruption.
+
+#### Rendezvous code instead of stop_machine for patching
+
+The hypervisor's time rendezvous code runs synchronously across all CPUs
+every second. Using the stop_machine to patch can stall the time rendezvous
+code and result in NMI. As such having the patching be done at the tail
+of rendezvous code should avoid this problem.
+
+However the entrance point for that code is
+do_softirq->timer_softirq_action->time_calibration
+which ends up calling on_selected_cpus on remote CPUs.
+
+The remote CPUs receive CALL_FUNCTION_VECTOR IPI and execute the
+desired function.
+
+#### Before entering the guest code.
+
+Before we call VMXResume we check whether any soft IRQs need to be executed.
+This is a good spot because all Xen stacks are effectively empty at
+that point.
+
+To randezvous all the CPUs an barrier with an maximum timeout (which
+could be adjusted), combined with forcing all other CPUs through the
+hypervisor with IPIs, can be utilized to execute lockstep instructions
+on all CPUs.
+
+The approach is similar in concept to stop_machine and the time rendezvous
+but is time-bound. However the local CPU stack is much shorter and
+a lot more deterministic.
+
+This is implemented in the Xen Project hypervisor.
+
+### Compiling the hypervisor code
+
+Hotpatch generation often requires support for compiling the target
+with -ffunction-sections / -fdata-sections. Changes would have to
+be done to the linker scripts to support this.
+
+### Generation of Live Patch ELF payloads
+
+The design of that is not discussed in this design.
+
+This is implemented in a seperate tool which lives in a seperate
+GIT repo.
+
+Currently it resides at https://github.com/rosslagerwall/livepatch-build
+
+### Exception tables and symbol tables growth
+
+We may need support for adapting or augmenting exception tables if
+patching such code. Hotpatches may need to bring their own small
+exception tables (similar to how Linux modules support this).
+
+If supporting hotpatches that introduce additional exception-locations
+is not important, one could also change the exception table in-place
+and reorder it afterwards.
+
+As found almost every patch (XSA) to a non-trivial function requires
+additional entries in the exception table and/or the bug frames.
+
+This is implemented in the Xen Project hypervisor.
+
+### .rodata sections
+
+The patching might require strings to be updated as well. As such we must be
+also able to patch the strings as needed. This sounds simple - but the compiler
+has a habit of coalescing strings that are the same - which means if we in-place
+alter the strings - other users will be inadvertently affected as well.
+
+This is also where pointers to functions live - and we may need to patch this
+as well. And switch-style jump tables.
+
+To guard against that we must be prepared to do patching similar to
+trampoline patching or in-line depending on the flavour. If we can
+do in-line patching we would need to:
+
+ * alter `.rodata` to be writeable.
+ * inline patch.
+ * alter `.rodata` to be read-only.
+
+If are doing trampoline patching we would need to:
+
+ * allocate a new memory location for the string.
+ * all locations which use this string will have to be updated to use the
+ offset to the string.
+ * mark the region RO when we are done.
+
+The trampoline patching is implemented in the Xen Project hypervisor.
+
+### .bss and .data sections.
+
+In place patching writable data is not suitable as it is unclear what should be done
+depending on the current state of data. As such it should not be attempted.
+
+However, functions which are being patched can bring in changes to strings
+(.data or .rodata section changes), or even to .bss sections.
+
+As such the ELF payload can introduce new .rodata, .bss, and .data sections.
+Patching in the new function will end up also patching in the new .rodata
+section and the new function will reference the new string in the new
+.rodata section.
+
+This is implemented in the Xen Project hypervisor.
+
+### Security
+
+Only the privileged domain should be allowed to do this operation.
+
+### Live patch interdependencies
+
+Live patch patches interdependencies are tricky.
+
+There are the ways this can be addressed:
+ * A single large patch that subsumes and replaces all previous ones.
+ Over the life-time of patching the hypervisor this large patch
+ grows to accumulate all the code changes.
+ * Hotpatch stack - where an mechanism exists that loads the hotpatches
+ in the same order they were built in. We would need an build-id
+ of the hypevisor to make sure the hot-patches are build against the
+ correct build.
+ * Payload containing the old code to check against that. That allows
+ the hotpatches to be loaded indepedently (if they don't overlap) - or
+ if the old code also containst previously patched code - even if they
+ overlap.
+
+The disadvantage of the first large patch is that it can grow over
+time and not provide an bisection mechanism to identify faulty patches.
+
+The hot-patch stack puts stricts requirements on the order of the patches
+being loaded and requires an hypervisor build-id to match against.
+
+The old code allows much more flexibility and an additional guard,
+but is more complex to implement.
+
+The second option which requires an build-id of the hypervisor
+is implemented in the Xen Project hypervisor.
+
+Specifically each payload has two build-id ELF notes:
+ * The build-id of the payload itself (generated via --build-id).
+ * 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.
+
+# Not Yet Done
+
+This is for further development of live patching.
+
+## TODO Goals
+
+The implementation must also have a mechanism for (in no particular order):
+
+ * Be able to lookup in the Xen hypervisor the symbol names of functions from the
+ ELF payload. (Either as `symbol` or `symbol`+`offset`).
+ * Be able to patch .rodata, .bss, and .data sections.
+ * Deal with NMI/MCE checks during patching instead of ignoring them.
+ * Further safety checks (blacklist of which functions cannot be patched, check
+ the stack, make sure the payload is built with same compiler as hypervisor).
+ Specifically we want to make sure that live patching codepaths cannot be patched.
+ * NOP out the code sequence if `new_size` is zero.
+ * Deal with other relocation types: R_X86_64_[8,16,32,32S], R_X86_64_PC[8,16,64]
+ in payload file.
+
+### Handle inlined __LINE__
+
+This problem is related to hotpatch construction
+and potentially has influence on the design of the hotpatching
+infrastructure in Xen.
+
+For example:
+
+We have file1.c with functions f1 and f2 (in that order). f2 contains a
+BUG() (or WARN()) macro and at that point embeds the source line number
+into the generated code for f2.
+
+Now we want to hotpatch f1 and the hotpatch source-code patch adds 2
+lines to f1 and as a consequence shifts out f2 by two lines. The newly
+constructed file1.o will now contain differences in both binary
+functions f1 (because we actually changed it with the applied patch) and
+f2 (because the contained BUG macro embeds the new line number).
+
+Without additional information, an algorithm comparing file1.o before
+and after hotpatch application will determine both functions to be
+changed and will have to include both into the binary hotpatch.
+
+Options:
+
+1. Transform source code patches for hotpatches to be line-neutral for
+ each chunk. This can be done in almost all cases with either
+ reformatting of the source code or by introducing artificial
+ preprocessor "#line n" directives to adjust for the introduced
+ differences.
+
+ This approach is low-tech and simple. Potentially generated
+ backtraces and existing debug information refers to the original
+ build and does not reflect hotpatching state except for actually
+ hotpatched functions but should be mostly correct.
+
+2. Ignoring the problem and living with artificially large hotpatches
+ that unnecessarily patch many functions.
+
+ This approach might lead to some very large hotpatches depending on
+ content of specific source file. It may also trigger pulling in
+ functions into the hotpatch that cannot reasonable be hotpatched due
+ to limitations of a hotpatching framework (init-sections, parts of
+ the hotpatching framework itself, ...) and may thereby prevent us
+ from patching a specific problem.
+
+ The decision between 1. and 2. can be made on a patch--by-patch
+ basis.
+
+3. Introducing an indirection table for storing line numbers and
+ treating that specially for binary diffing. Linux may follow
+ this approach.
+
+ We might either use this indirection table for runtime use and patch
+ that with each hotpatch (similarly to exception tables) or we might
+ purely use it when building hotpatches to ignore functions that only
+ differ at exactly the location where a line-number is embedded.
+
+For BUG(), WARN(), etc., the line number is embedded into the bug frame, not
+the function itself.
+
+Similar considerations are true to a lesser extent for __FILE__, but it
+could be argued that file renaming should be done outside of hotpatches.
+
+## Signature checking requirements.
+
+The signature checking requires that the layout of the data in memory
+**MUST** be same for signature to be verified. This means that the payload
+data layout in ELF format **MUST** match what the hypervisor would be
+expecting such that it can properly do signature verification.
+
+The signature is based on the all of the payloads continuously laid out
+in memory. The signature is to be appended at the end of the ELF payload
+prefixed with the string `'~Module signature appended~\n'`, followed by
+an signature header then followed by the signature, key identifier, and signers
+name.
+
+Specifically the signature header would be:
+
+<pre>
+#define PKEY_ALGO_DSA 0
+#define PKEY_ALGO_RSA 1
+
+#define PKEY_ID_PGP 0 /* OpenPGP generated key ID */
+#define PKEY_ID_X509 1 /* X.509 arbitrary subjectKeyIdentifier */
+
+#define HASH_ALGO_MD4 0
+#define HASH_ALGO_MD5 1
+#define HASH_ALGO_SHA1 2
+#define HASH_ALGO_RIPE_MD_160 3
+#define HASH_ALGO_SHA256 4
+#define HASH_ALGO_SHA384 5
+#define HASH_ALGO_SHA512 6
+#define HASH_ALGO_SHA224 7
+#define HASH_ALGO_RIPE_MD_128 8
+#define HASH_ALGO_RIPE_MD_256 9
+#define HASH_ALGO_RIPE_MD_320 10
+#define HASH_ALGO_WP_256 11
+#define HASH_ALGO_WP_384 12
+#define HASH_ALGO_WP_512 13
+#define HASH_ALGO_TGR_128 14
+#define HASH_ALGO_TGR_160 15
+#define HASH_ALGO_TGR_192 16
+
+
+struct elf_payload_signature {
+ u8 algo; /* Public-key crypto algorithm PKEY_ALGO_*. */
+ u8 hash; /* Digest algorithm: HASH_ALGO_*. */
+ u8 id_type; /* Key identifier type PKEY_ID*. */
+ u8 signer_len; /* Length of signer's name */
+ u8 key_id_len; /* Length of key identifier */
+ u8 __pad[3];
+ __be32 sig_len; /* Length of signature data */
+};
+
+</pre>
+(Note that this has been borrowed from Linux module signature code.).
+
+
+### .bss and .data sections.
+
+In place patching writable data is not suitable as it is unclear what should be done
+depending on the current state of data. As such it should not be attempted.
+
+That said we should provide hook functions so that the existing data
+can be changed during payload application.
+
+
+### Inline patching
+
+The hypervisor should verify that the in-place patching would fit within
+the code or data.
+
+### Trampoline (e9 opcode)
+
+The e9 opcode used for jmpq uses a 32-bit signed displacement. That means
+we are limited to up to 2GB of virtual address to place the new code
+from the old code. That should not be a problem since Xen hypervisor has
+a very small footprint.
+
+However if we need - we can always add two trampolines. One at the 2GB
+limit that calls the next trampoline.
+
+Please note there is a small limitation for trampolines in
+function entries: The target function (+ trailing padding) must be able
+to accomodate the trampoline. On x86 with +-2 GB relative jumps,
+this means 5 bytes are required.
+
+Depending on compiler settings, there are several functions in Xen that
+are smaller (without inter-function padding).
+
+<pre>
+readelf -sW xen-syms | grep " FUNC " | \
+ awk '{ if ($3 < 5) print $3, $4, $5, $8 }'
+
+...
+3 FUNC LOCAL wbinvd_ipi
+3 FUNC LOCAL shadow_l1_index
+...
+</pre>
+A compile-time check for, e.g., a minimum alignment of functions or a
+runtime check that verifies symbol size (+ padding to next symbols) for
+that in the hypervisor is advised.
+
+The tool for generating payloads currently does perform a compile-time
+check to ensure that the function to be replaced is large enough.
+
+++ /dev/null
-# xSplice Design v1
-
-## Rationale
-
-A mechanism is required to binarily patch the running hypervisor with new
-opcodes that have come about due to primarily security updates.
-
-This document describes the design of the API that would allow us to
-upload to the hypervisor binary patches.
-
-The document is split in four sections:
-
- * Detailed descriptions of the problem statement.
- * Design of the data structures.
- * Design of the hypercalls.
- * Implementation notes that should be taken into consideration.
-
-
-## Glossary
-
- * splice - patch in the binary code with new opcodes
- * trampoline - a jump to a new instruction.
- * 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.
-
-## History
-
-The document has gone under various reviews and only covers v1 design.
-
-The end of the document has a section titled `Not Yet Done` which
-outlines ideas and design for the future version of this work.
-
-## Multiple ways to patch
-
-The mechanism needs to be flexible to patch the hypervisor in multiple ways
-and be as simple as possible. The compiled code is contiguous in memory with
-no gaps - so we have no luxury of 'moving' existing code and must either
-insert a trampoline to the new code to be executed - or only modify in-place
-the code if there is sufficient space. The placement of new code has to be done
-by hypervisor and the virtual address for the new code is allocated dynamically.
-
-This implies that the hypervisor must compute the new offsets when splicing
-in the new trampoline code. Where the trampoline is added (inside
-the function we are patching or just the callers?) is also important.
-
-To lessen the amount of code in hypervisor, the consumer of the API
-is responsible for identifying which mechanism to employ and how many locations
-to patch. Combinations of modifying in-place code, adding trampoline, etc
-has to be supported. The API should allow read/write any memory within
-the hypervisor virtual address space.
-
-We must also have a mechanism to query what has been applied and a mechanism
-to revert it if needed.
-
-## Workflow
-
-The expected workflows of higher-level tools that manage multiple patches
-on production machines would be:
-
- * The first obvious task is loading all available / suggested
- hotpatches when they are available.
- * Whenever new hotpatches are installed, they should be loaded too.
- * One wants to query which modules have been loaded at runtime.
- * If unloading is deemed safe (see unloading below), one may want to
- support a workflow where a specific hotpatch is marked as bad and
- unloaded.
-
-## Patching code
-
-The first mechanism to patch that comes in mind is in-place replacement.
-That is replace the affected code with new code. Unfortunately the x86
-ISA is variable size which places limits on how much space we have available
-to replace the instructions. That is not a problem if the change is smaller
-than the original opcode and we can fill it with nops. Problems will
-appear if the replacement code is longer.
-
-The second mechanism is by ti replace the call or jump to the
-old function with the address of the new function.
-
-A third mechanism is to add a jump to the new function at the
-start of the old function. N.B. The Xen hypervisor implements the third
-mechanism. See `Trampoline (e9 opcode)` section for more details.
-
-### Example of trampoline and in-place splicing
-
-As example we will assume the hypervisor does not have XSA-132 (see
-*domctl/sysctl: don't leak hypervisor stack to toolstacks*
-4ff3449f0e9d175ceb9551d3f2aecb59273f639d) and we would like to binary patch
-the hypervisor with it. The original code looks as so:
-
-<pre>
- 48 89 e0 mov %rsp,%rax
- 48 25 00 80 ff ff and $0xffffffffffff8000,%rax
-</pre>
-
-while the new patched hypervisor would be:
-
-<pre>
- 48 c7 45 b8 00 00 00 00 movq $0x0,-0x48(%rbp)
- 48 c7 45 c0 00 00 00 00 movq $0x0,-0x40(%rbp)
- 48 c7 45 c8 00 00 00 00 movq $0x0,-0x38(%rbp)
- 48 89 e0 mov %rsp,%rax
- 48 25 00 80 ff ff and $0xffffffffffff8000,%rax
-</pre>
-
-This is inside the arch_do_domctl. This new change adds 21 extra
-bytes of code which alters all the offsets inside the function. To alter
-these offsets and add the extra 21 bytes of code we might not have enough
-space in .text to squeeze this in.
-
-As such we could simplify this problem by only patching the site
-which calls arch_do_domctl:
-
-<pre>
-do_domctl:
- e8 4b b1 05 00 callq ffff82d08015fbb9 <arch_do_domctl>
-</pre>
-
-with a new address for where the new `arch_do_domctl` would be (this
-area would be allocated dynamically).
-
-Astute readers will wonder what we need to do if we were to patch `do_domctl`
-- which is not called directly by hypervisor but on behalf of the guests via
-the `compat_hypercall_table` and `hypercall_table`.
-Patching the offset in `hypercall_table` for `do_domctl:
-(ffff82d080103079 <do_domctl>:)
-
-<pre>
-
- ffff82d08024d490: 79 30
- ffff82d08024d492: 10 80 d0 82 ff ff
-
-</pre>
-
-with the new address where the new `do_domctl` is possible. The other
-place where it is used is in `hvm_hypercall64_table` which would need
-to be patched in a similar way. This would require an in-place splicing
-of the new virtual address of `arch_do_domctl`.
-
-In summary this example patched the callee of the affected function by
- * allocating memory for the new code to live in,
- * changing the virtual address in all the functions which called the old
- code (computing the new offset, patching the callq with a new callq).
- * changing the function pointer tables with the new virtual address of
- the function (splicing in the new virtual address). Since this table
- resides in the .rodata section we would need to temporarily change the
- page table permissions during this part.
-
-However it has drawbacks - the safety checks which have to make sure
-the function is not on the stack - must also check every caller. For some
-patches this could mean - if there were an sufficient large amount of
-callers - that we would never be able to apply the update.
-
-Having the patching done at predetermined instances where the stacks
-are not deep mostly solves this problem.
-
-### Example of different trampoline patching.
-
-An alternative mechanism exists where we can insert a trampoline in the
-existing function to be patched to jump directly to the new code. This
-lessens the locations to be patched to one but it puts pressure on the
-CPU branching logic (I-cache, but it is just one unconditional jump).
-
-For this example we will assume that the hypervisor has not been compiled
-with fe2e079f642effb3d24a6e1a7096ef26e691d93e (XSA-125: *pre-fill structures
-for certain HYPERVISOR_xen_version sub-ops*) which mem-sets an structure
-in `xen_version` hypercall. This function is not called **anywhere** in
-the hypervisor (it is called by the guest) but referenced in the
-`compat_hypercall_table` and `hypercall_table` (and indirectly called
-from that). Patching the offset in `hypercall_table` for the old
-`do_xen_version` (ffff82d080112f9e <do_xen_version>)
-
-</pre>
- ffff82d08024b270 <hypercall_table>:
- ...
- ffff82d08024b2f8: 9e 2f 11 80 d0 82 ff ff
-
-</pre>
-
-with the new address where the new `do_xen_version` is possible. The other
-place where it is used is in `hvm_hypercall64_table` which would need
-to be patched in a similar way. This would require an in-place splicing
-of the new virtual address of `do_xen_version`.
-
-An alternative solution would be to patch insert a trampoline in the
-old `do_xen_version' function to directly jump to the new `do_xen_version`.
-
-<pre>
- ffff82d080112f9e do_xen_version:
- ffff82d080112f9e: 48 c7 c0 da ff ff ff mov $0xffffffffffffffda,%rax
- ffff82d080112fa5: 83 ff 09 cmp $0x9,%edi
- ffff82d080112fa8: 0f 87 24 05 00 00 ja ffff82d0801134d2 ; do_xen_version+0x534
-</pre>
-
-with:
-
-<pre>
- ffff82d080112f9e do_xen_version:
- ffff82d080112f9e: e9 XX YY ZZ QQ jmpq [new do_xen_version]
-</pre>
-
-which would lessen the amount of patching to just one location.
-
-In summary this example patched the affected function to jump to the
-new replacement function which required:
- * allocating memory for the new code to live in,
- * inserting trampoline with new offset in the old function to point to the
- new function.
- * Optionally we can insert in the old function a trampoline jump to an function
- providing an BUG_ON to catch errant code.
-
-The disadvantage of this are that the unconditional jump will consume a small
-I-cache penalty. However the simplicity of the patching and higher chance
-of passing safety checks make this a worthwhile option.
-
-This patching has a similar drawback as inline patching - the safety
-checks have to make sure the function is not on the stack. However
-since we are replacing at a higher level (a full function as opposed
-to various offsets within functions) the checks are simpler.
-
-Having the patching done at predetermined instances where the stacks
-are not deep mostly solves this problem as well.
-
-### Security
-
-With this method we can re-write the hypervisor - and as such we **MUST** be
-diligent in only allowing certain guests to perform this operation.
-
-Furthermore with SecureBoot or tboot, we **MUST** also verify the signature
-of the payload to be certain it came from a trusted source and integrity
-was intact.
-
-As such the hypercall **MUST** support an XSM policy to limit what the guest
-is allowed to invoke. If the system is booted with signature checking the
-signature checking will be enforced.
-
-## Design of payload format
-
-The payload **MUST** contain enough data to allow us to apply the update
-and also safely reverse it. As such we **MUST** know:
-
- * The locations in memory to be patched. This can be determined dynamically
- via symbols or via virtual addresses.
- * The new code that will be patched in.
-
-This binary format can be constructed using an custom binary format but
-there are severe disadvantages of it:
-
- * The format might need to be changed and we need an mechanism to accommodate
- that.
- * It has to be platform agnostic.
- * Easily constructed using existing tools.
-
-As such having the payload in an ELF file is the sensible way. We would be
-carrying the various sets of structures (and data) in the ELF sections under
-different names and with definitions.
-
-Note that every structure has padding. This is added so that the hypervisor
-can re-use those fields as it sees fit.
-
-Earlier design attempted to ineptly explain the relations of the ELF sections
-to each other without using proper ELF mechanism (sh_info, sh_link, data
-structures using Elf types, etc). This design will explain the structures
-and how they are used together and not dig in the ELF format - except mention
-that the section names should match the structure names.
-
-The xSplice payload is a relocatable ELF binary. A typical binary would have:
-
- * One or more .text sections.
- * Zero or more read-only data sections.
- * Zero or more data sections.
- * Relocations for each of these sections.
-
-It may also have some architecture-specific sections. For example:
-
- * Alternatives instructions.
- * Bug frames.
- * Exception tables.
- * Relocations for each of these sections.
-
-The xSplice core code loads the payload as a standard ELF binary, relocates it
-and handles the architecture-specifc sections as needed. This process is much
-like what the Linux kernel module loader does.
-
-The payload contains at least three sections:
-
- * `.xsplice.funcs` - which is an array of xsplice_patch_func structures.
- * `.xsplice.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.
-
-### .xsplice.funcs
-
-The `.xsplice.funcs` contains an array of xsplice_patch_func structures
-which describe the functions to be patched:
-
-<pre>
-struct xsplice_patch_func {
- const char *name;
- void *new_addr;
- void *old_addr;
- uint32_t new_size;
- uint32_t old_size;
- uint8_t version;
- uint8_t opaque[31];
-};
-</pre>
-
-The size of the structure is 64 bytes on 64-bit hypervisors. It will be
-52 on 32-bit hypervisors.
-
-* `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
- the payload).
-
-* `old_addr` is the address of the function to be patched and is filled in at
- payload generation time if hypervisor function address is known. If unknown,
- the value *MUST* be zero and the hypervisor will attempt to resolve the address.
-
-* `new_addr` is the address of the function that is replacing the old
- function. The address is filled in during relocation. The value **MUST** be
- the address of the new function in the file.
-
-* `old_size` and `new_size` contain the sizes of the respective functions in bytes.
- The value of `old_size` **MUST** not be zero.
-
-* `version` is to be one.
-
-* `opaque` **MUST** be zero.
-
-The size of the `xsplice_patch_func` array is determined from the ELF section
-size.
-
-When applying the patch the hypervisor iterates over each `xsplice_patch_func`
-structure and the core code inserts a trampoline at `old_addr` to `new_addr`.
-The `new_addr` is altered when the ELF payload is loaded.
-
-When reverting a patch, the hypervisor iterates over each `xsplice_patch_func`
-and the core code copies the data from the undo buffer (private internal copy)
-to `old_addr`.
-
-### Example of .xsplice.funcs
-
-A simple example of what a payload file can be:
-
-<pre>
-/* MUST be in sync with hypervisor. */
-struct xsplice_patch_func {
- const char *name;
- void *new_addr;
- void *old_addr;
- uint32_t new_size;
- uint32_t old_size;
- uint8_t version;
- uint8_t pad[31];
-};
-
-/* Our replacement function for xen_extra_version. */
-const char *xen_hello_world(void)
-{
- return "Hello World";
-}
-
-static unsigned char patch_this_fnc[] = "xen_extra_version";
-
-struct xsplice_patch_func xsplice_hello_world = {
- .version = XSPLICE_PAYLOAD_VERSION,
- .name = patch_this_fnc,
- .new_addr = xen_hello_world,
- .old_addr = (void *)0xffff82d08013963c, /* Extracted from xen-syms. */
- .new_size = 13, /* To be be computed by scripts. */
- .old_size = 13, /* -----------""--------------- */
-} __attribute__((__section__(".xsplice.funcs")));
-
-</pre>
-
-Code must be compiled with -fPIC.
-
-### .xsplice.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 `.xsplice.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.
-
-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
-checksum, MD5 checksum or any unique value.
-
-The size of these structures varies with the --build-id linker option.
-
-## Hypercalls
-
-We will employ the sub operations of the system management hypercall (sysctl).
-There are to be four sub-operations:
-
- * upload the payloads.
- * listing of payloads summary uploaded and their state.
- * getting an particular payload summary and its state.
- * command to apply, delete, or revert the payload.
-
-Most of the actions are asynchronous therefore the caller is responsible
-to verify that it has been applied properly by retrieving the summary of it
-and verifying that there are no error codes associated with the payload.
-
-We **MUST** make some of them asynchronous due to the nature of patching
-it requires every physical CPU to be lock-step with each other.
-The patching mechanism while an implementation detail, is not an short
-operation and as such the design **MUST** assume it will be an long-running
-operation.
-
-The sub-operations will spell out how preemption is to be handled (if at all).
-
-Furthermore it is possible to have multiple different payloads for the same
-function. As such an unique name per payload has to be visible to allow proper manipulation.
-
-The hypercall is part of the `xen_sysctl`. The top level structure contains
-one uint32_t to determine the sub-operations and one padding field which
-*MUST* always be zero.
-
-<pre>
-struct xen_sysctl_xsplice_op {
- uint32_t cmd; /* IN: XEN_SYSCTL_XSPLICE_*. */
- uint32_t pad; /* IN: Always zero. */
- union {
- ... see below ...
- } u;
-};
-
-</pre>
-while the rest of hypercall specific structures are part of the this structure.
-
-### Basic type: struct xen_xsplice_name
-
-Most of the hypercalls employ an shared structure called `struct xen_xsplice_name`
-which contains:
-
- * `name` - pointer where the string for the name is located.
- * `size` - the size of the string
- * `pad` - padding - to be zero.
-
-The structure is as follow:
-
-<pre>
-/*
- * Uniquely identifies the payload. Should be human readable.
- * Includes the NUL terminator
- */
-#define XEN_XSPLICE_NAME_SIZE 128
-struct xen_xsplice_name {
- XEN_GUEST_HANDLE_64(char) name; /* IN, pointer to name. */
- uint16_t size; /* IN, size of name. May be upto
- XEN_XSPLICE_NAME_SIZE. */
- uint16_t pad[3]; /* IN: MUST be zero. */
-};
-</pre>
-
-### XEN_SYSCTL_XSPLICE_UPLOAD (0)
-
-Upload a payload to the hypervisor. The payload is verified
-against basic checks and if there are any issues the proper return code
-will be returned. The payload is not applied at this time - that is
-controlled by *XEN_SYSCTL_XSPLICE_ACTION*.
-
-The caller provides:
-
- * A `struct xen_xsplice_name` called `name` which has the unique name.
- * `size` the size of the ELF payload (in bytes).
- * `payload` the virtual address of where the ELF payload is.
-
-The `name` could be an UUID that stays fixed forever for a given
-payload. It can be embedded into the ELF payload at creation time
-and extracted by tools.
-
-The return value is zero if the payload was succesfully uploaded.
-Otherwise an -XEN_EXX return value is provided. Duplicate `name` are not supported.
-
-The `payload` is the ELF payload as mentioned in the `Payload format` section.
-
-The structure is as follow:
-
-<pre>
-struct xen_sysctl_xsplice_upload {
- xen_xsplice_name_t name; /* IN, name of the patch. */
- uint64_t size; /* IN, size of the ELF file. */
- XEN_GUEST_HANDLE_64(uint8) payload; /* IN: ELF file. */
-};
-</pre>
-
-### XEN_SYSCTL_XSPLICE_GET (1)
-
-Retrieve an status of an specific payload. This caller provides:
-
- * A `struct xen_xsplice_name` called `name` which has the unique name.
- * A `struct xen_xsplice_status` structure. The member values will
- be over-written upon completion.
-
-Upon completion the `struct xen_xsplice_status` is updated.
-
- * `status` - indicates the current status of the payload:
- * *XSPLICE_STATUS_CHECKED* (1) loaded and the ELF payload safety checks passed.
- * *XSPLICE_STATUS_APPLIED* (2) loaded, checked, and applied.
- * No other value is possible.
- * `rc` - -XEN_EXX type errors encountered while performing the last
- XSPLICE_ACTION_* operation. The normal values can be zero or -XEN_EAGAIN which
- respectively mean: success or operation in progress. Other values
- imply an error occurred. If there is an error in `rc`, `status` will **NOT**
- have changed.
-
-The return value of the hypercall is zero on success and -XEN_EXX on failure.
-(Note that the `rc`` value can be different from the return value, as in
-rc=-XEN_EAGAIN and return value can be 0).
-
-For example, supposing there is an payload:
-
-<pre>
- status: XSPLICE_STATUS_CHECKED
- rc: 0
-</pre>
-
-We apply an action - XSPLICE_ACTION_REVERT - to revert it (which won't work
-as we have not even applied it. Afterwards we will have:
-
-<pre>
- status: XSPLICE_STATUS_CHECKED
- rc: -XEN_EINVAL
-</pre>
-
-It has failed but it remains loaded.
-
-This operation is synchronous and does not require preemption.
-
-The structure is as follow:
-
-<pre>
-struct xen_xsplice_status {
-#define XSPLICE_STATUS_CHECKED 1
-#define XSPLICE_STATUS_APPLIED 2
- uint32_t state; /* OUT: XSPLICE_STATE_*. */
- int32_t rc; /* OUT: 0 if no error, otherwise -XEN_EXX. */
-};
-
-struct xen_sysctl_xsplice_get {
- xen_xsplice_name_t name; /* IN, the name of the payload. */
- xen_xsplice_status_t status; /* IN/OUT: status of the payload. */
-};
-</pre>
-
-### XEN_SYSCTL_XSPLICE_LIST (2)
-
-Retrieve an array of abbreviated status and names of payloads that are loaded in the
-hypervisor.
-
-The caller provides:
-
- * `version`. Version of the payload. Caller should re-use the field provided by
- the hypervisor. If the value differs the data is stale.
- * `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.
- * `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_xsplice_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_XSPLICE_NAME_SIZE** size. Note that **XEN_XSPLICE_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).
-
-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.
-
-If the hypercall returns -XEN_E2BIG the `nr` is too big and should be
-lowered.
-
-If the hypercall returns an zero value there are no more payloads.
-
-Note that due to the asynchronous nature of hypercalls the control domain might
-have added or removed a number of payloads making this information stale. It is
-the responsibility of the toolstack to use the `version` field to check
-between each invocation. if the version differs it should discard the stale
-data and start from scratch. It is OK for the toolstack to use the new
-`version` field.
-
-The `struct xen_xsplice_status` structure contains an status of payload which includes:
-
- * `status` - indicates the current status of the payload:
- * *XSPLICE_STATUS_CHECKED* (1) loaded and the ELF payload safety checks passed.
- * *XSPLICE_STATUS_APPLIED* (2) loaded, checked, and applied.
- * No other value is possible.
- * `rc` - -XEN_EXX type errors encountered while performing the last
- XSPLICE_ACTION_* operation. The normal values can be zero or -XEN_EAGAIN which
- respectively mean: success or operation in progress. Other values
- imply an error occurred. If there is an error in `rc`, `status` will **NOT**
- have changed.
-
-The structure is as follow:
-
-<pre>
-struct xen_sysctl_xsplice_list {
- uint32_t version; /* OUT: Hypervisor stamps value.
- If varies between calls, we are
- getting stale data. */
- uint32_t idx; /* IN: Index into hypervisor list. */
- uint32_t nr; /* IN: How many status, names, and len
- should be filled out. Can be zero to get
- amount of payloads and version.
- OUT: How many payloads left. */
- uint32_t pad; /* IN: Must be zero. */
- XEN_GUEST_HANDLE_64(xen_xsplice_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_XSPLICE_NAME_SIZE in size.
- Must have nr of them. */
- XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of name's.
- Must have nr of them. */
-};
-</pre>
-
-### XEN_SYSCTL_XSPLICE_ACTION (3)
-
-Perform an operation on the payload structure referenced by the `name` field.
-The operation request is asynchronous and the status should be retrieved
-by using either **XEN_SYSCTL_XSPLICE_GET** or **XEN_SYSCTL_XSPLICE_LIST** hypercall.
-
-The caller provides:
-
- * A 'struct xen_xsplice_name` `name` containing the unique name.
- * `cmd` the command requested:
- * *XSPLICE_ACTION_UNLOAD* (1) unload the payload.
- Any further hypercalls against the `name` will result in failure unless
- **XEN_SYSCTL_XSPLICE_UPLOAD** hypercall is perfomed with same `name`.
- * *XSPLICE_ACTION_REVERT* (2) revert the payload. If the operation takes
- more time than the upper bound of time the `rc` in `xen_xsplice_status'
- retrieved via **XEN_SYSCTL_XSPLICE_GET** will be -XEN_EBUSY.
- * *XSPLICE_ACTION_APPLY* (3) apply the payload. If the operation takes
- more time than the upper bound of time the `rc` in `xen_xsplice_status'
- retrieved via **XEN_SYSCTL_XSPLICE_GET** will be -XEN_EBUSY.
- * *XSPLICE_ACTION_REPLACE* (4) revert all applied payloads and apply this
- payload. If the operation takes more time than the upper bound of time
- the `rc` in `xen_xsplice_status' retrieved via **XEN_SYSCTL_XSPLICE_GET**
- will be -XEN_EBUSY.
- * `time` the upper bound of time (ms) the cmd should take. Zero means infinite.
- If within the time the operation does not succeed the operation would go in
- error state.
- * `pad` - *MUST* be zero.
-
-The return value will be zero unless the provided fields are incorrect.
-
-The structure is as follow:
-
-<pre>
-#define XSPLICE_ACTION_UNLOAD 1
-#define XSPLICE_ACTION_REVERT 2
-#define XSPLICE_ACTION_APPLY 3
-#define XSPLICE_ACTION_REPLACE 4
-struct xen_sysctl_xsplice_action {
- xen_xsplice_name_t name; /* IN, name of the patch. */
- uint32_t cmd; /* IN: XSPLICE_ACTION_* */
- uint32_t time; /* IN: Zero if no timeout. */
- /* Or upper bound of time (ms) */
- /* for operation to take. */
-};
-
-</pre>
-
-## State diagrams of XSPLICE_ACTION commands.
-
-There is a strict ordering state of what the commands can be.
-The XSPLICE_ACTION prefix has been dropped to easy reading and
-does not include the XSPLICE_STATES:
-
-<pre>
- /->\
- \ /
- UNLOAD <--- CHECK ---> REPLACE|APPLY --> REVERT --\
- \ |
- \-------------------<-------------/
-
-</pre>
-## State transition table of XSPLICE_ACTION commands and XSPLICE_STATUS.
-
-Note that:
-
- - The CHECKED state is the starting one achieved with *XEN_SYSCTL_XSPLICE_UPLOAD* hypercall.
- - The REVERT operation on success will automatically move to the CHECKED state.
- - There are two STATES: CHECKED and APPLIED.
- - There are four actions (aka commands): APPLY, REPLACE, REVERT, and UNLOAD.
-
-The state transition table of valid states and action states:
-
-<pre>
-
-+---------+---------+--------------------------------+-------+--------+
-| ACTION | Current | Result | Next STATE: |
-| ACTION | STATE | |CHECKED|APPLIED |
-+---------+----------+-------------------------------+-------+--------+
-| UNLOAD | CHECKED | Unload payload. Always works. | | |
-| | | No next states. | | |
-+---------+---------+--------------------------------+-------+--------+
-| APPLY | CHECKED | Apply payload (success). | | x |
-+---------+---------+--------------------------------+-------+--------+
-| APPLY | CHECKED | Apply payload (error|timeout) | x | |
-+---------+---------+--------------------------------+-------+--------+
-| REPLACE | CHECKED | Revert payloads and apply new | | x |
-| | | payload with success. | | |
-+---------+---------+--------------------------------+-------+--------+
-| REPLACE | CHECKED | Revert payloads and apply new | x | |
-| | | payload with error. | | |
-+---------+---------+--------------------------------+-------+--------+
-| REVERT | APPLIED | Revert payload (success). | x | |
-+---------+---------+--------------------------------+-------+--------+
-| REVERT | APPLIED | Revert payload (error|timeout) | | x |
-+---------+---------+--------------------------------+-------+--------+
-</pre>
-
-All the other state transitions are invalid.
-
-## Sequence of events.
-
-The normal sequence of events is to:
-
- 1. *XEN_SYSCTL_XSPLICE_UPLOAD* to upload the payload. If there are errors *STOP* here.
- 2. *XEN_SYSCTL_XSPLICE_GET* to check the `->rc`. If *-XEN_EAGAIN* spin. If zero go to next step.
- 3. *XEN_SYSCTL_XSPLICE_ACTION* with *XSPLICE_ACTION_APPLY* to apply the patch.
- 4. *XEN_SYSCTL_XSPLICE_GET* to check the `->rc`. If in *-XEN_EAGAIN* spin. If zero exit with success.
-
-
-## Addendum
-
-Implementation quirks should not be discussed in a design document.
-
-However these observations can provide aid when developing against this
-document.
-
-
-### Alternative assembler
-
-Alternative assembler is a mechanism to use different instructions depending
-on what the CPU supports. This is done by providing multiple streams of code
-that can be patched in - or if the CPU does not support it - padded with
-`nop` operations. The alternative assembler macros cause the compiler to
-expand the code to place a most generic code in place - emit a special
-ELF .section header to tag this location. During run-time the hypervisor
-can leave the areas alone or patch them with an better suited opcodes.
-
-Note that patching functions that copy to or from guest memory requires
-to support alternative support. For example this can be due to SMAP
-(specifically *stac* and *clac* operations) which is enabled on Broadwell
-and later architectures. It may be related to other alternative instructions.
-
-### When to patch
-
-During the discussion on the design two candidates bubbled where
-the call stack for each CPU would be deterministic. This would
-minimize the chance of the patch not being applied due to safety
-checks failing. Safety checks such as not patching code which
-is on the stack - which can lead to corruption.
-
-#### Rendezvous code instead of stop_machine for patching
-
-The hypervisor's time rendezvous code runs synchronously across all CPUs
-every second. Using the stop_machine to patch can stall the time rendezvous
-code and result in NMI. As such having the patching be done at the tail
-of rendezvous code should avoid this problem.
-
-However the entrance point for that code is
-do_softirq->timer_softirq_action->time_calibration
-which ends up calling on_selected_cpus on remote CPUs.
-
-The remote CPUs receive CALL_FUNCTION_VECTOR IPI and execute the
-desired function.
-
-#### Before entering the guest code.
-
-Before we call VMXResume we check whether any soft IRQs need to be executed.
-This is a good spot because all Xen stacks are effectively empty at
-that point.
-
-To randezvous all the CPUs an barrier with an maximum timeout (which
-could be adjusted), combined with forcing all other CPUs through the
-hypervisor with IPIs, can be utilized to execute lockstep instructions
-on all CPUs.
-
-The approach is similar in concept to stop_machine and the time rendezvous
-but is time-bound. However the local CPU stack is much shorter and
-a lot more deterministic.
-
-This is implemented in the Xen Project hypervisor.
-
-### Compiling the hypervisor code
-
-Hotpatch generation often requires support for compiling the target
-with -ffunction-sections / -fdata-sections. Changes would have to
-be done to the linker scripts to support this.
-
-### Generation of xSplice ELF payloads
-
-The design of that is not discussed in this design.
-
-This is implemented in a seperate tool which lives in a seperate
-GIT repo.
-
-Currently it resides at https://github.com/rosslagerwall/xsplice-build
-
-### Exception tables and symbol tables growth
-
-We may need support for adapting or augmenting exception tables if
-patching such code. Hotpatches may need to bring their own small
-exception tables (similar to how Linux modules support this).
-
-If supporting hotpatches that introduce additional exception-locations
-is not important, one could also change the exception table in-place
-and reorder it afterwards.
-
-As found almost every patch (XSA) to a non-trivial function requires
-additional entries in the exception table and/or the bug frames.
-
-This is implemented in the Xen Project hypervisor.
-
-### .rodata sections
-
-The patching might require strings to be updated as well. As such we must be
-also able to patch the strings as needed. This sounds simple - but the compiler
-has a habit of coalescing strings that are the same - which means if we in-place
-alter the strings - other users will be inadvertently affected as well.
-
-This is also where pointers to functions live - and we may need to patch this
-as well. And switch-style jump tables.
-
-To guard against that we must be prepared to do patching similar to
-trampoline patching or in-line depending on the flavour. If we can
-do in-line patching we would need to:
-
- * alter `.rodata` to be writeable.
- * inline patch.
- * alter `.rodata` to be read-only.
-
-If are doing trampoline patching we would need to:
-
- * allocate a new memory location for the string.
- * all locations which use this string will have to be updated to use the
- offset to the string.
- * mark the region RO when we are done.
-
-The trampoline patching is implemented in the Xen Project hypervisor.
-
-### .bss and .data sections.
-
-In place patching writable data is not suitable as it is unclear what should be done
-depending on the current state of data. As such it should not be attempted.
-
-However, functions which are being patched can bring in changes to strings
-(.data or .rodata section changes), or even to .bss sections.
-
-As such the ELF payload can introduce new .rodata, .bss, and .data sections.
-Patching in the new function will end up also patching in the new .rodata
-section and the new function will reference the new string in the new
-.rodata section.
-
-This is implemented in the Xen Project hypervisor.
-
-### Security
-
-Only the privileged domain should be allowed to do this operation.
-
-### xSplice interdependencies
-
-xSplice patches interdependencies are tricky.
-
-There are the ways this can be addressed:
- * A single large patch that subsumes and replaces all previous ones.
- Over the life-time of patching the hypervisor this large patch
- grows to accumulate all the code changes.
- * Hotpatch stack - where an mechanism exists that loads the hotpatches
- in the same order they were built in. We would need an build-id
- of the hypevisor to make sure the hot-patches are build against the
- correct build.
- * Payload containing the old code to check against that. That allows
- the hotpatches to be loaded indepedently (if they don't overlap) - or
- if the old code also containst previously patched code - even if they
- overlap.
-
-The disadvantage of the first large patch is that it can grow over
-time and not provide an bisection mechanism to identify faulty patches.
-
-The hot-patch stack puts stricts requirements on the order of the patches
-being loaded and requires an hypervisor build-id to match against.
-
-The old code allows much more flexibility and an additional guard,
-but is more complex to implement.
-
-The second option which requires an build-id of the hypervisor
-is implemented in the Xen Project hypervisor.
-
-Specifically each payload has two build-id ELF notes:
- * The build-id of the payload itself (generated via --build-id).
- * 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.
-
-# Not Yet Done
-
-This is for further development of xSplice.
-
-## TODO Goals
-
-The implementation must also have a mechanism for (in no particular order):
-
- * Be able to lookup in the Xen hypervisor the symbol names of functions from the
- ELF payload. (Either as `symbol` or `symbol`+`offset`).
- * Be able to patch .rodata, .bss, and .data sections.
- * Deal with NMI/MCE checks during patching instead of ignoring them.
- * Further safety checks (blacklist of which functions cannot be patched, check
- the stack, make sure the payload is built with same compiler as hypervisor).
- Specifically we want to make sure that xSplice codepaths cannot be patched.
- * NOP out the code sequence if `new_size` is zero.
- * Deal with other relocation types: R_X86_64_[8,16,32,32S], R_X86_64_PC[8,16,64]
- in payload file.
-
-### Handle inlined __LINE__
-
-This problem is related to hotpatch construction
-and potentially has influence on the design of the hotpatching
-infrastructure in Xen.
-
-For example:
-
-We have file1.c with functions f1 and f2 (in that order). f2 contains a
-BUG() (or WARN()) macro and at that point embeds the source line number
-into the generated code for f2.
-
-Now we want to hotpatch f1 and the hotpatch source-code patch adds 2
-lines to f1 and as a consequence shifts out f2 by two lines. The newly
-constructed file1.o will now contain differences in both binary
-functions f1 (because we actually changed it with the applied patch) and
-f2 (because the contained BUG macro embeds the new line number).
-
-Without additional information, an algorithm comparing file1.o before
-and after hotpatch application will determine both functions to be
-changed and will have to include both into the binary hotpatch.
-
-Options:
-
-1. Transform source code patches for hotpatches to be line-neutral for
- each chunk. This can be done in almost all cases with either
- reformatting of the source code or by introducing artificial
- preprocessor "#line n" directives to adjust for the introduced
- differences.
-
- This approach is low-tech and simple. Potentially generated
- backtraces and existing debug information refers to the original
- build and does not reflect hotpatching state except for actually
- hotpatched functions but should be mostly correct.
-
-2. Ignoring the problem and living with artificially large hotpatches
- that unnecessarily patch many functions.
-
- This approach might lead to some very large hotpatches depending on
- content of specific source file. It may also trigger pulling in
- functions into the hotpatch that cannot reasonable be hotpatched due
- to limitations of a hotpatching framework (init-sections, parts of
- the hotpatching framework itself, ...) and may thereby prevent us
- from patching a specific problem.
-
- The decision between 1. and 2. can be made on a patch--by-patch
- basis.
-
-3. Introducing an indirection table for storing line numbers and
- treating that specially for binary diffing. Linux may follow
- this approach.
-
- We might either use this indirection table for runtime use and patch
- that with each hotpatch (similarly to exception tables) or we might
- purely use it when building hotpatches to ignore functions that only
- differ at exactly the location where a line-number is embedded.
-
-For BUG(), WARN(), etc., the line number is embedded into the bug frame, not
-the function itself.
-
-Similar considerations are true to a lesser extent for __FILE__, but it
-could be argued that file renaming should be done outside of hotpatches.
-
-## Signature checking requirements.
-
-The signature checking requires that the layout of the data in memory
-**MUST** be same for signature to be verified. This means that the payload
-data layout in ELF format **MUST** match what the hypervisor would be
-expecting such that it can properly do signature verification.
-
-The signature is based on the all of the payloads continuously laid out
-in memory. The signature is to be appended at the end of the ELF payload
-prefixed with the string `'~Module signature appended~\n'`, followed by
-an signature header then followed by the signature, key identifier, and signers
-name.
-
-Specifically the signature header would be:
-
-<pre>
-#define PKEY_ALGO_DSA 0
-#define PKEY_ALGO_RSA 1
-
-#define PKEY_ID_PGP 0 /* OpenPGP generated key ID */
-#define PKEY_ID_X509 1 /* X.509 arbitrary subjectKeyIdentifier */
-
-#define HASH_ALGO_MD4 0
-#define HASH_ALGO_MD5 1
-#define HASH_ALGO_SHA1 2
-#define HASH_ALGO_RIPE_MD_160 3
-#define HASH_ALGO_SHA256 4
-#define HASH_ALGO_SHA384 5
-#define HASH_ALGO_SHA512 6
-#define HASH_ALGO_SHA224 7
-#define HASH_ALGO_RIPE_MD_128 8
-#define HASH_ALGO_RIPE_MD_256 9
-#define HASH_ALGO_RIPE_MD_320 10
-#define HASH_ALGO_WP_256 11
-#define HASH_ALGO_WP_384 12
-#define HASH_ALGO_WP_512 13
-#define HASH_ALGO_TGR_128 14
-#define HASH_ALGO_TGR_160 15
-#define HASH_ALGO_TGR_192 16
-
-
-struct elf_payload_signature {
- u8 algo; /* Public-key crypto algorithm PKEY_ALGO_*. */
- u8 hash; /* Digest algorithm: HASH_ALGO_*. */
- u8 id_type; /* Key identifier type PKEY_ID*. */
- u8 signer_len; /* Length of signer's name */
- u8 key_id_len; /* Length of key identifier */
- u8 __pad[3];
- __be32 sig_len; /* Length of signature data */
-};
-
-</pre>
-(Note that this has been borrowed from Linux module signature code.).
-
-
-### .bss and .data sections.
-
-In place patching writable data is not suitable as it is unclear what should be done
-depending on the current state of data. As such it should not be attempted.
-
-That said we should provide hook functions so that the existing data
-can be changed during payload application.
-
-
-### Inline patching
-
-The hypervisor should verify that the in-place patching would fit within
-the code or data.
-
-### Trampoline (e9 opcode)
-
-The e9 opcode used for jmpq uses a 32-bit signed displacement. That means
-we are limited to up to 2GB of virtual address to place the new code
-from the old code. That should not be a problem since Xen hypervisor has
-a very small footprint.
-
-However if we need - we can always add two trampolines. One at the 2GB
-limit that calls the next trampoline.
-
-Please note there is a small limitation for trampolines in
-function entries: The target function (+ trailing padding) must be able
-to accomodate the trampoline. On x86 with +-2 GB relative jumps,
-this means 5 bytes are required.
-
-Depending on compiler settings, there are several functions in Xen that
-are smaller (without inter-function padding).
-
-<pre>
-readelf -sW xen-syms | grep " FUNC " | \
- awk '{ if ($3 < 5) print $3, $4, $5, $8 }'
-
-...
-3 FUNC LOCAL wbinvd_ipi
-3 FUNC LOCAL shadow_l1_index
-...
-</pre>
-A compile-time check for, e.g., a minimum alignment of functions or a
-runtime check that verifies symbol size (+ padding to next symbols) for
-that in the hypervisor is advised.
-
-The tool for generating payloads currently does perform a compile-time
-check to ensure that the function to be replaced is large enough.
-
get_symbol
get_cpu_levelling_caps
get_cpu_featureset
- xsplice_op
+ livepatch_op
};
# Allow dom0 to use all XENVER_ subops that have checks.
#endif
-int xc_xsplice_upload(xc_interface *xch,
- char *name, unsigned char *payload, uint32_t size);
+int xc_livepatch_upload(xc_interface *xch,
+ char *name, unsigned char *payload, uint32_t size);
-int xc_xsplice_get(xc_interface *xch,
- char *name,
- xen_xsplice_status_t *status);
+int xc_livepatch_get(xc_interface *xch,
+ char *name,
+ xen_livepatch_status_t *status);
/*
- * The heart of this function is to get an array of xen_xsplice_status_t.
+ * The heart of this function is to get an array of xen_livepatch_status_t.
*
* However it is complex because it has to deal with the hypervisor
* returning some of the requested data or data being stale
* 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_XSPLICE_NAME_SIZE
+ * Each entry in the 'name' array is expected to be of XEN_LIVEPATCH_NAME_SIZE
* length.
*
- * Each entry in the 'info' array is expected to be of xen_xsplice_status_t
+ * Each entry in the 'info' array is expected to be of xen_livepatch_status_t
* structure size.
*
* Each entry in the 'len' array is expected to be of uint32_t size.
* will contain the number of entries that had been succesfully
* retrieved (if any).
*/
-int xc_xsplice_list(xc_interface *xch, unsigned int max, unsigned int start,
- xen_xsplice_status_t *info, char *name,
- uint32_t *len, unsigned int *done,
- unsigned int *left);
+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);
/*
* The operations are asynchronous and the hypervisor may take a while
* operation if it could not be completed within the specified time
* (in ms). Value of 0 means let hypervisor decide the best timeout.
*/
-int xc_xsplice_apply(xc_interface *xch, char *name, uint32_t timeout);
-int xc_xsplice_revert(xc_interface *xch, char *name, uint32_t timeout);
-int xc_xsplice_unload(xc_interface *xch, char *name, uint32_t timeout);
-int xc_xsplice_replace(xc_interface *xch, char *name, uint32_t timeout);
+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);
/* Compat shims */
#include "xenctrl_compat.h"
return rc;
}
-int xc_xsplice_upload(xc_interface *xch,
- char *name,
- unsigned char *payload,
- uint32_t size)
+int xc_livepatch_upload(xc_interface *xch,
+ char *name,
+ unsigned char *payload,
+ uint32_t size)
{
int rc;
DECLARE_SYSCTL;
DECLARE_HYPERCALL_BUFFER(char, local);
DECLARE_HYPERCALL_BOUNCE(name, 0 /* later */, XC_HYPERCALL_BUFFER_BOUNCE_IN);
- xen_xsplice_name_t def_name = { .pad = { 0, 0, 0 } };
+ xen_livepatch_name_t def_name = { .pad = { 0, 0, 0 } };
if ( !name || !payload )
{
}
def_name.size = strlen(name) + 1;
- if ( def_name.size > XEN_XSPLICE_NAME_SIZE )
+ if ( def_name.size > XEN_LIVEPATCH_NAME_SIZE )
{
errno = EINVAL;
return -1;
}
memcpy(local, payload, size);
- sysctl.cmd = XEN_SYSCTL_xsplice_op;
- sysctl.u.xsplice.cmd = XEN_SYSCTL_XSPLICE_UPLOAD;
- sysctl.u.xsplice.pad = 0;
- sysctl.u.xsplice.u.upload.size = size;
- set_xen_guest_handle(sysctl.u.xsplice.u.upload.payload, local);
+ sysctl.cmd = XEN_SYSCTL_livepatch_op;
+ sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_UPLOAD;
+ sysctl.u.livepatch.pad = 0;
+ sysctl.u.livepatch.u.upload.size = size;
+ set_xen_guest_handle(sysctl.u.livepatch.u.upload.payload, local);
- sysctl.u.xsplice.u.upload.name = def_name;
- set_xen_guest_handle(sysctl.u.xsplice.u.upload.name.name, name);
+ sysctl.u.livepatch.u.upload.name = def_name;
+ set_xen_guest_handle(sysctl.u.livepatch.u.upload.name.name, name);
rc = do_sysctl(xch, &sysctl);
return rc;
}
-int xc_xsplice_get(xc_interface *xch,
- char *name,
- xen_xsplice_status_t *status)
+int xc_livepatch_get(xc_interface *xch,
+ char *name,
+ xen_livepatch_status_t *status)
{
int rc;
DECLARE_SYSCTL;
DECLARE_HYPERCALL_BOUNCE(name, 0 /*adjust later */, XC_HYPERCALL_BUFFER_BOUNCE_IN);
- xen_xsplice_name_t def_name = { .pad = { 0, 0, 0 } };
+ xen_livepatch_name_t def_name = { .pad = { 0, 0, 0 } };
if ( !name )
{
}
def_name.size = strlen(name) + 1;
- if ( def_name.size > XEN_XSPLICE_NAME_SIZE )
+ if ( def_name.size > XEN_LIVEPATCH_NAME_SIZE )
{
errno = EINVAL;
return -1;
if ( xc_hypercall_bounce_pre(xch, name) )
return -1;
- sysctl.cmd = XEN_SYSCTL_xsplice_op;
- sysctl.u.xsplice.cmd = XEN_SYSCTL_XSPLICE_GET;
- sysctl.u.xsplice.pad = 0;
+ sysctl.cmd = XEN_SYSCTL_livepatch_op;
+ sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_GET;
+ sysctl.u.livepatch.pad = 0;
- sysctl.u.xsplice.u.get.status.state = 0;
- sysctl.u.xsplice.u.get.status.rc = 0;
+ sysctl.u.livepatch.u.get.status.state = 0;
+ sysctl.u.livepatch.u.get.status.rc = 0;
- sysctl.u.xsplice.u.get.name = def_name;
- set_xen_guest_handle(sysctl.u.xsplice.u.get.name.name, name);
+ sysctl.u.livepatch.u.get.name = def_name;
+ set_xen_guest_handle(sysctl.u.livepatch.u.get.name.name, name);
rc = do_sysctl(xch, &sysctl);
xc_hypercall_bounce_post(xch, name);
- memcpy(status, &sysctl.u.xsplice.u.get.status, sizeof(*status));
+ memcpy(status, &sysctl.u.livepatch.u.get.status, sizeof(*status));
return rc;
}
/*
- * The heart of this function is to get an array of xen_xsplice_status_t.
+ * The heart of this function is to get an array of xen_livepatch_status_t.
*
* However it is complex because it has to deal with the hypervisor
* returning some of the requested data or data being stale
* 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_XSPLICE_NAME_SIZE
+ * Each entry in the 'name' array is expected to be of XEN_LIVEPATCH_NAME_SIZE
* length.
*
- * Each entry in the 'info' array is expected to be of xen_xsplice_status_t
+ * Each entry in the 'info' array is expected to be of xen_livepatch_status_t
* structure size.
*
* Each entry in the 'len' array is expected to be of uint32_t size.
* will contain the number of entries that had been succesfully
* retrieved (if any).
*/
-int xc_xsplice_list(xc_interface *xch, unsigned int max, unsigned int start,
- xen_xsplice_status_t *info,
- char *name, uint32_t *len,
- unsigned int *done,
- unsigned int *left)
+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 rc;
DECLARE_SYSCTL;
return -1;
}
- sysctl.cmd = XEN_SYSCTL_xsplice_op;
- sysctl.u.xsplice.cmd = XEN_SYSCTL_XSPLICE_LIST;
- sysctl.u.xsplice.pad = 0;
- sysctl.u.xsplice.u.list.version = 0;
- sysctl.u.xsplice.u.list.idx = start;
- sysctl.u.xsplice.u.list.pad = 0;
+ 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_XSPLICE_NAME_SIZE;
+ sz = sizeof(*name) * XEN_LIVEPATCH_NAME_SIZE;
*done = 0;
*left = 0;
do {
nr = min(max - *done, max_batch_sz);
- sysctl.u.xsplice.u.list.nr = nr;
+ 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);
if ( rc )
break;
- set_xen_guest_handle(sysctl.u.xsplice.u.list.status, info);
- set_xen_guest_handle(sysctl.u.xsplice.u.list.name, name);
- set_xen_guest_handle(sysctl.u.xsplice.u.list.len, len);
+ 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);
rc = do_sysctl(xch, &sysctl);
/*
break;
if ( !version )
- version = sysctl.u.xsplice.u.list.version;
+ version = sysctl.u.livepatch.u.list.version;
- if ( sysctl.u.xsplice.u.list.version != version )
+ if ( sysctl.u.livepatch.u.list.version != version )
{
/* We could make this configurable as parameter? */
if ( retries++ > 3 )
break;
}
*done = 0; /* Retry from scratch. */
- version = sysctl.u.xsplice.u.list.version;
+ version = sysctl.u.livepatch.u.list.version;
adjust = 1; /* And make sure we continue in the loop. */
/* No memory leaks. */
xc_hypercall_bounce_post(xch, info);
rc = -1;
break;
}
- *left = sysctl.u.xsplice.u.list.nr; /* Total remaining count. */
+ *left = sysctl.u.livepatch.u.list.nr; /* Total remaining count. */
/* 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));
/* And update how many elements of info we have copied into. */
*done += rc;
/* Update idx. */
- sysctl.u.xsplice.u.list.idx = *done;
+ sysctl.u.livepatch.u.list.idx = *done;
} while ( adjust || (*done < max && *left != 0) );
if ( rc < 0 )
return rc > 0 ? 0 : rc;
}
-static int _xc_xsplice_action(xc_interface *xch,
- char *name,
- unsigned int action,
- uint32_t timeout)
+static int _xc_livepatch_action(xc_interface *xch,
+ char *name,
+ unsigned int action,
+ uint32_t timeout)
{
int rc;
DECLARE_SYSCTL;
/* The size is figured out when we strlen(name) */
DECLARE_HYPERCALL_BOUNCE(name, 0, XC_HYPERCALL_BUFFER_BOUNCE_IN);
- xen_xsplice_name_t def_name = { .pad = { 0, 0, 0 } };
+ xen_livepatch_name_t def_name = { .pad = { 0, 0, 0 } };
def_name.size = strlen(name) + 1;
- if ( def_name.size > XEN_XSPLICE_NAME_SIZE )
+ if ( def_name.size > XEN_LIVEPATCH_NAME_SIZE )
{
errno = EINVAL;
return -1;
if ( xc_hypercall_bounce_pre(xch, name) )
return -1;
- sysctl.cmd = XEN_SYSCTL_xsplice_op;
- sysctl.u.xsplice.cmd = XEN_SYSCTL_XSPLICE_ACTION;
- sysctl.u.xsplice.pad = 0;
- sysctl.u.xsplice.u.action.cmd = action;
- sysctl.u.xsplice.u.action.timeout = timeout;
+ sysctl.cmd = XEN_SYSCTL_livepatch_op;
+ sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_ACTION;
+ sysctl.u.livepatch.pad = 0;
+ sysctl.u.livepatch.u.action.cmd = action;
+ sysctl.u.livepatch.u.action.timeout = timeout;
- sysctl.u.xsplice.u.action.name = def_name;
- set_xen_guest_handle(sysctl.u.xsplice.u.action.name.name, name);
+ sysctl.u.livepatch.u.action.name = def_name;
+ set_xen_guest_handle(sysctl.u.livepatch.u.action.name.name, name);
rc = do_sysctl(xch, &sysctl);
return rc;
}
-int xc_xsplice_apply(xc_interface *xch, char *name, uint32_t timeout)
+int xc_livepatch_apply(xc_interface *xch, char *name, uint32_t timeout)
{
- return _xc_xsplice_action(xch, name, XSPLICE_ACTION_APPLY, timeout);
+ return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_APPLY, timeout);
}
-int xc_xsplice_revert(xc_interface *xch, char *name, uint32_t timeout)
+int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout)
{
- return _xc_xsplice_action(xch, name, XSPLICE_ACTION_REVERT, timeout);
+ return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REVERT, timeout);
}
-int xc_xsplice_unload(xc_interface *xch, char *name, uint32_t timeout)
+int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout)
{
- return _xc_xsplice_action(xch, name, XSPLICE_ACTION_UNLOAD, timeout);
+ return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_UNLOAD, timeout);
}
-int xc_xsplice_replace(xc_interface *xch, char *name, uint32_t timeout)
+int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout)
{
- return _xc_xsplice_action(xch, name, XSPLICE_ACTION_REPLACE, timeout);
+ return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REPLACE, timeout);
}
/*
INSTALL_SBIN += xenperf
INSTALL_SBIN += xenpm
INSTALL_SBIN += xenwatchdogd
-INSTALL_SBIN += xen-xsplice
+INSTALL_SBIN += xen-livepatch
INSTALL_SBIN += $(INSTALL_SBIN-y)
# Everything to be installed in a private bin/
xenwatchdogd: xenwatchdogd.o
$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
-xen-xsplice: xen-xsplice.o
+xen-livepatch: xen-livepatch.o
$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
xen-lowmemd: xen-lowmemd.o
--- /dev/null
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <xenctrl.h>
+#include <xenstore.h>
+
+#include <xen/errno.h>
+
+static xc_interface *xch;
+
+void show_help(void)
+{
+ fprintf(stderr,
+ "xen-livepatch: live patching test tool\n"
+ "Usage: xen-livepatch <command> [args]\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"
+ " 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",
+ XEN_LIVEPATCH_NAME_SIZE);
+}
+
+/* wrapper function */
+static int help_func(int argc, char *argv[])
+{
+ show_help();
+ return 0;
+}
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+static const char *state2str(unsigned int state)
+{
+#define STATE(x) [LIVEPATCH_STATE_##x] = #x
+ static const char *const names[] = {
+ STATE(CHECKED),
+ STATE(APPLIED),
+ };
+#undef STATE
+ if (state >= ARRAY_SIZE(names) || !names[state])
+ return "unknown";
+
+ 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;
+ xen_livepatch_status_t *info = NULL;
+ char *name = NULL;
+ uint32_t *len = NULL;
+ int rc = ENOMEM;
+
+ if ( argc )
+ {
+ show_help();
+ return -1;
+ }
+ idx = left = 0;
+ info = malloc(sizeof(*info) * MAX_LEN);
+ if ( !info )
+ return rc;
+ name = malloc(sizeof(*name) * XEN_LIVEPATCH_NAME_SIZE * MAX_LEN);
+ if ( !name )
+ {
+ free(info);
+ return rc;
+ }
+ len = malloc(sizeof(*len) * MAX_LEN);
+ if ( !len ) {
+ free(name);
+ free(info);
+ return rc;
+ }
+
+ fprintf(stdout," ID | status\n"
+ "----------------------------------------+------------\n");
+ 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 )
+ {
+ fprintf(stderr, "Failed to list %d/%d: %d(%s)!\n",
+ idx, left, errno, strerror(errno));
+ break;
+ }
+ for ( i = 0; i < done; i++ )
+ {
+ unsigned int j;
+ uint32_t sz;
+ char *str;
+
+ sz = len[i];
+ str = name + (i * XEN_LIVEPATCH_NAME_SIZE);
+ for ( j = sz; j < XEN_LIVEPATCH_NAME_SIZE; j++ )
+ str[j] = '\0';
+
+ 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 );
+
+ free(name);
+ free(info);
+ free(len);
+ return rc;
+}
+#undef MAX_LEN
+
+static int get_name(int argc, char *argv[], char *name)
+{
+ ssize_t len = strlen(argv[0]);
+ if ( len > XEN_LIVEPATCH_NAME_SIZE )
+ {
+ fprintf(stderr, "ID MUST be %d characters!\n", XEN_LIVEPATCH_NAME_SIZE);
+ errno = EINVAL;
+ return errno;
+ }
+ /* Don't want any funny strings from the stack. */
+ memset(name, 0, XEN_LIVEPATCH_NAME_SIZE);
+ strncpy(name, argv[0], len);
+ return 0;
+}
+
+static int upload_func(int argc, char *argv[])
+{
+ char *filename;
+ char name[XEN_LIVEPATCH_NAME_SIZE];
+ int fd = 0, rc;
+ struct stat buf;
+ unsigned char *fbuf;
+ ssize_t len;
+
+ if ( argc != 2 )
+ {
+ show_help();
+ return -1;
+ }
+
+ if ( get_name(argc, argv, name) )
+ return EINVAL;
+
+ filename = argv[1];
+ fd = open(filename, O_RDONLY);
+ if ( fd < 0 )
+ {
+ fprintf(stderr, "Could not open %s, error: %d(%s)\n",
+ filename, errno, strerror(errno));
+ return errno;
+ }
+ if ( stat(filename, &buf) != 0 )
+ {
+ fprintf(stderr, "Could not get right size %s, error: %d(%s)\n",
+ filename, errno, strerror(errno));
+ close(fd);
+ return errno;
+ }
+
+ len = buf.st_size;
+ fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( fbuf == MAP_FAILED )
+ {
+ fprintf(stderr,"Could not map: %s, error: %d(%s)\n",
+ filename, errno, strerror(errno));
+ close (fd);
+ return errno;
+ }
+ printf("Uploading %s (%zu bytes)\n", filename, len);
+ rc = xc_livepatch_upload(xch, name, fbuf, len);
+ if ( rc )
+ fprintf(stderr, "Upload failed: %s, error: %d(%s)!\n",
+ filename, errno, strerror(errno));
+
+ if ( munmap( fbuf, len) )
+ {
+ fprintf(stderr, "Could not unmap!? error: %d(%s)!\n",
+ errno, strerror(errno));
+ if ( !rc )
+ rc = errno;
+ }
+ close(fd);
+
+ return rc;
+}
+
+/* These MUST match to the 'action_options[]' array slots. */
+enum {
+ ACTION_APPLY = 0,
+ ACTION_REVERT = 1,
+ ACTION_UNLOAD = 2,
+ ACTION_REPLACE = 3,
+};
+
+struct {
+ int allow; /* State it must be in to call function. */
+ int expected; /* The state to be in after the function. */
+ const char *name;
+ int (*function)(xc_interface *xch, char *name, uint32_t timeout);
+ unsigned int executed; /* Has the function been called?. */
+} action_options[] = {
+ { .allow = LIVEPATCH_STATE_CHECKED,
+ .expected = LIVEPATCH_STATE_APPLIED,
+ .name = "apply",
+ .function = xc_livepatch_apply,
+ },
+ { .allow = LIVEPATCH_STATE_APPLIED,
+ .expected = LIVEPATCH_STATE_CHECKED,
+ .name = "revert",
+ .function = xc_livepatch_revert,
+ },
+ { .allow = LIVEPATCH_STATE_CHECKED,
+ .expected = -XEN_ENOENT,
+ .name = "unload",
+ .function = xc_livepatch_unload,
+ },
+ { .allow = LIVEPATCH_STATE_CHECKED,
+ .expected = LIVEPATCH_STATE_APPLIED,
+ .name = "replace",
+ .function = xc_livepatch_replace,
+ },
+};
+
+/* Go around 300 * 0.1 seconds = 30 seconds. */
+#define RETRIES 300
+/* aka 0.1 second */
+#define DELAY 100000
+
+int action_func(int argc, char *argv[], unsigned int idx)
+{
+ char name[XEN_LIVEPATCH_NAME_SIZE];
+ int rc, original_state;
+ xen_livepatch_status_t status;
+ unsigned int retry = 0;
+
+ if ( argc != 1 )
+ {
+ show_help();
+ return -1;
+ }
+
+ if ( idx >= ARRAY_SIZE(action_options) )
+ return -1;
+
+ if ( get_name(argc, argv, name) )
+ return EINVAL;
+
+ /* Check initial status. */
+ rc = xc_livepatch_get(xch, name, &status);
+ if ( rc )
+ {
+ fprintf(stderr, "%s failed to get status %d(%s)!\n",
+ name, errno, strerror(errno));
+ return -1;
+ }
+ if ( status.rc == -XEN_EAGAIN )
+ {
+ fprintf(stderr, "%s failed. Operation already in progress\n", name);
+ return -1;
+ }
+
+ if ( status.state == action_options[idx].expected )
+ {
+ printf("No action needed\n");
+ return 0;
+ }
+
+ /* Perform action. */
+ if ( action_options[idx].allow & status.state )
+ {
+ printf("Performing %s:", action_options[idx].name);
+ rc = action_options[idx].function(xch, name, 0);
+ if ( rc )
+ {
+ fprintf(stderr, "%s failed with %d(%s)\n", name, errno,
+ strerror(errno));
+ return -1;
+ }
+ }
+ else
+ {
+ printf("%s: in wrong state (%s), expected (%s)\n",
+ name, state2str(status.state),
+ state2str(action_options[idx].expected));
+ return -1;
+ }
+
+ original_state = status.state;
+ do {
+ rc = xc_livepatch_get(xch, name, &status);
+ if ( rc )
+ {
+ rc = -errno;
+ break;
+ }
+
+ if ( status.state != original_state )
+ break;
+ if ( status.rc && status.rc != -XEN_EAGAIN )
+ {
+ rc = status.rc;
+ break;
+ }
+
+ printf(".");
+ fflush(stdout);
+ usleep(DELAY);
+ } while ( ++retry < RETRIES );
+
+ if ( retry >= RETRIES )
+ {
+ fprintf(stderr, "%s: Operation didn't complete after 30 seconds.\n", name);
+ return -1;
+ }
+ else
+ {
+ if ( rc == 0 )
+ rc = status.state;
+
+ if ( action_options[idx].expected == rc )
+ printf(" completed\n");
+ else if ( rc < 0 )
+ {
+ fprintf(stderr, "%s failed with %d(%s)\n", name, -rc, strerror(-rc));
+ return -1;
+ }
+ else
+ {
+ fprintf(stderr, "%s: in wrong state (%s), expected (%s)\n",
+ name, state2str(rc),
+ state2str(action_options[idx].expected));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int load_func(int argc, char *argv[])
+{
+ int rc;
+ char *new_argv[2];
+ char *path, *name, *lastdot;
+
+ if ( argc != 1 )
+ {
+ show_help();
+ return -1;
+ }
+ /* <file> */
+ new_argv[1] = argv[0];
+
+ /* Synthesize the <id> */
+ path = strdup(argv[0]);
+
+ name = basename(path);
+ lastdot = strrchr(name, '.');
+ if ( lastdot != NULL )
+ *lastdot = '\0';
+ new_argv[0] = name;
+
+ rc = upload_func(2 /* <id> <file> */, new_argv);
+ if ( rc )
+ return rc;
+
+ rc = action_func(1 /* only <id> */, new_argv, ACTION_APPLY);
+ if ( rc )
+ action_func(1, new_argv, ACTION_UNLOAD);
+
+ free(path);
+ return rc;
+}
+
+/*
+ * These are also functions in action_options that are called in case
+ * none of the ones in main_options match.
+ */
+struct {
+ const char *name;
+ int (*function)(int argc, char *argv[]);
+} main_options[] = {
+ { "help", help_func },
+ { "list", list_func },
+ { "upload", upload_func },
+ { "load", load_func },
+};
+
+int main(int argc, char *argv[])
+{
+ int i, j, ret;
+
+ if ( argc <= 1 )
+ {
+ show_help();
+ return 0;
+ }
+ for ( i = 0; i < ARRAY_SIZE(main_options); i++ )
+ if (!strncmp(main_options[i].name, argv[1], strlen(argv[1])))
+ break;
+
+ if ( i == ARRAY_SIZE(main_options) )
+ {
+ for ( j = 0; j < ARRAY_SIZE(action_options); j++ )
+ if (!strncmp(action_options[j].name, argv[1], strlen(argv[1])))
+ break;
+
+ if ( j == ARRAY_SIZE(action_options) )
+ {
+ fprintf(stderr, "Unrecognised command '%s' -- try "
+ "'xen-livepatch help'\n", argv[1]);
+ return 1;
+ }
+ } else
+ j = ARRAY_SIZE(action_options);
+
+ xch = xc_interface_open(0,0,0);
+ if ( !xch )
+ {
+ fprintf(stderr, "failed to get the handler\n");
+ return 0;
+ }
+
+ if ( i == ARRAY_SIZE(main_options) )
+ ret = action_func(argc -2, argv + 2, j);
+ else
+ ret = main_options[i].function(argc -2, argv + 2);
+
+ xc_interface_close(xch);
+
+ return !!ret;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+++ /dev/null
-/*
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
- */
-
-#include <fcntl.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <xenctrl.h>
-#include <xenstore.h>
-
-#include <xen/errno.h>
-
-static xc_interface *xch;
-
-void show_help(void)
-{
- fprintf(stderr,
- "xen-xsplice: Xsplice test tool\n"
- "Usage: xen-xsplice <command> [args]\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"
- " 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",
- XEN_XSPLICE_NAME_SIZE);
-}
-
-/* wrapper function */
-static int help_func(int argc, char *argv[])
-{
- show_help();
- return 0;
-}
-
-#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
-
-static const char *state2str(unsigned int state)
-{
-#define STATE(x) [XSPLICE_STATE_##x] = #x
- static const char *const names[] = {
- STATE(CHECKED),
- STATE(APPLIED),
- };
-#undef STATE
- if (state >= ARRAY_SIZE(names) || !names[state])
- return "unknown";
-
- 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;
- xen_xsplice_status_t *info = NULL;
- char *name = NULL;
- uint32_t *len = NULL;
- int rc = ENOMEM;
-
- if ( argc )
- {
- show_help();
- return -1;
- }
- idx = left = 0;
- info = malloc(sizeof(*info) * MAX_LEN);
- if ( !info )
- return rc;
- name = malloc(sizeof(*name) * XEN_XSPLICE_NAME_SIZE * MAX_LEN);
- if ( !name )
- {
- free(info);
- return rc;
- }
- len = malloc(sizeof(*len) * MAX_LEN);
- if ( !len ) {
- free(name);
- free(info);
- return rc;
- }
-
- fprintf(stdout," ID | status\n"
- "----------------------------------------+------------\n");
- 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_XSPLICE_NAME_SIZE);
- memset(len, 'C', sizeof(*len) * MAX_LEN);
- rc = xc_xsplice_list(xch, MAX_LEN, idx, info, name, len, &done, &left);
- if ( rc )
- {
- fprintf(stderr, "Failed to list %d/%d: %d(%s)!\n",
- idx, left, errno, strerror(errno));
- break;
- }
- for ( i = 0; i < done; i++ )
- {
- unsigned int j;
- uint32_t sz;
- char *str;
-
- sz = len[i];
- str = name + (i * XEN_XSPLICE_NAME_SIZE);
- for ( j = sz; j < XEN_XSPLICE_NAME_SIZE; j++ )
- str[j] = '\0';
-
- 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 );
-
- free(name);
- free(info);
- free(len);
- return rc;
-}
-#undef MAX_LEN
-
-static int get_name(int argc, char *argv[], char *name)
-{
- ssize_t len = strlen(argv[0]);
- if ( len > XEN_XSPLICE_NAME_SIZE )
- {
- fprintf(stderr, "ID MUST be %d characters!\n", XEN_XSPLICE_NAME_SIZE);
- errno = EINVAL;
- return errno;
- }
- /* Don't want any funny strings from the stack. */
- memset(name, 0, XEN_XSPLICE_NAME_SIZE);
- strncpy(name, argv[0], len);
- return 0;
-}
-
-static int upload_func(int argc, char *argv[])
-{
- char *filename;
- char name[XEN_XSPLICE_NAME_SIZE];
- int fd = 0, rc;
- struct stat buf;
- unsigned char *fbuf;
- ssize_t len;
-
- if ( argc != 2 )
- {
- show_help();
- return -1;
- }
-
- if ( get_name(argc, argv, name) )
- return EINVAL;
-
- filename = argv[1];
- fd = open(filename, O_RDONLY);
- if ( fd < 0 )
- {
- fprintf(stderr, "Could not open %s, error: %d(%s)\n",
- filename, errno, strerror(errno));
- return errno;
- }
- if ( stat(filename, &buf) != 0 )
- {
- fprintf(stderr, "Could not get right size %s, error: %d(%s)\n",
- filename, errno, strerror(errno));
- close(fd);
- return errno;
- }
-
- len = buf.st_size;
- fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
- if ( fbuf == MAP_FAILED )
- {
- fprintf(stderr,"Could not map: %s, error: %d(%s)\n",
- filename, errno, strerror(errno));
- close (fd);
- return errno;
- }
- printf("Uploading %s (%zu bytes)\n", filename, len);
- rc = xc_xsplice_upload(xch, name, fbuf, len);
- if ( rc )
- fprintf(stderr, "Upload failed: %s, error: %d(%s)!\n",
- filename, errno, strerror(errno));
-
- if ( munmap( fbuf, len) )
- {
- fprintf(stderr, "Could not unmap!? error: %d(%s)!\n",
- errno, strerror(errno));
- if ( !rc )
- rc = errno;
- }
- close(fd);
-
- return rc;
-}
-
-/* These MUST match to the 'action_options[]' array slots. */
-enum {
- ACTION_APPLY = 0,
- ACTION_REVERT = 1,
- ACTION_UNLOAD = 2,
- ACTION_REPLACE = 3,
-};
-
-struct {
- int allow; /* State it must be in to call function. */
- int expected; /* The state to be in after the function. */
- const char *name;
- int (*function)(xc_interface *xch, char *name, uint32_t timeout);
- unsigned int executed; /* Has the function been called?. */
-} action_options[] = {
- { .allow = XSPLICE_STATE_CHECKED,
- .expected = XSPLICE_STATE_APPLIED,
- .name = "apply",
- .function = xc_xsplice_apply,
- },
- { .allow = XSPLICE_STATE_APPLIED,
- .expected = XSPLICE_STATE_CHECKED,
- .name = "revert",
- .function = xc_xsplice_revert,
- },
- { .allow = XSPLICE_STATE_CHECKED,
- .expected = -XEN_ENOENT,
- .name = "unload",
- .function = xc_xsplice_unload,
- },
- { .allow = XSPLICE_STATE_CHECKED,
- .expected = XSPLICE_STATE_APPLIED,
- .name = "replace",
- .function = xc_xsplice_replace,
- },
-};
-
-/* Go around 300 * 0.1 seconds = 30 seconds. */
-#define RETRIES 300
-/* aka 0.1 second */
-#define DELAY 100000
-
-int action_func(int argc, char *argv[], unsigned int idx)
-{
- char name[XEN_XSPLICE_NAME_SIZE];
- int rc, original_state;
- xen_xsplice_status_t status;
- unsigned int retry = 0;
-
- if ( argc != 1 )
- {
- show_help();
- return -1;
- }
-
- if ( idx >= ARRAY_SIZE(action_options) )
- return -1;
-
- if ( get_name(argc, argv, name) )
- return EINVAL;
-
- /* Check initial status. */
- rc = xc_xsplice_get(xch, name, &status);
- if ( rc )
- {
- fprintf(stderr, "%s failed to get status %d(%s)!\n",
- name, errno, strerror(errno));
- return -1;
- }
- if ( status.rc == -XEN_EAGAIN )
- {
- fprintf(stderr, "%s failed. Operation already in progress\n", name);
- return -1;
- }
-
- if ( status.state == action_options[idx].expected )
- {
- printf("No action needed\n");
- return 0;
- }
-
- /* Perform action. */
- if ( action_options[idx].allow & status.state )
- {
- printf("Performing %s:", action_options[idx].name);
- rc = action_options[idx].function(xch, name, 0);
- if ( rc )
- {
- fprintf(stderr, "%s failed with %d(%s)\n", name, errno,
- strerror(errno));
- return -1;
- }
- }
- else
- {
- printf("%s: in wrong state (%s), expected (%s)\n",
- name, state2str(status.state),
- state2str(action_options[idx].expected));
- return -1;
- }
-
- original_state = status.state;
- do {
- rc = xc_xsplice_get(xch, name, &status);
- if ( rc )
- {
- rc = -errno;
- break;
- }
-
- if ( status.state != original_state )
- break;
- if ( status.rc && status.rc != -XEN_EAGAIN )
- {
- rc = status.rc;
- break;
- }
-
- printf(".");
- fflush(stdout);
- usleep(DELAY);
- } while ( ++retry < RETRIES );
-
- if ( retry >= RETRIES )
- {
- fprintf(stderr, "%s: Operation didn't complete after 30 seconds.\n", name);
- return -1;
- }
- else
- {
- if ( rc == 0 )
- rc = status.state;
-
- if ( action_options[idx].expected == rc )
- printf(" completed\n");
- else if ( rc < 0 )
- {
- fprintf(stderr, "%s failed with %d(%s)\n", name, -rc, strerror(-rc));
- return -1;
- }
- else
- {
- fprintf(stderr, "%s: in wrong state (%s), expected (%s)\n",
- name, state2str(rc),
- state2str(action_options[idx].expected));
- return -1;
- }
- }
-
- return 0;
-}
-
-static int load_func(int argc, char *argv[])
-{
- int rc;
- char *new_argv[2];
- char *path, *name, *lastdot;
-
- if ( argc != 1 )
- {
- show_help();
- return -1;
- }
- /* <file> */
- new_argv[1] = argv[0];
-
- /* Synthesize the <id> */
- path = strdup(argv[0]);
-
- name = basename(path);
- lastdot = strrchr(name, '.');
- if ( lastdot != NULL )
- *lastdot = '\0';
- new_argv[0] = name;
-
- rc = upload_func(2 /* <id> <file> */, new_argv);
- if ( rc )
- return rc;
-
- rc = action_func(1 /* only <id> */, new_argv, ACTION_APPLY);
- if ( rc )
- action_func(1, new_argv, ACTION_UNLOAD);
-
- free(path);
- return rc;
-}
-
-/*
- * These are also functions in action_options that are called in case
- * none of the ones in main_options match.
- */
-struct {
- const char *name;
- int (*function)(int argc, char *argv[]);
-} main_options[] = {
- { "help", help_func },
- { "list", list_func },
- { "upload", upload_func },
- { "load", load_func },
-};
-
-int main(int argc, char *argv[])
-{
- int i, j, ret;
-
- if ( argc <= 1 )
- {
- show_help();
- return 0;
- }
- for ( i = 0; i < ARRAY_SIZE(main_options); i++ )
- if (!strncmp(main_options[i].name, argv[1], strlen(argv[1])))
- break;
-
- if ( i == ARRAY_SIZE(main_options) )
- {
- for ( j = 0; j < ARRAY_SIZE(action_options); j++ )
- if (!strncmp(action_options[j].name, argv[1], strlen(argv[1])))
- break;
-
- if ( j == ARRAY_SIZE(action_options) )
- {
- fprintf(stderr, "Unrecognised command '%s' -- try "
- "'xen-xsplice help'\n", argv[1]);
- return 1;
- }
- } else
- j = ARRAY_SIZE(action_options);
-
- xch = xc_interface_open(0,0,0);
- if ( !xch )
- {
- fprintf(stderr, "failed to get the handler\n");
- return 0;
- }
-
- if ( i == ARRAY_SIZE(main_options) )
- ret = action_func(argc -2, argv + 2, j);
- else
- ret = main_options[i].function(argc -2, argv + 2);
-
- xc_interface_close(xch);
-
- return !!ret;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
obj-y += decode.o
obj-y += processor.o
obj-y += smc.o
-obj-$(CONFIG_XSPLICE) += xsplice.o
+obj-$(CONFIG_LIVEPATCH) += livepatch.o
#obj-bin-y += ....o
--- /dev/null
+/*
+ * Copyright (C) 2016 Citrix Systems R&D Ltd.
+ */
+#include <xen/errno.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/livepatch_elf.h>
+#include <xen/livepatch.h>
+
+void arch_livepatch_quiesce(void)
+{
+}
+
+void arch_livepatch_revive(void)
+{
+}
+
+int arch_livepatch_verify_func(const struct livepatch_func *func)
+{
+ return -ENOSYS;
+}
+
+void arch_livepatch_apply_jmp(struct livepatch_func *func)
+{
+}
+
+void arch_livepatch_revert_jmp(const struct livepatch_func *func)
+{
+}
+
+void arch_livepatch_post_action(void)
+{
+}
+
+void arch_livepatch_mask(void)
+{
+}
+
+void arch_livepatch_unmask(void)
+{
+}
+
+int arch_livepatch_verify_elf(const struct livepatch_elf *elf)
+{
+ return -ENOSYS;
+}
+
+int arch_livepatch_perform_rel(struct livepatch_elf *elf,
+ const struct livepatch_elf_sec *base,
+ const struct livepatch_elf_sec *rela)
+{
+ return -ENOSYS;
+}
+
+int arch_livepatch_perform_rela(struct livepatch_elf *elf,
+ const struct livepatch_elf_sec *base,
+ const struct livepatch_elf_sec *rela)
+{
+ return -ENOSYS;
+}
+
+int arch_livepatch_secure(const void *va, unsigned int pages, enum va_type type)
+{
+ return -ENOSYS;
+}
+
+void __init arch_livepatch_init(void)
+{
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+++ /dev/null
-/*
- * Copyright (C) 2016 Citrix Systems R&D Ltd.
- */
-#include <xen/errno.h>
-#include <xen/init.h>
-#include <xen/lib.h>
-#include <xen/xsplice_elf.h>
-#include <xen/xsplice.h>
-
-void arch_xsplice_patching_enter(void)
-{
-}
-
-void arch_xsplice_patching_leave(void)
-{
-}
-
-int arch_xsplice_verify_func(const struct xsplice_patch_func *func)
-{
- return -ENOSYS;
-}
-
-void arch_xsplice_apply_jmp(struct xsplice_patch_func *func)
-{
-}
-
-void arch_xsplice_revert_jmp(const struct xsplice_patch_func *func)
-{
-}
-
-void arch_xsplice_post_action(void)
-{
-}
-
-void arch_xsplice_mask(void)
-{
-}
-
-void arch_xsplice_unmask(void)
-{
-}
-
-int arch_xsplice_verify_elf(const struct xsplice_elf *elf)
-{
- return -ENOSYS;
-}
-
-int arch_xsplice_perform_rel(struct xsplice_elf *elf,
- const struct xsplice_elf_sec *base,
- const struct xsplice_elf_sec *rela)
-{
- return -ENOSYS;
-}
-
-int arch_xsplice_perform_rela(struct xsplice_elf *elf,
- const struct xsplice_elf_sec *base,
- const struct xsplice_elf_sec *rela)
-{
- return -ENOSYS;
-}
-
-int arch_xsplice_secure(const void *va, unsigned int pages, enum va_type type)
-{
- return -ENOSYS;
-}
-
-void __init arch_xsplice_init(void)
-{
-}
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
subdir-y += x86_64
alternative-y := alternative.init.o
-alternative-$(CONFIG_XSPLICE) :=
+alternative-$(CONFIG_LIVEPATCH) :=
obj-bin-y += $(alternative-y)
obj-y += apic.o
obj-y += bitops.o
obj-y += i387.o
obj-y += i8259.o
obj-y += io_apic.o
+obj-$(CONFIG_LIVEPATCH) += alternative.o livepatch.o
obj-y += msi.o
obj-y += ioport_emulate.o
obj-y += irq.o
obj-y += tboot.o
obj-y += hpet.o
obj-y += vm_event.o
-obj-$(CONFIG_XSPLICE) += alternative.o xsplice.o
obj-y += xstate.o
obj-$(crash_debug) += gdbstub.o
notes_phdrs =
endif
-ifdef CONFIG_XSPLICE
+ifdef CONFIG_LIVEPATCH
all_symbols = --all-symbols
ifdef CONFIG_FAST_SYMBOL_LOOKUP
all_symbols = --all-symbols --sort-by-name
.PHONY: tests
tests:
- $(MAKE) -f $(BASEDIR)/Rules.mk -C test xsplice
+ $(MAKE) -f $(BASEDIR)/Rules.mk -C test livepatch
ALL_OBJS := $(BASEDIR)/arch/x86/boot/built_in.o $(BASEDIR)/arch/x86/efi/built_in.o $(ALL_OBJS)
#include <asm/system.h>
#include <asm/traps.h>
#include <asm/nmi.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#define MAX_PATCH_LEN (255-1)
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
#ifdef K8_NOP1
-static const unsigned char k8nops[] init_or_xsplice_const = {
+static const unsigned char k8nops[] init_or_livepatch_const = {
K8_NOP1,
K8_NOP2,
K8_NOP3,
K8_NOP7,
K8_NOP8
};
-static const unsigned char * const k8_nops[ASM_NOP_MAX+1] init_or_xsplice_constrel = {
+static const unsigned char * const k8_nops[ASM_NOP_MAX+1] init_or_livepatch_constrel = {
NULL,
k8nops,
k8nops + 1,
#endif
#ifdef P6_NOP1
-static const unsigned char p6nops[] init_or_xsplice_const = {
+static const unsigned char p6nops[] init_or_livepatch_const = {
P6_NOP1,
P6_NOP2,
P6_NOP3,
P6_NOP7,
P6_NOP8
};
-static const unsigned char * const p6_nops[ASM_NOP_MAX+1] init_or_xsplice_constrel = {
+static const unsigned char * const p6_nops[ASM_NOP_MAX+1] init_or_livepatch_constrel = {
NULL,
p6nops,
p6nops + 1,
};
#endif
-static const unsigned char * const *ideal_nops init_or_xsplice_data = k8_nops;
+static const unsigned char * const *ideal_nops init_or_livepatch_data = k8_nops;
static int __init mask_nmi_callback(const struct cpu_user_regs *regs, int cpu)
{
}
/* Use this to add nops to a buffer, then text_poke the whole buffer. */
-static void init_or_xsplice add_nops(void *insns, unsigned int len)
+static void init_or_livepatch add_nops(void *insns, unsigned int len)
{
while ( len > 0 )
{
* You should run this with interrupts disabled or on code that is not
* executing.
*/
-static void *init_or_xsplice text_poke(void *addr, const void *opcode, size_t len)
+static void *init_or_livepatch text_poke(void *addr, const void *opcode, size_t len)
{
memcpy(addr, opcode, len);
sync_core();
* APs have less capabilities than the boot processor are not handled.
* Tough. Make sure you disable such features by hand.
*/
-void init_or_xsplice apply_alternatives_nocheck(struct alt_instr *start, struct alt_instr *end)
+void init_or_livepatch apply_alternatives_nocheck(struct alt_instr *start, struct alt_instr *end)
{
struct alt_instr *a;
u8 *instr, *replacement;
#include <xen/cpu.h>
#include <xen/wait.h>
#include <xen/guest_access.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#include <public/sysctl.h>
#include <public/hvm/hvm_vcpu.h>
#include <asm/regs.h>
* We MUST be last (or before pm_idle). Otherwise after we get the
* softirq we would execute pm_idle (and sleep) and not patch.
*/
- check_for_xsplice_work();
+ check_for_livepatch_work();
}
}
#include <xen/spinlock.h>
#include <asm/uaccess.h>
#include <xen/virtual_region.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#define EX_FIELD(ptr, field) ((unsigned long)&(ptr)->field + (ptr)->field)
return EX_FIELD(x, cont);
}
-static int init_or_xsplice cmp_ex(const void *a, const void *b)
+static int init_or_livepatch cmp_ex(const void *a, const void *b)
{
const struct exception_table_entry *l = a, *r = b;
unsigned long lip = ex_addr(l);
}
#ifndef swap_ex
-static void init_or_xsplice swap_ex(void *a, void *b, int size)
+static void init_or_livepatch swap_ex(void *a, void *b, int size)
{
struct exception_table_entry *l = a, *r = b, tmp;
long delta = b - a;
}
#endif
-void init_or_xsplice sort_exception_table(struct exception_table_entry *start,
+void init_or_livepatch sort_exception_table(struct exception_table_entry *start,
const struct exception_table_entry *stop)
{
sort(start, stop - start,
--- /dev/null
+/*
+ * Copyright (C) 2016 Citrix Systems R&D Ltd.
+ */
+
+#include <xen/errno.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/mm.h>
+#include <xen/pfn.h>
+#include <xen/vmap.h>
+#include <xen/livepatch_elf.h>
+#include <xen/livepatch.h>
+
+#include <asm/nmi.h>
+
+#define PATCH_INSN_SIZE 5
+
+void arch_livepatch_quiesce(void)
+{
+ /* Disable WP to allow changes to read-only pages. */
+ write_cr0(read_cr0() & ~X86_CR0_WP);
+}
+
+void arch_livepatch_revive(void)
+{
+ /* Reinstate WP. */
+ write_cr0(read_cr0() | X86_CR0_WP);
+}
+
+int arch_livepatch_verify_func(const struct livepatch_func *func)
+{
+ /* No NOP patching yet. */
+ if ( !func->new_size )
+ return -EOPNOTSUPP;
+
+ if ( func->old_size < PATCH_INSN_SIZE )
+ return -EINVAL;
+
+ return 0;
+}
+
+void arch_livepatch_apply_jmp(struct livepatch_func *func)
+{
+ int32_t val;
+ uint8_t *old_ptr;
+
+ BUILD_BUG_ON(PATCH_INSN_SIZE > sizeof(func->opaque));
+ BUILD_BUG_ON(PATCH_INSN_SIZE != (1 + sizeof(val)));
+
+ old_ptr = func->old_addr;
+ memcpy(func->opaque, old_ptr, PATCH_INSN_SIZE);
+
+ *old_ptr++ = 0xe9; /* Relative jump */
+ val = func->new_addr - func->old_addr - PATCH_INSN_SIZE;
+ memcpy(old_ptr, &val, sizeof(val));
+}
+
+void arch_livepatch_revert_jmp(const struct livepatch_func *func)
+{
+ memcpy(func->old_addr, func->opaque, PATCH_INSN_SIZE);
+}
+
+/* Serialise the CPU pipeline. */
+void arch_livepatch_post_action(void)
+{
+ cpuid_eax(0);
+}
+
+static nmi_callback_t *saved_nmi_callback;
+/*
+ * Note that because of this NOP code the do_nmi is not safely patchable.
+ * Also if we do receive 'real' NMIs we have lost them.
+ */
+static int mask_nmi_callback(const struct cpu_user_regs *regs, int cpu)
+{
+ /* TODO: Handle missing NMI/MCE.*/
+ return 1;
+}
+
+void arch_livepatch_mask(void)
+{
+ saved_nmi_callback = set_nmi_callback(mask_nmi_callback);
+}
+
+void arch_livepatch_unmask(void)
+{
+ set_nmi_callback(saved_nmi_callback);
+}
+
+int arch_livepatch_verify_elf(const struct livepatch_elf *elf)
+{
+
+ const Elf_Ehdr *hdr = elf->hdr;
+
+ if ( hdr->e_machine != EM_X86_64 ||
+ hdr->e_ident[EI_CLASS] != ELFCLASS64 ||
+ hdr->e_ident[EI_DATA] != ELFDATA2LSB )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Unsupported ELF Machine type!\n",
+ elf->name);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+int arch_livepatch_perform_rel(struct livepatch_elf *elf,
+ const struct livepatch_elf_sec *base,
+ const struct livepatch_elf_sec *rela)
+{
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: SHT_REL relocation unsupported\n",
+ elf->name);
+ return -EOPNOTSUPP;
+}
+
+int arch_livepatch_perform_rela(struct livepatch_elf *elf,
+ const struct livepatch_elf_sec *base,
+ const struct livepatch_elf_sec *rela)
+{
+ const Elf_RelA *r;
+ unsigned int symndx, i;
+ uint64_t val;
+ uint8_t *dest;
+
+ /* Nothing to do. */
+ if ( !rela->sec->sh_size )
+ return 0;
+
+ if ( rela->sec->sh_entsize < sizeof(Elf_RelA) ||
+ rela->sec->sh_size % rela->sec->sh_entsize )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section relative header is corrupted!\n",
+ elf->name);
+ return -EINVAL;
+ }
+
+ for ( i = 0; i < (rela->sec->sh_size / rela->sec->sh_entsize); i++ )
+ {
+ r = rela->data + i * rela->sec->sh_entsize;
+
+ symndx = ELF64_R_SYM(r->r_info);
+
+ if ( symndx > elf->nsym )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Relative relocation wants symbol@%u which is past end!\n",
+ elf->name, symndx);
+ return -EINVAL;
+ }
+
+ dest = base->load_addr + r->r_offset;
+ val = r->r_addend + elf->sym[symndx].sym->st_value;
+
+ switch ( ELF64_R_TYPE(r->r_info) )
+ {
+ case R_X86_64_NONE:
+ break;
+
+ case R_X86_64_64:
+ if ( r->r_offset >= base->sec->sh_size ||
+ (r->r_offset + sizeof(uint64_t)) > base->sec->sh_size )
+ goto bad_offset;
+
+ *(uint64_t *)dest = val;
+ break;
+
+ case R_X86_64_PLT32:
+ /*
+ * Xen uses -fpic which normally uses PLT relocations
+ * except that it sets visibility to hidden which means
+ * that they are not used. However, when gcc cannot
+ * inline memcpy it emits memcpy with default visibility
+ * which then creates a PLT relocation. It can just be
+ * treated the same as R_X86_64_PC32.
+ */
+ case R_X86_64_PC32:
+ if ( r->r_offset >= base->sec->sh_size ||
+ (r->r_offset + sizeof(uint32_t)) > base->sec->sh_size )
+ goto bad_offset;
+
+ val -= (uint64_t)dest;
+ *(int32_t *)dest = val;
+ if ( (int64_t)val != *(int32_t *)dest )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Overflow in relocation %u in %s for %s!\n",
+ elf->name, i, rela->name, base->name);
+ return -EOVERFLOW;
+ }
+ break;
+
+ default:
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Unhandled relocation %lu\n",
+ elf->name, ELF64_R_TYPE(r->r_info));
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+
+ bad_offset:
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Relative relocation offset is past %s section!\n",
+ elf->name, base->name);
+ return -EINVAL;
+}
+
+/*
+ * Once the resolving symbols, performing relocations, etc is complete
+ * we secure the memory by putting in the proper page table attributes
+ * for the desired type.
+ */
+int arch_livepatch_secure(const void *va, unsigned int pages, enum va_type type)
+{
+ unsigned long start = (unsigned long)va;
+ unsigned int flag;
+
+ ASSERT(va);
+ ASSERT(pages);
+
+ if ( type == LIVEPATCH_VA_RX )
+ flag = PAGE_HYPERVISOR_RX;
+ else if ( type == LIVEPATCH_VA_RW )
+ flag = PAGE_HYPERVISOR_RW;
+ else
+ flag = PAGE_HYPERVISOR_RO;
+
+ modify_xen_mappings(start, start + pages * PAGE_SIZE, flag);
+
+ return 0;
+}
+
+void __init arch_livepatch_init(void)
+{
+ void *start, *end;
+
+ start = (void *)xen_virt_end;
+ end = (void *)(XEN_VIRT_END - NR_CPUS * PAGE_SIZE);
+
+ BUG_ON(end <= start);
+
+ vm_init_type(VMAP_XEN, start, end);
+}
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
.PHONY: default
-XSPLICE := xen_hello_world.xsplice
-XSPLICE_BYE := xen_bye_world.xsplice
-XSPLICE_REPLACE := xen_replace_world.xsplice
+LIVEPATCH := xen_hello_world.livepatch
+LIVEPATCH_BYE := xen_bye_world.livepatch
+LIVEPATCH_REPLACE := xen_replace_world.livepatch
-default: xsplice
+default: livepatch
-install: xsplice
- $(INSTALL_DATA) $(XSPLICE) $(DESTDIR)$(DEBUG_DIR)/$(XSPLICE)
- $(INSTALL_DATA) $(XSPLICE_BYE) $(DESTDIR)$(DEBUG_DIR)/$(XSPLICE_BYE)
- $(INSTALL_DATA) $(XSPLICE_REPLACE) $(DESTDIR)$(DEBUG_DIR)/$(XSPLICE_REPLACE)
+install: livepatch
+ $(INSTALL_DATA) $(LIVEPATCH) $(DESTDIR)$(DEBUG_DIR)/$(LIVEPATCH)
+ $(INSTALL_DATA) $(LIVEPATCH_BYE) $(DESTDIR)$(DEBUG_DIR)/$(LIVEPATCH_BYE)
+ $(INSTALL_DATA) $(LIVEPATCH_REPLACE) $(DESTDIR)$(DEBUG_DIR)/$(LIVEPATCH_REPLACE)
uninstall:
- rm -f $(DESTDIR)$(DEBUG_DIR)/$(XSPLICE)
- rm -f $(DESTDIR)$(DEBUG_DIR)/$(XSPLICE_BYE)
- rm -f $(DESTDIR)$(DEBUG_DIR)/$(XSPLICE_REPLACE)
+ rm -f $(DESTDIR)$(DEBUG_DIR)/$(LIVEPATCH)
+ rm -f $(DESTDIR)$(DEBUG_DIR)/$(LIVEPATCH_BYE)
+ rm -f $(DESTDIR)$(DEBUG_DIR)/$(LIVEPATCH_REPLACE)
.PHONY: clean
clean::
- rm -f *.o .*.o.d *.xsplice config.h
+ rm -f *.o .*.o.d *.livepatch config.h
#
# To compute these values we need the binary files: xen-syms
xen_hello_world.o: config.h
-.PHONY: $(XSPLICE)
-$(XSPLICE): xen_hello_world_func.o xen_hello_world.o note.o
- $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(XSPLICE) $^
+.PHONY: $(LIVEPATCH)
+$(LIVEPATCH): xen_hello_world_func.o xen_hello_world.o note.o
+ $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH) $^
#
-# This target is only accessible if CONFIG_XSPLICE is defined, which
+# This target is only accessible if CONFIG_LIVEPATCH is defined, which
# depends on $(build_id_linker) being available. Hence we do not
# need any checks.
#
note.o:
$(OBJCOPY) -O binary --only-section=.note.gnu.build-id $(BASEDIR)/xen-syms $@.bin
$(OBJCOPY) -I binary -O elf64-x86-64 -B i386:x86-64 \
- --rename-section=.data=.xsplice.depends -S $@.bin $@
+ --rename-section=.data=.livepatch.depends -S $@.bin $@
rm -f $@.bin
#
-# Extract the build-id of the xen_hello_world.xsplice
+# Extract the build-id of the xen_hello_world.livepatch
# (which xen_bye_world will depend on).
#
.PHONY: hello_world_note.o
-hello_world_note.o: $(XSPLICE)
- $(OBJCOPY) -O binary --only-section=.note.gnu.build-id $(XSPLICE) $@.bin
+hello_world_note.o: $(LIVEPATCH)
+ $(OBJCOPY) -O binary --only-section=.note.gnu.build-id $(LIVEPATCH) $@.bin
$(OBJCOPY) -I binary -O elf64-x86-64 -B i386:x86-64 \
- --rename-section=.data=.xsplice.depends -S $@.bin $@
+ --rename-section=.data=.livepatch.depends -S $@.bin $@
rm -f $@.bin
xen_bye_world.o: config.h
-.PHONY: $(XSPLICE_BYE)
-$(XSPLICE_BYE): xen_bye_world_func.o xen_bye_world.o hello_world_note.o
- $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(XSPLICE_BYE) $^
+.PHONY: $(LIVEPATCH_BYE)
+$(LIVEPATCH_BYE): xen_bye_world_func.o xen_bye_world.o hello_world_note.o
+ $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_BYE) $^
xen_replace_world.o: config.h
-.PHONY: $(XSPLICE_REPLACE)
-$(XSPLICE_REPLACE): xen_replace_world_func.o xen_replace_world.o note.o
- $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(XSPLICE_REPLACE) $^
+.PHONY: $(LIVEPATCH_REPLACE)
+$(LIVEPATCH_REPLACE): xen_replace_world_func.o xen_replace_world.o note.o
+ $(LD) $(LDFLAGS) $(build_id_linker) -r -o $(LIVEPATCH_REPLACE) $^
-.PHONY: xsplice
-xsplice: $(XSPLICE) $(XSPLICE_BYE) $(XSPLICE_REPLACE)
+.PHONY: livepatch
+livepatch: $(LIVEPATCH) $(LIVEPATCH_BYE) $(LIVEPATCH_REPLACE)
#include <xen/lib.h>
#include <xen/types.h>
#include <xen/version.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#include <public/sysctl.h>
static char bye_world_patch_this_fnc[] = "xen_extra_version";
extern const char *xen_bye_world(void);
-struct xsplice_patch_func __section(".xsplice.funcs") xsplice_xen_bye_world = {
- .version = XSPLICE_PAYLOAD_VERSION,
+struct livepatch_func __section(".livepatch.funcs") livepatch_xen_bye_world = {
+ .version = LIVEPATCH_PAYLOAD_VERSION,
.name = bye_world_patch_this_fnc,
.new_addr = xen_bye_world,
.old_addr = xen_extra_version,
#include "config.h"
#include <xen/types.h>
#include <xen/version.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#include <public/sysctl.h>
static char hello_world_patch_this_fnc[] = "xen_extra_version";
extern const char *xen_hello_world(void);
-struct xsplice_patch_func __section(".xsplice.funcs") xsplice_xen_hello_world = {
- .version = XSPLICE_PAYLOAD_VERSION,
+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,
#include "config.h"
#include <xen/lib.h>
#include <xen/types.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#include <public/sysctl.h>
static char xen_replace_world_name[] = "xen_extra_version";
extern const char *xen_replace_world(void);
-struct xsplice_patch_func __section(".xsplice.funcs") xsplice_xen_replace_world = {
- .version = XSPLICE_PAYLOAD_VERSION,
+struct livepatch_func __section(".livepatch.funcs") livepatch_xen_replace_world = {
+ .version = LIVEPATCH_PAYLOAD_VERSION,
.name = xen_replace_world_name,
.old_addr = 0, /* Forces the hypervisor to lookup .name */
.new_addr = xen_replace_world,
#include <xen/paging.h>
#include <xen/virtual_region.h>
#include <xen/watchdog.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/atomic.h>
+++ /dev/null
-/*
- * Copyright (C) 2016 Citrix Systems R&D Ltd.
- */
-
-#include <xen/errno.h>
-#include <xen/init.h>
-#include <xen/lib.h>
-#include <xen/mm.h>
-#include <xen/pfn.h>
-#include <xen/vmap.h>
-#include <xen/xsplice_elf.h>
-#include <xen/xsplice.h>
-
-#include <asm/nmi.h>
-
-#define PATCH_INSN_SIZE 5
-
-void arch_xsplice_patching_enter(void)
-{
- /* Disable WP to allow changes to read-only pages. */
- write_cr0(read_cr0() & ~X86_CR0_WP);
-}
-
-void arch_xsplice_patching_leave(void)
-{
- /* Reinstate WP. */
- write_cr0(read_cr0() | X86_CR0_WP);
-}
-
-int arch_xsplice_verify_func(const struct xsplice_patch_func *func)
-{
- /* No NOP patching yet. */
- if ( !func->new_size )
- return -EOPNOTSUPP;
-
- if ( func->old_size < PATCH_INSN_SIZE )
- return -EINVAL;
-
- return 0;
-}
-
-void arch_xsplice_apply_jmp(struct xsplice_patch_func *func)
-{
- int32_t val;
- uint8_t *old_ptr;
-
- BUILD_BUG_ON(PATCH_INSN_SIZE > sizeof(func->opaque));
- BUILD_BUG_ON(PATCH_INSN_SIZE != (1 + sizeof(val)));
-
- old_ptr = func->old_addr;
- memcpy(func->opaque, old_ptr, PATCH_INSN_SIZE);
-
- *old_ptr++ = 0xe9; /* Relative jump */
- val = func->new_addr - func->old_addr - PATCH_INSN_SIZE;
- memcpy(old_ptr, &val, sizeof(val));
-}
-
-void arch_xsplice_revert_jmp(const struct xsplice_patch_func *func)
-{
- memcpy(func->old_addr, func->opaque, PATCH_INSN_SIZE);
-}
-
-/* Serialise the CPU pipeline. */
-void arch_xsplice_post_action(void)
-{
- cpuid_eax(0);
-}
-
-static nmi_callback_t *saved_nmi_callback;
-/*
- * Note that because of this NOP code the do_nmi is not safely patchable.
- * Also if we do receive 'real' NMIs we have lost them.
- */
-static int mask_nmi_callback(const struct cpu_user_regs *regs, int cpu)
-{
- /* TODO: Handle missing NMI/MCE.*/
- return 1;
-}
-
-void arch_xsplice_mask(void)
-{
- saved_nmi_callback = set_nmi_callback(mask_nmi_callback);
-}
-
-void arch_xsplice_unmask(void)
-{
- set_nmi_callback(saved_nmi_callback);
-}
-
-int arch_xsplice_verify_elf(const struct xsplice_elf *elf)
-{
-
- const Elf_Ehdr *hdr = elf->hdr;
-
- if ( hdr->e_machine != EM_X86_64 ||
- hdr->e_ident[EI_CLASS] != ELFCLASS64 ||
- hdr->e_ident[EI_DATA] != ELFDATA2LSB )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Unsupported ELF Machine type!\n",
- elf->name);
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-int arch_xsplice_perform_rel(struct xsplice_elf *elf,
- const struct xsplice_elf_sec *base,
- const struct xsplice_elf_sec *rela)
-{
- dprintk(XENLOG_ERR, XSPLICE "%s: SHT_REL relocation unsupported\n",
- elf->name);
- return -EOPNOTSUPP;
-}
-
-int arch_xsplice_perform_rela(struct xsplice_elf *elf,
- const struct xsplice_elf_sec *base,
- const struct xsplice_elf_sec *rela)
-{
- const Elf_RelA *r;
- unsigned int symndx, i;
- uint64_t val;
- uint8_t *dest;
-
- /* Nothing to do. */
- if ( !rela->sec->sh_size )
- return 0;
-
- if ( rela->sec->sh_entsize < sizeof(Elf_RelA) ||
- rela->sec->sh_size % rela->sec->sh_entsize )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section relative header is corrupted!\n",
- elf->name);
- return -EINVAL;
- }
-
- for ( i = 0; i < (rela->sec->sh_size / rela->sec->sh_entsize); i++ )
- {
- r = rela->data + i * rela->sec->sh_entsize;
-
- symndx = ELF64_R_SYM(r->r_info);
-
- if ( symndx > elf->nsym )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Relative relocation wants symbol@%u which is past end!\n",
- elf->name, symndx);
- return -EINVAL;
- }
-
- dest = base->load_addr + r->r_offset;
- val = r->r_addend + elf->sym[symndx].sym->st_value;
-
- switch ( ELF64_R_TYPE(r->r_info) )
- {
- case R_X86_64_NONE:
- break;
-
- case R_X86_64_64:
- if ( r->r_offset >= base->sec->sh_size ||
- (r->r_offset + sizeof(uint64_t)) > base->sec->sh_size )
- goto bad_offset;
-
- *(uint64_t *)dest = val;
- break;
-
- case R_X86_64_PLT32:
- /*
- * Xen uses -fpic which normally uses PLT relocations
- * except that it sets visibility to hidden which means
- * that they are not used. However, when gcc cannot
- * inline memcpy it emits memcpy with default visibility
- * which then creates a PLT relocation. It can just be
- * treated the same as R_X86_64_PC32.
- */
- case R_X86_64_PC32:
- if ( r->r_offset >= base->sec->sh_size ||
- (r->r_offset + sizeof(uint32_t)) > base->sec->sh_size )
- goto bad_offset;
-
- val -= (uint64_t)dest;
- *(int32_t *)dest = val;
- if ( (int64_t)val != *(int32_t *)dest )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Overflow in relocation %u in %s for %s!\n",
- elf->name, i, rela->name, base->name);
- return -EOVERFLOW;
- }
- break;
-
- default:
- dprintk(XENLOG_ERR, XSPLICE "%s: Unhandled relocation %lu\n",
- elf->name, ELF64_R_TYPE(r->r_info));
- return -EOPNOTSUPP;
- }
- }
-
- return 0;
-
- bad_offset:
- dprintk(XENLOG_ERR, XSPLICE "%s: Relative relocation offset is past %s section!\n",
- elf->name, base->name);
- return -EINVAL;
-}
-
-/*
- * Once the resolving symbols, performing relocations, etc is complete
- * we secure the memory by putting in the proper page table attributes
- * for the desired type.
- */
-int arch_xsplice_secure(const void *va, unsigned int pages, enum va_type type)
-{
- unsigned long start = (unsigned long)va;
- unsigned int flag;
-
- ASSERT(va);
- ASSERT(pages);
-
- if ( type == XSPLICE_VA_RX )
- flag = PAGE_HYPERVISOR_RX;
- else if ( type == XSPLICE_VA_RW )
- flag = PAGE_HYPERVISOR_RW;
- else
- flag = PAGE_HYPERVISOR_RO;
-
- modify_xen_mappings(start, start + pages * PAGE_SIZE, flag);
-
- return 0;
-}
-
-void __init arch_xsplice_init(void)
-{
- void *start, *end;
-
- start = (void *)xen_virt_end;
- end = (void *)(XEN_VIRT_END - NR_CPUS * PAGE_SIZE);
-
- BUG_ON(end <= start);
-
- vm_init_type(VMAP_XEN, start, end);
-}
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
endmenu
-# Enable/Disable xsplice support
-config XSPLICE
- bool "xSplice live patching support (TECH PREVIEW)"
+# Enable/Disable live patching support
+config LIVEPATCH
+ bool "Live patching support (TECH PREVIEW)"
default n
depends on X86 && HAS_BUILD_ID = "y"
---help---
config FAST_SYMBOL_LOOKUP
bool "Fast symbol lookup (bigger binary)"
default y
- depends on XSPLICE
+ depends on LIVEPATCH
---help---
When searching for symbol addresses we can use the built-in system
that is optimized for searching symbols using addresses as the key.
However using it for the inverse (find address using the symbol name)
it is slow. This extra data and code (~55kB) speeds up the search.
- The only user of this is xSplice.
+ The only user of this is Live patching.
If unsure, say Y.
endmenu
obj-$(CONFIG_KEXEC) += kexec.o
obj-$(CONFIG_KEXEC) += kimage.o
obj-y += lib.o
+obj-$(CONFIG_LIVEPATCH) += livepatch.o livepatch_elf.o
obj-y += lzo.o
obj-$(CONFIG_HAS_MEM_ACCESS) += mem_access.o
obj-y += memory.o
obj-y += wait.o
obj-$(CONFIG_XENOPROF) += xenoprof.o
obj-y += xmalloc_tlsf.o
-obj-$(CONFIG_XSPLICE) += xsplice.o
-obj-$(CONFIG_XSPLICE) += xsplice_elf.o
obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma unlzo unlz4 earlycpio,$(n).init.o)
--- /dev/null
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ *
+ */
+
+#include <xen/cpu.h>
+#include <xen/elf.h>
+#include <xen/err.h>
+#include <xen/guest_access.h>
+#include <xen/keyhandler.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/mm.h>
+#include <xen/sched.h>
+#include <xen/smp.h>
+#include <xen/softirq.h>
+#include <xen/spinlock.h>
+#include <xen/string.h>
+#include <xen/symbols.h>
+#include <xen/version.h>
+#include <xen/virtual_region.h>
+#include <xen/vmap.h>
+#include <xen/wait.h>
+#include <xen/livepatch_elf.h>
+#include <xen/livepatch.h>
+
+#include <asm/event.h>
+
+/*
+ * Protects against payload_list operations and also allows only one
+ * caller in schedule_work.
+ */
+static DEFINE_SPINLOCK(payload_lock);
+static LIST_HEAD(payload_list);
+
+/*
+ * Patches which have been applied. Need RCU in case we crash (and then
+ * traps code would iterate via applied_list) when adding entries onthe list.
+ */
+static DEFINE_RCU_READ_LOCK(rcu_applied_lock);
+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. */
+ 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). */
+ char name[XEN_LIVEPATCH_NAME_SIZE]; /* Name of it. */
+};
+
+/* Defines an outstanding patching action. */
+struct livepatch_work
+{
+ atomic_t semaphore; /* Used to rendezvous CPUs in
+ check_for_livepatch_work. */
+ uint32_t timeout; /* Timeout to do the operation. */
+ struct payload *data; /* The payload on which to act. */
+ volatile bool_t do_work; /* Signals work to do. */
+ volatile bool_t ready; /* Signals all CPUs synchronized. */
+ unsigned int cmd; /* Action request: LIVEPATCH_ACTION_* */
+};
+
+/* There can be only one outstanding patching action. */
+static struct livepatch_work livepatch_work;
+
+/*
+ * Indicate whether the CPU needs to consult livepatch_work structure.
+ * We want an per-cpu data structure otherwise the check_for_livepatch_work
+ * would hammer a global livepatch_work structure on every guest VMEXIT.
+ * Having an per-cpu lessens the load.
+ */
+static DEFINE_PER_CPU(bool_t, work_to_do);
+
+static int get_name(const xen_livepatch_name_t *name, char *n)
+{
+ if ( !name->size || name->size > XEN_LIVEPATCH_NAME_SIZE )
+ return -EINVAL;
+
+ if ( name->pad[0] || name->pad[1] || name->pad[2] )
+ return -EINVAL;
+
+ if ( copy_from_guest(n, name->name, name->size) )
+ return -EFAULT;
+
+ if ( n[name->size - 1] )
+ return -EINVAL;
+
+ return 0;
+}
+
+static int verify_payload(const xen_sysctl_livepatch_upload_t *upload, char *n)
+{
+ if ( get_name(&upload->name, n) )
+ return -EINVAL;
+
+ if ( !upload->size )
+ return -EINVAL;
+
+ if ( upload->size > MB(2) )
+ return -EINVAL;
+
+ if ( !guest_handle_okay(upload->payload, upload->size) )
+ return -EFAULT;
+
+ return 0;
+}
+
+bool_t is_patch(const void *ptr)
+{
+ const struct payload *data;
+ bool_t r = 0;
+
+ /*
+ * Only RCU locking since this list is only ever changed during apply
+ * or revert context. And in case it dies there we need an safe list.
+ */
+ rcu_read_lock(&rcu_applied_lock);
+ list_for_each_entry_rcu ( data, &applied_list, applied_list )
+ {
+ if ( (ptr >= data->rw_addr &&
+ ptr < (data->rw_addr + data->rw_size)) ||
+ (ptr >= data->ro_addr &&
+ ptr < (data->ro_addr + data->ro_size)) ||
+ (ptr >= data->text_addr &&
+ ptr < (data->text_addr + data->text_size)) )
+ {
+ r = 1;
+ break;
+ }
+
+ }
+ rcu_read_unlock(&rcu_applied_lock);
+
+ return r;
+}
+
+unsigned long livepatch_symbols_lookup_by_name(const char *symname)
+{
+ const struct payload *data;
+
+ ASSERT(spin_is_locked(&payload_lock));
+ list_for_each_entry ( data, &payload_list, list )
+ {
+ unsigned int i;
+
+ for ( i = 0; i < data->nsyms; i++ )
+ {
+ if ( !data->symtab[i].new_symbol )
+ continue;
+
+ if ( !strcmp(data->symtab[i].name, symname) )
+ return data->symtab[i].value;
+ }
+ }
+
+ return 0;
+}
+
+static const char *livepatch_symbols_lookup(unsigned long addr,
+ unsigned long *symbolsize,
+ unsigned long *offset,
+ char *namebuf)
+{
+ const struct payload *data;
+ unsigned int i, best;
+ const void *va = (const void *)addr;
+ const char *n = NULL;
+
+ /*
+ * Only RCU locking since this list is only ever changed during apply
+ * or revert context. And in case it dies there we need an safe list.
+ */
+ rcu_read_lock(&rcu_applied_lock);
+ list_for_each_entry_rcu ( data, &applied_list, applied_list )
+ {
+ if ( va < data->text_addr ||
+ va >= (data->text_addr + data->text_size) )
+ continue;
+
+ best = UINT_MAX;
+
+ for ( i = 0; i < data->nsyms; i++ )
+ {
+ if ( data->symtab[i].value <= addr &&
+ (best == UINT_MAX ||
+ data->symtab[best].value < data->symtab[i].value) )
+ best = i;
+ }
+
+ if ( best == UINT_MAX )
+ break;
+
+ if ( symbolsize )
+ *symbolsize = data->symtab[best].size;
+ if ( offset )
+ *offset = addr - data->symtab[best].value;
+ if ( namebuf )
+ strlcpy(namebuf, data->name, KSYM_NAME_LEN);
+
+ n = data->symtab[best].name;
+ break;
+ }
+ rcu_read_unlock(&rcu_applied_lock);
+
+ return n;
+}
+
+static struct payload *find_payload(const char *name)
+{
+ struct payload *data, *found = NULL;
+
+ ASSERT(spin_is_locked(&payload_lock));
+ list_for_each_entry ( data, &payload_list, list )
+ {
+ if ( !strcmp(data->name, name) )
+ {
+ found = data;
+ break;
+ }
+ }
+
+ return found;
+}
+
+/*
+ * Functions related to XEN_SYSCTL_LIVEPATCH_UPLOAD (see livepatch_upload), and
+ * freeing payload (XEN_SYSCTL_LIVEPATCH_ACTION:LIVEPATCH_ACTION_UNLOAD).
+ */
+
+static void free_payload_data(struct payload *payload)
+{
+ /* Set to zero until "move_payload". */
+ if ( !payload->pages )
+ return;
+
+ vfree((void *)payload->text_addr);
+
+ payload->pages = 0;
+}
+
+/*
+* calc_section computes the size (taking into account section alignment).
+*
+* Furthermore the offset is set with the offset from the start of the virtual
+* address space for the payload (using passed in size). This is used in
+* move_payload to figure out the destination location (load_addr).
+*/
+static void calc_section(const struct livepatch_elf_sec *sec, size_t *size,
+ unsigned int *offset)
+{
+ const Elf_Shdr *s = sec->sec;
+ size_t align_size;
+
+ align_size = ROUNDUP(*size, s->sh_addralign);
+ *offset = align_size;
+ *size = s->sh_size + align_size;
+}
+
+static int move_payload(struct payload *payload, struct livepatch_elf *elf)
+{
+ void *text_buf, *ro_buf, *rw_buf;
+ unsigned int i;
+ size_t size = 0;
+ unsigned int *offset;
+ int rc = 0;
+
+ offset = xmalloc_array(unsigned int, elf->hdr->e_shnum);
+ if ( !offset )
+ return -ENOMEM;
+
+ /* Compute size of different regions. */
+ for ( i = 1; i < elf->hdr->e_shnum; i++ )
+ {
+ /*
+ * Do nothing. These are .rel.text, rel.*, .symtab, .strtab,
+ * and .shstrtab. For the non-relocate we allocate and copy these
+ * via other means - and the .rel we can ignore as we only use it
+ * once during loading.
+ */
+ if ( !(elf->sec[i].sec->sh_flags & SHF_ALLOC) )
+ offset[i] = UINT_MAX;
+ else if ( (elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
+ !(elf->sec[i].sec->sh_flags & SHF_WRITE) )
+ calc_section(&elf->sec[i], &payload->text_size, &offset[i]);
+ else if ( !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
+ (elf->sec[i].sec->sh_flags & SHF_WRITE) )
+ calc_section(&elf->sec[i], &payload->rw_size, &offset[i]);
+ else if ( !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
+ !(elf->sec[i].sec->sh_flags & SHF_WRITE) )
+ calc_section(&elf->sec[i], &payload->ro_size, &offset[i]);
+ else
+ {
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Not supporting %s section!\n",
+ elf->name, elf->sec[i].name);
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ }
+
+ /*
+ * Total of all three regions - RX, RW, and RO. We have to have
+ * keep them in seperate pages so we PAGE_ALIGN the RX and RW to have
+ * them on seperate pages. The last one will by default fall on its
+ * own page.
+ */
+ size = PAGE_ALIGN(payload->text_size) + PAGE_ALIGN(payload->rw_size) +
+ payload->ro_size;
+
+ size = PFN_UP(size); /* Nr of pages. */
+ text_buf = vmalloc_xen(size * PAGE_SIZE);
+ if ( !text_buf )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Could not allocate memory for payload!\n",
+ elf->name);
+ rc = -ENOMEM;
+ goto out;
+ }
+ rw_buf = text_buf + PAGE_ALIGN(payload->text_size);
+ ro_buf = rw_buf + PAGE_ALIGN(payload->rw_size);
+
+ payload->pages = size;
+ payload->text_addr = text_buf;
+ payload->rw_addr = rw_buf;
+ payload->ro_addr = ro_buf;
+
+ for ( i = 1; i < elf->hdr->e_shnum; i++ )
+ {
+ if ( elf->sec[i].sec->sh_flags & SHF_ALLOC )
+ {
+ void *buf;
+
+ if ( elf->sec[i].sec->sh_flags & SHF_EXECINSTR )
+ buf = text_buf;
+ else if ( elf->sec[i].sec->sh_flags & SHF_WRITE )
+ buf = rw_buf;
+ else
+ buf = ro_buf;
+
+ ASSERT(offset[i] != UINT_MAX);
+
+ elf->sec[i].load_addr = buf + offset[i];
+
+ /* Don't copy NOBITS - such as BSS. */
+ if ( elf->sec[i].sec->sh_type != SHT_NOBITS )
+ {
+ memcpy(elf->sec[i].load_addr, elf->sec[i].data,
+ elf->sec[i].sec->sh_size);
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Loaded %s at %p\n",
+ elf->name, elf->sec[i].name, elf->sec[i].load_addr);
+ }
+ else
+ memset(elf->sec[i].load_addr, 0, elf->sec[i].sec->sh_size);
+ }
+ }
+
+ out:
+ xfree(offset);
+
+ return rc;
+}
+
+static int secure_payload(struct payload *payload, struct livepatch_elf *elf)
+{
+ int rc;
+ unsigned int text_pages, rw_pages, ro_pages;
+
+ text_pages = PFN_UP(payload->text_size);
+ ASSERT(text_pages);
+
+ rc = arch_livepatch_secure(payload->text_addr, text_pages, LIVEPATCH_VA_RX);
+ if ( rc )
+ return rc;
+
+ rw_pages = PFN_UP(payload->rw_size);
+ if ( rw_pages )
+ {
+ rc = arch_livepatch_secure(payload->rw_addr, rw_pages, LIVEPATCH_VA_RW);
+ if ( rc )
+ return rc;
+ }
+
+ ro_pages = PFN_UP(payload->ro_size);
+ if ( ro_pages )
+ rc = arch_livepatch_secure(payload->ro_addr, ro_pages, LIVEPATCH_VA_RO);
+
+ ASSERT(ro_pages + rw_pages + text_pages == payload->pages);
+
+ return rc;
+}
+
+static int check_special_sections(const struct livepatch_elf *elf)
+{
+ unsigned int i;
+ static const char *const names[] = { ELF_LIVEPATCH_FUNC,
+ ELF_LIVEPATCH_DEPENDS,
+ ELF_BUILD_ID_NOTE};
+ DECLARE_BITMAP(found, ARRAY_SIZE(names)) = { 0 };
+
+ 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_ERR, LIVEPATCH "%s: %s is missing!\n",
+ elf->name, names[i]);
+ return -EINVAL;
+ }
+
+ if ( !sec->sec->sh_size )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: %s is empty!\n",
+ elf->name, names[i]);
+ return -EINVAL;
+ }
+
+ if ( test_and_set_bit(i, found) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: %s was seen more than once!\n",
+ elf->name, names[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int prepare_payload(struct payload *payload,
+ struct livepatch_elf *elf)
+{
+ const struct livepatch_elf_sec *sec;
+ unsigned int i;
+ struct livepatch_func *f;
+ struct virtual_region *region;
+ const Elf_Note *n;
+
+ sec = livepatch_elf_sec_by_name(elf, ELF_LIVEPATCH_FUNC);
+ ASSERT(sec);
+ if ( sec->sec->sh_size % sizeof(*payload->funcs) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Wrong size of "ELF_LIVEPATCH_FUNC"!\n",
+ elf->name);
+ 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 )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Wrong version (%u). Expected %d!\n",
+ elf->name, f->version, LIVEPATCH_PAYLOAD_VERSION);
+ return -EOPNOTSUPP;
+ }
+
+ if ( !f->new_addr || !f->new_size )
+ {
+ dprintk(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;
+
+ /* Lookup function's old address if not already resolved. */
+ if ( !f->old_addr )
+ {
+ f->old_addr = (void *)symbols_lookup_by_name(f->name);
+ if ( !f->old_addr )
+ {
+ f->old_addr = (void *)livepatch_symbols_lookup_by_name(f->name);
+ if ( !f->old_addr )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Could not resolve old address of %s\n",
+ elf->name, f->name);
+ return -ENOENT;
+ }
+ }
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Resolved old address %s => %p\n",
+ elf->name, f->name, f->old_addr);
+ }
+ }
+
+ sec = livepatch_elf_sec_by_name(elf, ELF_BUILD_ID_NOTE);
+ if ( sec )
+ {
+ const struct payload *data;
+
+ n = sec->load_addr;
+
+ if ( sec->sec->sh_size <= sizeof(*n) )
+ return -EINVAL;
+
+ if ( xen_build_id_check(n, sec->sec->sh_size,
+ &payload->id.p, &payload->id.len) )
+ return -EINVAL;
+
+ if ( !payload->id.len || !payload->id.p )
+ return -EINVAL;
+
+ /* Make sure it is not a duplicate. */
+ list_for_each_entry ( data, &payload_list, list )
+ {
+ /* No way _this_ payload is on the list. */
+ ASSERT(data != payload);
+ if ( data->id.len == payload->id.len &&
+ !memcmp(data->id.p, payload->id.p, data->id.len) )
+ {
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Already loaded as %s!\n",
+ elf->name, data->name);
+ return -EEXIST;
+ }
+ }
+ }
+
+ sec = livepatch_elf_sec_by_name(elf, ELF_LIVEPATCH_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->dep.p, &payload->dep.len) )
+ return -EINVAL;
+
+ if ( !payload->dep.len || !payload->dep.p )
+ return -EINVAL;
+ }
+
+ /* Setup the virtual region with proper data. */
+ region = &payload->region;
+
+ region->symbols_lookup = livepatch_symbols_lookup;
+ region->start = payload->text_addr;
+ region->end = payload->text_addr + payload->text_size;
+
+ /* Optional sections. */
+ for ( i = 0; i < BUGFRAME_NR; i++ )
+ {
+ char str[14];
+
+ snprintf(str, sizeof(str), ".bug_frames.%u", i);
+ sec = livepatch_elf_sec_by_name(elf, str);
+ if ( !sec )
+ continue;
+
+ if ( sec->sec->sh_size % sizeof(*region->frame[i].bugs) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Wrong size of .bug_frames.%u!\n",
+ elf->name, i);
+ return -EINVAL;
+ }
+
+ region->frame[i].bugs = sec->load_addr;
+ region->frame[i].n_bugs = sec->sec->sh_size /
+ sizeof(*region->frame[i].bugs);
+ }
+
+#ifndef CONFIG_ARM
+ sec = livepatch_elf_sec_by_name(elf, ".altinstructions");
+ if ( sec )
+ {
+ struct alt_instr *a, *start, *end;
+
+ if ( sec->sec->sh_size % sizeof(*a) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Size of .alt_instr is not multiple of %zu!\n",
+ elf->name, sizeof(*a));
+ return -EINVAL;
+ }
+
+ start = sec->load_addr;
+ end = sec->load_addr + sec->sec->sh_size;
+
+ for ( a = start; a < end; a++ )
+ {
+ const void *instr = &a->instr_offset + a->instr_offset;
+ const void *replacement = &a->repl_offset + a->repl_offset;
+
+ if ( (instr < region->start && instr >= region->end) ||
+ (replacement < region->start && replacement >= region->end) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s Alt patching outside payload: %p!\n",
+ elf->name, instr);
+ return -EINVAL;
+ }
+ }
+ apply_alternatives_nocheck(start, end);
+ }
+
+ sec = livepatch_elf_sec_by_name(elf, ".ex_table");
+ if ( sec )
+ {
+ struct exception_table_entry *s, *e;
+
+ if ( !sec->sec->sh_size ||
+ (sec->sec->sh_size % sizeof(*region->ex)) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Wrong size of .ex_table (exp:%lu vs %lu)!\n",
+ elf->name, sizeof(*region->ex),
+ sec->sec->sh_size);
+ return -EINVAL;
+ }
+
+ s = sec->load_addr;
+ e = sec->load_addr + sec->sec->sh_size;
+
+ sort_exception_table(s ,e);
+
+ region->ex = s;
+ region->ex_end = e;
+ }
+#endif
+
+ return 0;
+}
+
+static bool_t is_payload_symbol(const struct livepatch_elf *elf,
+ const struct livepatch_elf_sym *sym)
+{
+ if ( sym->sym->st_shndx == SHN_UNDEF ||
+ sym->sym->st_shndx >= elf->hdr->e_shnum )
+ return 0;
+
+ /*
+ * The payload is not a final image as we dynmically link against it.
+ * As such the linker has left symbols we don't care about and which
+ * binutils would have removed had it be a final image. Hence we:
+ * - For SHF_ALLOC - ignore symbols referring to sections that are not
+ * loaded.
+ */
+ if ( !(elf->sec[sym->sym->st_shndx].sec->sh_flags & SHF_ALLOC) )
+ return 0;
+
+ /* - And ignore empty symbols (\0). */
+ if ( *sym->name == '\0' )
+ return 0;
+
+ /*
+ * - For SHF_MERGE - ignore local symbols referring to mergeable sections.
+ * (ld squashes them all in one section and discards the symbols) when
+ * those symbols start with '.L' (like .LCx). Those are intermediate
+ * artifacts of assembly.
+ *
+ * See elf_link_input_bfd and _bfd_elf_is_local_label_name in binutils.
+ */
+ if ( (elf->sec[sym->sym->st_shndx].sec->sh_flags & SHF_MERGE) &&
+ !strncmp(sym->name, ".L", 2) )
+ return 0;
+
+ return 1;
+}
+
+static int build_symbol_table(struct payload *payload,
+ const struct livepatch_elf *elf)
+{
+ unsigned int i, j, nsyms = 0;
+ size_t strtab_len = 0;
+ struct livepatch_symbol *symtab;
+ char *strtab;
+
+ ASSERT(payload->nfuncs);
+
+ /* Recall that section @0 is always NULL. */
+ for ( i = 1; i < elf->nsym; i++ )
+ {
+ if ( is_payload_symbol(elf, elf->sym + i) )
+ {
+ nsyms++;
+ strtab_len += strlen(elf->sym[i].name) + 1;
+ }
+ }
+
+ symtab = xmalloc_array(struct livepatch_symbol, nsyms);
+ strtab = xmalloc_array(char, strtab_len);
+
+ if ( !strtab || !symtab )
+ {
+ xfree(strtab);
+ xfree(symtab);
+ return -ENOMEM;
+ }
+
+ nsyms = 0;
+ strtab_len = 0;
+ for ( i = 1; i < elf->nsym; i++ )
+ {
+ if ( is_payload_symbol(elf, elf->sym + i) )
+ {
+ symtab[nsyms].name = strtab + strtab_len;
+ symtab[nsyms].size = elf->sym[i].sym->st_size;
+ symtab[nsyms].value = elf->sym[i].sym->st_value;
+ symtab[nsyms].new_symbol = 0; /* May be overwritten below. */
+ strtab_len += strlcpy(strtab + strtab_len, elf->sym[i].name,
+ KSYM_NAME_LEN) + 1;
+ nsyms++;
+ }
+ }
+
+ for ( i = 0; i < nsyms; i++ )
+ {
+ bool_t found = 0;
+
+ for ( j = 0; j < payload->nfuncs; j++ )
+ {
+ if ( symtab[i].value == (unsigned long)payload->funcs[j].new_addr )
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ if ( symbols_lookup_by_name(symtab[i].name) ||
+ livepatch_symbols_lookup_by_name(symtab[i].name) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: duplicate new symbol: %s\n",
+ elf->name, symtab[i].name);
+ xfree(symtab);
+ xfree(strtab);
+ return -EEXIST;
+ }
+ symtab[i].new_symbol = 1;
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: new symbol %s\n",
+ elf->name, symtab[i].name);
+ }
+ else
+ {
+ /* new_symbol is not set. */
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: overriding symbol %s\n",
+ elf->name, symtab[i].name);
+ }
+ }
+
+ payload->symtab = symtab;
+ payload->strtab = strtab;
+ payload->nsyms = nsyms;
+
+ return 0;
+}
+
+static void free_payload(struct payload *data)
+{
+ ASSERT(spin_is_locked(&payload_lock));
+ list_del(&data->list);
+ payload_cnt--;
+ payload_version++;
+ free_payload_data(data);
+ xfree((void *)data->symtab);
+ xfree((void *)data->strtab);
+ xfree(data);
+}
+
+static int load_payload_data(struct payload *payload, void *raw, size_t len)
+{
+ struct livepatch_elf elf = { .name = payload->name, .len = len };
+ int rc = 0;
+
+ rc = livepatch_elf_load(&elf, raw);
+ if ( rc )
+ goto out;
+
+ rc = move_payload(payload, &elf);
+ if ( rc )
+ goto out;
+
+ rc = livepatch_elf_resolve_symbols(&elf);
+ if ( rc )
+ goto out;
+
+ rc = livepatch_elf_perform_relocs(&elf);
+ if ( rc )
+ goto out;
+
+ rc = check_special_sections(&elf);
+ if ( rc )
+ goto out;
+
+ rc = prepare_payload(payload, &elf);
+ if ( rc )
+ goto out;
+
+ rc = build_symbol_table(payload, &elf);
+ if ( rc )
+ goto out;
+
+ rc = secure_payload(payload, &elf);
+
+ out:
+ if ( rc )
+ free_payload_data(payload);
+
+ /* Free our temporary data structure. */
+ livepatch_elf_free(&elf);
+
+ return rc;
+}
+
+static int livepatch_upload(xen_sysctl_livepatch_upload_t *upload)
+{
+ struct payload *data, *found;
+ char n[XEN_LIVEPATCH_NAME_SIZE];
+ void *raw_data;
+ int rc;
+
+ rc = verify_payload(upload, n);
+ if ( rc )
+ return rc;
+
+ data = xzalloc(struct payload);
+ raw_data = vmalloc(upload->size);
+
+ spin_lock(&payload_lock);
+
+ found = find_payload(n);
+ if ( IS_ERR(found) )
+ rc = PTR_ERR(found);
+ else if ( found )
+ rc = -EEXIST;
+ else if ( !data || !raw_data )
+ rc = -ENOMEM;
+ else if ( __copy_from_guest(raw_data, upload->payload, upload->size) )
+ rc = -EFAULT;
+ else
+ {
+ memcpy(data->name, n, strlen(n));
+
+ rc = load_payload_data(data, raw_data, upload->size);
+ if ( rc )
+ goto out;
+
+ data->state = LIVEPATCH_STATE_CHECKED;
+ INIT_LIST_HEAD(&data->list);
+ INIT_LIST_HEAD(&data->applied_list);
+
+ list_add_tail(&data->list, &payload_list);
+ payload_cnt++;
+ payload_version++;
+ }
+
+ out:
+ spin_unlock(&payload_lock);
+
+ vfree(raw_data);
+
+ if ( rc && data )
+ {
+ xfree((void *)data->symtab);
+ xfree((void *)data->strtab);
+ xfree(data);
+ }
+
+ return rc;
+}
+
+static int livepatch_get(xen_sysctl_livepatch_get_t *get)
+{
+ struct payload *data;
+ int rc;
+ char n[XEN_LIVEPATCH_NAME_SIZE];
+
+ rc = get_name(&get->name, n);
+ if ( rc )
+ return rc;
+
+ spin_lock(&payload_lock);
+
+ data = find_payload(n);
+ if ( IS_ERR_OR_NULL(data) )
+ {
+ spin_unlock(&payload_lock);
+
+ if ( !data )
+ return -ENOENT;
+
+ return PTR_ERR(data);
+ }
+
+ get->status.state = data->state;
+ get->status.rc = data->rc;
+
+ spin_unlock(&payload_lock);
+
+ return 0;
+}
+
+static int livepatch_list(xen_sysctl_livepatch_list_t *list)
+{
+ xen_livepatch_status_t status;
+ struct payload *data;
+ unsigned int idx = 0, i = 0;
+ int rc = 0;
+
+ if ( list->nr > 1024 )
+ return -E2BIG;
+
+ if ( list->pad )
+ return -EINVAL;
+
+ if ( list->nr &&
+ (!guest_handle_okay(list->status, list->nr) ||
+ !guest_handle_okay(list->name, XEN_LIVEPATCH_NAME_SIZE * list->nr) ||
+ !guest_handle_okay(list->len, list->nr)) )
+ return -EINVAL;
+
+ spin_lock(&payload_lock);
+ if ( list->idx >= payload_cnt && payload_cnt )
+ {
+ spin_unlock(&payload_lock);
+ return -EINVAL;
+ }
+
+ if ( list->nr )
+ {
+ list_for_each_entry( data, &payload_list, list )
+ {
+ uint32_t len;
+
+ if ( list->idx > i++ )
+ continue;
+
+ status.state = data->state;
+ status.rc = data->rc;
+ len = strlen(data->name) + 1;
+
+ /* 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) )
+ {
+ rc = -EFAULT;
+ break;
+ }
+
+ idx++;
+
+ if ( (idx >= list->nr) || hypercall_preempt_check() )
+ break;
+ }
+ }
+ list->nr = payload_cnt - i; /* Remaining amount. */
+ list->version = payload_version;
+ spin_unlock(&payload_lock);
+
+ /* And how many we have processed. */
+ return rc ? : idx;
+}
+
+/*
+ * The following functions get the CPUs into an appropriate state and
+ * apply (or revert) each of the payload's functions. This is needed
+ * for XEN_SYSCTL_LIVEPATCH_ACTION operation (see livepatch_action).
+ */
+
+static int apply_payload(struct payload *data)
+{
+ unsigned int i;
+
+ printk(XENLOG_INFO LIVEPATCH "%s: Applying %u functions\n",
+ data->name, data->nfuncs);
+
+ arch_livepatch_quiesce();
+
+ for ( i = 0; i < data->nfuncs; i++ )
+ arch_livepatch_apply_jmp(&data->funcs[i]);
+
+ arch_livepatch_revive();
+
+ /*
+ * We need RCU variant (which has barriers) in case we crash here.
+ * The applied_list is iterated by the trap code.
+ */
+ list_add_tail_rcu(&data->applied_list, &applied_list);
+ register_virtual_region(&data->region);
+
+ return 0;
+}
+
+static int revert_payload(struct payload *data)
+{
+ unsigned int i;
+
+ printk(XENLOG_INFO LIVEPATCH "%s: Reverting\n", data->name);
+
+ arch_livepatch_quiesce();
+
+ for ( i = 0; i < data->nfuncs; i++ )
+ arch_livepatch_revert_jmp(&data->funcs[i]);
+
+ arch_livepatch_revive();
+
+ /*
+ * We need RCU variant (which has barriers) in case we crash here.
+ * The applied_list is iterated by the trap code.
+ */
+ list_del_rcu(&data->applied_list);
+ unregister_virtual_region(&data->region);
+
+ return 0;
+}
+
+/*
+ * This function is executed having all other CPUs with no deep stack (we may
+ * have cpu_idle on it) and IRQs disabled.
+ */
+static void livepatch_do_action(void)
+{
+ int rc;
+ struct payload *data, *other, *tmp;
+
+ data = livepatch_work.data;
+ /*
+ * This function and the transition from asm to C code should be the only
+ * one on any stack. No need to lock the payload list or applied list.
+ */
+ switch ( livepatch_work.cmd )
+ {
+ case LIVEPATCH_ACTION_APPLY:
+ rc = apply_payload(data);
+ if ( rc == 0 )
+ data->state = LIVEPATCH_STATE_APPLIED;
+ break;
+
+ case LIVEPATCH_ACTION_REVERT:
+ rc = revert_payload(data);
+ if ( rc == 0 )
+ data->state = LIVEPATCH_STATE_CHECKED;
+ break;
+
+ case LIVEPATCH_ACTION_REPLACE:
+ rc = 0;
+ /*
+ * N.B: Use 'applied_list' member, not 'list'. We also abuse the
+ * the 'normal' list iterator as the list is an RCU one.
+ */
+ list_for_each_entry_safe_reverse ( other, tmp, &applied_list, applied_list )
+ {
+ other->rc = revert_payload(other);
+ if ( other->rc == 0 )
+ other->state = LIVEPATCH_STATE_CHECKED;
+ else
+ {
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ if ( rc == 0 )
+ {
+ rc = apply_payload(data);
+ if ( rc == 0 )
+ data->state = LIVEPATCH_STATE_APPLIED;
+ }
+ break;
+
+ default:
+ rc = -EINVAL; /* Make GCC5 happy. */
+ ASSERT_UNREACHABLE();
+ break;
+ }
+
+ /* We must set rc as livepatch_action sets it to -EAGAIN when kicking of. */
+ data->rc = rc;
+}
+
+static bool_t is_work_scheduled(const struct payload *data)
+{
+ ASSERT(spin_is_locked(&payload_lock));
+
+ return livepatch_work.do_work && livepatch_work.data == data;
+}
+
+static int schedule_work(struct payload *data, uint32_t cmd, uint32_t timeout)
+{
+ ASSERT(spin_is_locked(&payload_lock));
+
+ /* Fail if an operation is already scheduled. */
+ if ( livepatch_work.do_work )
+ return -EBUSY;
+
+ if ( !get_cpu_maps() )
+ {
+ printk(XENLOG_ERR LIVEPATCH "%s: unable to get cpu_maps lock!\n",
+ data->name);
+ return -EBUSY;
+ }
+
+ livepatch_work.cmd = cmd;
+ livepatch_work.data = data;
+ livepatch_work.timeout = timeout ?: MILLISECS(30);
+
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: timeout is %"PRI_stime"ms\n",
+ data->name, livepatch_work.timeout / MILLISECS(1));
+
+ atomic_set(&livepatch_work.semaphore, -1);
+
+ livepatch_work.ready = 0;
+
+ smp_wmb();
+
+ livepatch_work.do_work = 1;
+ this_cpu(work_to_do) = 1;
+
+ put_cpu_maps();
+
+ return 0;
+}
+
+static void reschedule_fn(void *unused)
+{
+ this_cpu(work_to_do) = 1;
+ raise_softirq(SCHEDULE_SOFTIRQ);
+}
+
+static int livepatch_spin(atomic_t *counter, s_time_t timeout,
+ unsigned int cpus, const char *s)
+{
+ int rc = 0;
+
+ while ( atomic_read(counter) != cpus && NOW() < timeout )
+ cpu_relax();
+
+ /* Log & abort. */
+ if ( atomic_read(counter) != cpus )
+ {
+ printk(XENLOG_ERR LIVEPATCH "%s: Timed out on semaphore in %s quiesce phase %u/%u\n",
+ livepatch_work.data->name, s, atomic_read(counter), cpus);
+ rc = -EBUSY;
+ livepatch_work.data->rc = rc;
+ smp_wmb();
+ livepatch_work.do_work = 0;
+ }
+
+ return rc;
+}
+
+/*
+ * The main function which manages the work of quiescing the system and
+ * patching code.
+ */
+void check_for_livepatch_work(void)
+{
+#define ACTION(x) [LIVEPATCH_ACTION_##x] = #x
+ static const char *const names[] = {
+ ACTION(APPLY),
+ ACTION(REVERT),
+ ACTION(REPLACE),
+ };
+#undef ACTION
+ unsigned int cpu = smp_processor_id();
+ s_time_t timeout;
+ unsigned long flags;
+
+ /* Fast path: no work to do. */
+ if ( !per_cpu(work_to_do, cpu ) )
+ return;
+
+ smp_rmb();
+ /* In case we aborted, other CPUs can skip right away. */
+ if ( !livepatch_work.do_work )
+ {
+ per_cpu(work_to_do, cpu) = 0;
+ return;
+ }
+
+ ASSERT(local_irq_is_enabled());
+
+ /* Set at -1, so will go up to num_online_cpus - 1. */
+ if ( atomic_inc_and_test(&livepatch_work.semaphore) )
+ {
+ struct payload *p;
+ unsigned int cpus;
+
+ p = livepatch_work.data;
+ if ( !get_cpu_maps() )
+ {
+ printk(XENLOG_ERR LIVEPATCH "%s: CPU%u - unable to get cpu_maps lock!\n",
+ p->name, cpu);
+ per_cpu(work_to_do, cpu) = 0;
+ livepatch_work.data->rc = -EBUSY;
+ smp_wmb();
+ livepatch_work.do_work = 0;
+ /*
+ * Do NOT decrement livepatch_work.semaphore down - as that may cause
+ * the other CPU (which may be at this point ready to increment it)
+ * to assume the role of master and then needlessly time out
+ * out (as do_work is zero).
+ */
+ return;
+ }
+ /* "Mask" NMIs. */
+ arch_livepatch_mask();
+
+ barrier(); /* MUST do it after get_cpu_maps. */
+ cpus = num_online_cpus() - 1;
+
+ if ( cpus )
+ {
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: CPU%u - IPIing the other %u CPUs\n",
+ p->name, cpu, cpus);
+ smp_call_function(reschedule_fn, NULL, 0);
+ }
+
+ timeout = livepatch_work.timeout + NOW();
+ if ( livepatch_spin(&livepatch_work.semaphore, timeout, cpus, "CPU") )
+ goto abort;
+
+ /* All CPUs are waiting, now signal to disable IRQs. */
+ atomic_set(&livepatch_work.semaphore, 0);
+ /*
+ * MUST have a barrier after semaphore so that the other CPUs don't
+ * leak out of the 'Wait for all CPUs to rendezvous' loop and increment
+ * 'semaphore' before we set it to zero.
+ */
+ smp_wmb();
+ livepatch_work.ready = 1;
+
+ if ( !livepatch_spin(&livepatch_work.semaphore, timeout, cpus, "IRQ") )
+ {
+ local_irq_save(flags);
+ /* Do the patching. */
+ livepatch_do_action();
+ /* Serialize and flush out the CPU via CPUID instruction (on x86). */
+ arch_livepatch_post_action();
+ local_irq_restore(flags);
+ }
+
+ abort:
+ arch_livepatch_unmask();
+
+ per_cpu(work_to_do, cpu) = 0;
+ livepatch_work.do_work = 0;
+
+ /* put_cpu_maps has an barrier(). */
+ put_cpu_maps();
+
+ printk(XENLOG_INFO LIVEPATCH "%s finished %s with rc=%d\n",
+ p->name, names[livepatch_work.cmd], p->rc);
+ }
+ else
+ {
+ /* Wait for all CPUs to rendezvous. */
+ while ( livepatch_work.do_work && !livepatch_work.ready )
+ cpu_relax();
+
+ /* Disable IRQs and signal. */
+ local_irq_save(flags);
+ /*
+ * We re-use the sempahore, so MUST have it reset by master before
+ * we exit the loop above.
+ */
+ atomic_inc(&livepatch_work.semaphore);
+
+ /* Wait for patching to complete. */
+ while ( livepatch_work.do_work )
+ cpu_relax();
+
+ /* To flush out pipeline. */
+ arch_livepatch_post_action();
+ local_irq_restore(flags);
+
+ per_cpu(work_to_do, cpu) = 0;
+ }
+}
+
+/*
+ * Only allow dependent payload is applied on top of the correct
+ * build-id.
+ *
+ * This enforces an stacking order - the first payload MUST be against the
+ * hypervisor. The second against the first payload, and so on.
+ *
+ * Unless the 'internal' parameter is used - in which case we only
+ * check against the hypervisor.
+ */
+static int build_id_dep(struct payload *payload, bool_t internal)
+{
+ const void *id = NULL;
+ unsigned int len = 0;
+ int rc;
+ const char *name = "hypervisor";
+
+ ASSERT(payload->dep.len && payload->dep.p);
+
+ /* First time user is against hypervisor. */
+ if ( internal )
+ {
+ rc = xen_build_id(&id, &len);
+ if ( rc )
+ return rc;
+ }
+ else
+ {
+ /* We should be against the last applied one. */
+ const struct payload *data;
+
+ data = list_last_entry(&applied_list, struct payload, applied_list);
+
+ id = data->id.p;
+ len = data->id.len;
+ name = data->name;
+ }
+
+ if ( payload->dep.len != len ||
+ memcmp(id, payload->dep.p, len) )
+ {
+ dprintk(XENLOG_ERR, "%s%s: check against %s build-id failed!\n",
+ LIVEPATCH, payload->name, name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int livepatch_action(xen_sysctl_livepatch_action_t *action)
+{
+ struct payload *data;
+ char n[XEN_LIVEPATCH_NAME_SIZE];
+ int rc;
+
+ rc = get_name(&action->name, n);
+ if ( rc )
+ return rc;
+
+ spin_lock(&payload_lock);
+
+ data = find_payload(n);
+ if ( IS_ERR_OR_NULL(data) )
+ {
+ spin_unlock(&payload_lock);
+
+ if ( !data )
+ return -ENOENT;
+
+ return PTR_ERR(data);
+ }
+
+ if ( is_work_scheduled(data) )
+ {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ switch ( action->cmd )
+ {
+ case LIVEPATCH_ACTION_UNLOAD:
+ if ( data->state == LIVEPATCH_STATE_CHECKED )
+ {
+ free_payload(data);
+ /* No touching 'data' from here on! */
+ data = NULL;
+ }
+ else
+ rc = -EINVAL;
+ break;
+
+ case LIVEPATCH_ACTION_REVERT:
+ if ( data->state == LIVEPATCH_STATE_APPLIED )
+ {
+ const struct payload *p;
+
+ p = list_last_entry(&applied_list, struct payload, applied_list);
+ ASSERT(p);
+ /* We should be the last applied one. */
+ if ( p != data )
+ {
+ dprintk(XENLOG_ERR, "%s%s: can't unload. Top is %s!\n",
+ LIVEPATCH, data->name, p->name);
+ rc = -EBUSY;
+ break;
+ }
+ data->rc = -EAGAIN;
+ rc = schedule_work(data, action->cmd, action->timeout);
+ }
+ break;
+
+ case LIVEPATCH_ACTION_APPLY:
+ if ( data->state == LIVEPATCH_STATE_CHECKED )
+ {
+ rc = build_id_dep(data, !!list_empty(&applied_list));
+ if ( rc )
+ break;
+ data->rc = -EAGAIN;
+ rc = schedule_work(data, action->cmd, action->timeout);
+ }
+ break;
+
+ case LIVEPATCH_ACTION_REPLACE:
+ if ( data->state == LIVEPATCH_STATE_CHECKED )
+ {
+ rc = build_id_dep(data, 1 /* against hypervisor. */);
+ if ( rc )
+ break;
+ data->rc = -EAGAIN;
+ rc = schedule_work(data, action->cmd, action->timeout);
+ }
+ break;
+
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+ }
+
+ out:
+ spin_unlock(&payload_lock);
+
+ return rc;
+}
+
+int livepatch_op(xen_sysctl_livepatch_op_t *livepatch)
+{
+ int rc;
+
+ if ( livepatch->pad )
+ return -EINVAL;
+
+ switch ( livepatch->cmd )
+ {
+ case XEN_SYSCTL_LIVEPATCH_UPLOAD:
+ rc = livepatch_upload(&livepatch->u.upload);
+ break;
+
+ case XEN_SYSCTL_LIVEPATCH_GET:
+ rc = livepatch_get(&livepatch->u.get);
+ break;
+
+ case XEN_SYSCTL_LIVEPATCH_LIST:
+ rc = livepatch_list(&livepatch->u.list);
+ break;
+
+ case XEN_SYSCTL_LIVEPATCH_ACTION:
+ rc = livepatch_action(&livepatch->u.action);
+ break;
+
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+ }
+
+ return rc;
+}
+
+static const char *state2str(unsigned int state)
+{
+#define STATE(x) [LIVEPATCH_STATE_##x] = #x
+ static const char *const names[] = {
+ STATE(CHECKED),
+ STATE(APPLIED),
+ };
+#undef STATE
+
+ if ( state >= ARRAY_SIZE(names) || !names[state] )
+ return "unknown";
+
+ return names[state];
+}
+
+static void livepatch_printall(unsigned char key)
+{
+ struct payload *data;
+ const void *binary_id = NULL;
+ unsigned int len = 0;
+ unsigned int i;
+
+ printk("'%c' pressed - Dumping all livepatch patches\n", key);
+
+ if ( !xen_build_id(&binary_id, &len) )
+ printk("build-id: %*phN\n", len, binary_id);
+
+ if ( !spin_trylock(&payload_lock) )
+ {
+ printk("Lock held. Try again.\n");
+ return;
+ }
+
+ list_for_each_entry ( data, &payload_list, list )
+ {
+ printk(" name=%s state=%s(%d) %p (.data=%p, .rodata=%p) using %u pages.\n",
+ data->name, state2str(data->state), data->state, data->text_addr,
+ data->rw_addr, data->ro_addr, data->pages);
+
+ for ( i = 0; i < data->nfuncs; i++ )
+ {
+ struct livepatch_func *f = &(data->funcs[i]);
+ printk(" %s patch %p(%u) with %p (%u)\n",
+ f->name, f->old_addr, f->old_size, f->new_addr, f->new_size);
+
+ if ( i && !(i % 64) )
+ {
+ spin_unlock(&payload_lock);
+ process_pending_softirqs();
+ if ( spin_trylock(&payload_lock) )
+ {
+ printk("Couldn't reacquire lock. Try again.\n");
+ return;
+ }
+ }
+ }
+ if ( data->id.len )
+ printk("build-id=%*phN\n", data->id.len, data->id.p);
+
+ if ( data->dep.len )
+ printk("depend-on=%*phN\n", data->dep.len, data->dep.p);
+ }
+
+ spin_unlock(&payload_lock);
+}
+
+static int __init livepatch_init(void)
+{
+ const void *binary_id;
+ unsigned int len;
+
+ if ( !xen_build_id(&binary_id, &len) )
+ printk(XENLOG_INFO LIVEPATCH ": build-id: %*phN\n", len, binary_id);
+
+ register_keyhandler('x', livepatch_printall, "print livepatch info", 1);
+
+ arch_livepatch_init();
+ return 0;
+}
+__initcall(livepatch_init);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 Citrix Systems R&D Ltd.
+ */
+
+#include <xen/errno.h>
+#include <xen/lib.h>
+#include <xen/symbols.h>
+#include <xen/livepatch_elf.h>
+#include <xen/livepatch.h>
+
+const struct livepatch_elf_sec *
+livepatch_elf_sec_by_name(const struct livepatch_elf *elf,
+ const char *name)
+{
+ unsigned int i;
+
+ for ( i = 1; i < elf->hdr->e_shnum; i++ )
+ {
+ if ( !strcmp(name, elf->sec[i].name) )
+ return &elf->sec[i];
+ }
+
+ return NULL;
+}
+
+static int elf_verify_strtab(const struct livepatch_elf_sec *sec)
+{
+ const Elf_Shdr *s;
+ const char *contents;
+
+ s = sec->sec;
+
+ if ( s->sh_type != SHT_STRTAB )
+ return -EINVAL;
+
+ if ( !s->sh_size )
+ return -EINVAL;
+
+ contents = sec->data;
+
+ if ( contents[0] || contents[s->sh_size - 1] )
+ return -EINVAL;
+
+ return 0;
+}
+
+static int elf_resolve_sections(struct livepatch_elf *elf, const void *data)
+{
+ struct livepatch_elf_sec *sec;
+ unsigned int i;
+ Elf_Off delta;
+ int rc;
+
+ /* livepatch_elf_load sanity checked e_shnum. */
+ sec = xmalloc_array(struct livepatch_elf_sec, elf->hdr->e_shnum);
+ if ( !sec )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH"%s: Could not allocate memory for section table!\n",
+ elf->name);
+ return -ENOMEM;
+ }
+
+ elf->sec = sec;
+
+ /* e_shoff and e_shnum overflow checks are done in livepatch_header_check. */
+ delta = elf->hdr->e_shoff + elf->hdr->e_shnum * elf->hdr->e_shentsize;
+ ASSERT(delta <= elf->len);
+
+ for ( i = 1; i < elf->hdr->e_shnum; i++ )
+ {
+ delta = elf->hdr->e_shoff + i * elf->hdr->e_shentsize;
+
+ sec[i].sec = data + delta;
+
+ delta = sec[i].sec->sh_offset;
+ /*
+ * N.B. elf_resolve_section_names, elf_get_sym skip this check as
+ * we do it here.
+ */
+ if ( delta < sizeof(Elf_Ehdr) ||
+ (sec[i].sec->sh_type != SHT_NOBITS && /* Skip SHT_NOBITS */
+ (delta > elf->len || (delta + sec[i].sec->sh_size > elf->len))) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section [%u] data %s of payload!\n",
+ elf->name, i,
+ delta < sizeof(Elf_Ehdr) ? "at ELF header" : "is past end");
+ return -EINVAL;
+ }
+
+ sec[i].data = data + delta;
+ /* Name is populated in elf_resolve_section_names. */
+ sec[i].name = NULL;
+
+ if ( sec[i].sec->sh_type == SHT_SYMTAB )
+ {
+ if ( elf->symtab )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Unsupported multiple symbol tables!\n",
+ elf->name);
+ return -EOPNOTSUPP;
+ }
+
+ elf->symtab = &sec[i];
+
+ elf->symtab_idx = i;
+ /*
+ * elf->symtab->sec->sh_link would point to the right section
+ * but we hadn't finished parsing all the sections.
+ */
+ if ( elf->symtab->sec->sh_link >= elf->hdr->e_shnum )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH
+ "%s: Symbol table idx (%u) to strtab past end (%u)\n",
+ elf->name, elf->symtab->sec->sh_link,
+ elf->hdr->e_shnum);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if ( !elf->symtab )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: No symbol table found!\n",
+ elf->name);
+ return -EINVAL;
+ }
+
+ if ( !elf->symtab->sec->sh_size ||
+ elf->symtab->sec->sh_entsize < sizeof(Elf_Sym) ||
+ elf->symtab->sec->sh_size % elf->symtab->sec->sh_entsize )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Symbol table header is corrupted!\n",
+ elf->name);
+ return -EINVAL;
+ }
+
+ /*
+ * There can be multiple SHT_STRTAB (.shstrtab, .strtab) so pick the one
+ * associated with the symbol table.
+ */
+ elf->strtab = &sec[elf->symtab->sec->sh_link];
+
+ rc = elf_verify_strtab(elf->strtab);
+ if ( rc )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: String table section is corrupted\n",
+ elf->name);
+ }
+
+ return rc;
+}
+
+static int elf_resolve_section_names(struct livepatch_elf *elf, const void *data)
+{
+ const char *shstrtab;
+ unsigned int i;
+ Elf_Off offset, delta;
+ struct livepatch_elf_sec *sec;
+ int rc;
+
+ /*
+ * The elf->sec[0 -> e_shnum] structures have been verified by
+ * elf_resolve_sections. Find file offset for section string table
+ * (normally called .shstrtab)
+ */
+ sec = &elf->sec[elf->hdr->e_shstrndx];
+
+ rc = elf_verify_strtab(sec);
+ if ( rc )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section string table is corrupted\n",
+ elf->name);
+ return rc;
+ }
+
+ /* Verified in elf_resolve_sections but just in case. */
+ offset = sec->sec->sh_offset;
+ ASSERT(offset < elf->len && (offset + sec->sec->sh_size <= elf->len));
+
+ shstrtab = data + offset;
+
+ for ( i = 1; i < elf->hdr->e_shnum; i++ )
+ {
+ delta = elf->sec[i].sec->sh_name;
+
+ /* Boundary check on offset of name within the .shstrtab. */
+ if ( delta >= sec->sec->sh_size )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section %u name is not within .shstrtab!\n",
+ elf->name, i);
+ return -EINVAL;
+ }
+
+ elf->sec[i].name = shstrtab + delta;
+ }
+
+ return 0;
+}
+
+static int elf_get_sym(struct livepatch_elf *elf, const void *data)
+{
+ const struct livepatch_elf_sec *symtab_sec, *strtab_sec;
+ struct livepatch_elf_sym *sym;
+ unsigned int i, nsym;
+ Elf_Off offset;
+ Elf_Word delta;
+
+ symtab_sec = elf->symtab;
+ strtab_sec = elf->strtab;
+
+ /* Pointers arithmetic to get file offset. */
+ offset = strtab_sec->data - data;
+
+ /* Checked already in elf_resolve_sections, but just in case. */
+ ASSERT(offset == strtab_sec->sec->sh_offset);
+ ASSERT(offset < elf->len && (offset + strtab_sec->sec->sh_size <= elf->len));
+
+ /* symtab_sec->data was computed in elf_resolve_sections. */
+ ASSERT((symtab_sec->sec->sh_offset + data) == symtab_sec->data);
+
+ /* No need to check values as elf_resolve_sections did it. */
+ nsym = symtab_sec->sec->sh_size / symtab_sec->sec->sh_entsize;
+
+ sym = xmalloc_array(struct livepatch_elf_sym, nsym);
+ if ( !sym )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Could not allocate memory for symbols\n",
+ elf->name);
+ return -ENOMEM;
+ }
+
+ /* So we don't leak memory. */
+ elf->sym = sym;
+
+ for ( i = 1; i < nsym; i++ )
+ {
+ const Elf_Sym *s = symtab_sec->data + symtab_sec->sec->sh_entsize * i;
+
+ delta = s->st_name;
+ /* Boundary check within the .strtab. */
+ if ( delta >= strtab_sec->sec->sh_size )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Symbol [%u] name is not within .strtab!\n",
+ elf->name, i);
+ return -EINVAL;
+ }
+
+ sym[i].sym = s;
+ sym[i].name = strtab_sec->data + delta;
+ }
+ elf->nsym = nsym;
+
+ return 0;
+}
+
+int livepatch_elf_resolve_symbols(struct livepatch_elf *elf)
+{
+ unsigned int i;
+ int rc = 0;
+
+ ASSERT(elf->sym);
+
+ for ( i = 1; i < elf->nsym; i++ )
+ {
+ unsigned int idx = elf->sym[i].sym->st_shndx;
+ const Elf_Sym *sym = elf->sym[i].sym;
+ Elf_Addr st_value = sym->st_value;
+
+ switch ( idx )
+ {
+ case SHN_COMMON:
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Unexpected common symbol: %s\n",
+ elf->name, elf->sym[i].name);
+ rc = -EINVAL;
+ break;
+
+ case SHN_UNDEF:
+ st_value = symbols_lookup_by_name(elf->sym[i].name);
+ if ( !st_value )
+ {
+ st_value = livepatch_symbols_lookup_by_name(elf->sym[i].name);
+ if ( !st_value )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Unknown symbol: %s\n",
+ elf->name, elf->sym[i].name);
+ rc = -ENOENT;
+ break;
+ }
+ }
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Undefined symbol resolved: %s => %#"PRIxElfAddr"\n",
+ elf->name, elf->sym[i].name, st_value);
+ break;
+
+ case SHN_ABS:
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Absolute symbol: %s => %#"PRIxElfAddr"\n",
+ elf->name, elf->sym[i].name, sym->st_value);
+ break;
+
+ default:
+ /* SHN_COMMON and SHN_ABS are above. */
+ if ( idx >= SHN_LORESERVE )
+ rc = -EOPNOTSUPP;
+ else if ( idx >= elf->hdr->e_shnum )
+ rc = -EINVAL;
+
+ if ( rc )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Out of bounds symbol section %#x\n",
+ elf->name, idx);
+ break;
+ }
+
+ /* Matches 'move_payload' which ignores such sections. */
+ if ( !(elf->sec[idx].sec->sh_flags & SHF_ALLOC) )
+ break;
+
+ st_value += (unsigned long)elf->sec[idx].load_addr;
+ if ( elf->sym[i].name )
+ dprintk(XENLOG_DEBUG, LIVEPATCH "%s: Symbol resolved: %s => %#"PRIxElfAddr" (%s)\n",
+ elf->name, elf->sym[i].name,
+ st_value, elf->sec[idx].name);
+ }
+
+ if ( rc )
+ break;
+
+ ((Elf_Sym *)sym)->st_value = st_value;
+ }
+
+ return rc;
+}
+
+int livepatch_elf_perform_relocs(struct livepatch_elf *elf)
+{
+ struct livepatch_elf_sec *r, *base;
+ unsigned int i;
+ int rc = 0;
+
+ ASSERT(elf->sym);
+
+ for ( i = 1; i < elf->hdr->e_shnum; i++ )
+ {
+ r = &elf->sec[i];
+
+ if ( (r->sec->sh_type != SHT_RELA) &&
+ (r->sec->sh_type != SHT_REL) )
+ continue;
+
+ /* Is it a valid relocation section? */
+ if ( r->sec->sh_info >= elf->hdr->e_shnum )
+ continue;
+
+ base = &elf->sec[r->sec->sh_info];
+
+ /* Don't relocate non-allocated sections. */
+ if ( !(base->sec->sh_flags & SHF_ALLOC) )
+ continue;
+
+ if ( r->sec->sh_link != elf->symtab_idx )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Relative link of %s is incorrect (%d, expected=%d)\n",
+ elf->name, r->name, r->sec->sh_link, elf->symtab_idx);
+ rc = -EINVAL;
+ break;
+ }
+
+ if ( r->sec->sh_type == SHT_RELA )
+ rc = arch_livepatch_perform_rela(elf, base, r);
+ else /* SHT_REL */
+ rc = arch_livepatch_perform_rel(elf, base, r);
+
+ if ( rc )
+ break;
+ }
+
+ return rc;
+}
+
+static int livepatch_header_check(const struct livepatch_elf *elf)
+{
+ const Elf_Ehdr *hdr = elf->hdr;
+ int rc;
+
+ if ( sizeof(*elf->hdr) > elf->len )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section header is bigger than payload!\n",
+ elf->name);
+ return -EINVAL;
+ }
+
+ if ( !IS_ELF(*hdr) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Not an ELF payload!\n", elf->name);
+ return -EINVAL;
+ }
+
+ /* EI_CLASS, EI_DATA, and e_flags are platform specific. */
+ if ( hdr->e_version != EV_CURRENT ||
+ hdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ hdr->e_ident[EI_ABIVERSION] != 0 ||
+ (hdr->e_ident[EI_OSABI] != ELFOSABI_NONE &&
+ hdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD) ||
+ hdr->e_type != ET_REL ||
+ hdr->e_phnum != 0 )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Invalid ELF payload!\n", elf->name);
+ return -EOPNOTSUPP;
+ }
+
+ rc = arch_livepatch_verify_elf(elf);
+ if ( rc )
+ return rc;
+
+ if ( elf->hdr->e_shstrndx == SHN_UNDEF )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section name idx is undefined!?\n",
+ elf->name);
+ return -EINVAL;
+ }
+
+ /* Arbitrary boundary limit. */
+ if ( elf->hdr->e_shnum >= 1024 )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Too many (%u) sections!\n",
+ elf->name, elf->hdr->e_shnum);
+ return -EOPNOTSUPP;
+ }
+
+ /* Check that section name index is within the sections. */
+ if ( elf->hdr->e_shstrndx >= elf->hdr->e_shnum )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section name idx (%u) is past end of sections (%u)!\n",
+ elf->name, elf->hdr->e_shstrndx, elf->hdr->e_shnum);
+ return -EINVAL;
+ }
+
+ if ( elf->hdr->e_shoff >= elf->len )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Bogus e_shoff!\n", elf->name);
+ return -EINVAL;
+ }
+
+ if ( elf->hdr->e_shentsize < sizeof(Elf_Shdr) )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section header size is %u! Expected %zu!?\n",
+ elf->name, elf->hdr->e_shentsize, sizeof(Elf_Shdr));
+ return -EINVAL;
+ }
+
+ if ( ((elf->len - elf->hdr->e_shoff) / elf->hdr->e_shentsize) <
+ elf->hdr->e_shnum )
+ {
+ dprintk(XENLOG_ERR, LIVEPATCH "%s: Section header size is corrupted!\n",
+ elf->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int livepatch_elf_load(struct livepatch_elf *elf, const void *data)
+{
+ int rc;
+
+ elf->hdr = data;
+
+ rc = livepatch_header_check(elf);
+ if ( rc )
+ return rc;
+
+ rc = elf_resolve_sections(elf, data);
+ if ( rc )
+ return rc;
+
+ rc = elf_resolve_section_names(elf, data);
+ if ( rc )
+ return rc;
+
+ rc = elf_get_sym(elf, data);
+ if ( rc )
+ return rc;
+
+ return 0;
+}
+
+void livepatch_elf_free(struct livepatch_elf *elf)
+{
+ xfree(elf->sec);
+ elf->sec = NULL;
+ xfree(elf->sym);
+ elf->sym = NULL;
+ elf->nsym = 0;
+ elf->name = NULL;
+ elf->len = 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
#include <xsm/xsm.h>
#include <xen/pmstat.h>
#include <xen/gcov.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl)
{
ret = tmem_control(&op->u.tmem_op);
break;
- case XEN_SYSCTL_xsplice_op:
- ret = xsplice_op(&op->u.xsplice);
+ case XEN_SYSCTL_livepatch_op:
+ ret = livepatch_op(&op->u.livepatch);
if ( ret != -ENOSYS && ret != -EOPNOTSUPP )
copyback = 1;
break;
* RCU locking. Additions are done either at startup (when there is only
* one CPU) or when all CPUs are running without IRQs.
*
- * Deletions are bit tricky. We do it when xSplicing (all CPUs running
+ * Deletions are bit tricky. We do it when Live Patch (all CPUs running
* without IRQs) or during bootup (when clearing the init).
*
* Hence we use list_del_rcu (which sports an memory fence) and a spinlock
void unregister_virtual_region(struct virtual_region *r)
{
- /* Expected to be called from xSplice - which has IRQs disabled. */
+ /* Expected to be called from Live Patch - which has IRQs disabled. */
ASSERT(!local_irq_is_enabled());
remove_virtual_region(r);
#include <xen/symbols.h>
#include <xen/lib.h>
#include <xen/sched.h>
-#include <xen/xsplice.h>
+#include <xen/livepatch.h>
#include <asm/div64.h>
#include <asm/page.h>
}
/*
- * namebuf contents and s for core hypervisor are same but for xSplice
+ * namebuf contents and s for core hypervisor are same but for Live Patch
* payloads they differ (namebuf contains the name of the payload).
*/
if ( namebuf != s )
+++ /dev/null
-/*
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
- *
- */
-
-#include <xen/cpu.h>
-#include <xen/elf.h>
-#include <xen/err.h>
-#include <xen/guest_access.h>
-#include <xen/keyhandler.h>
-#include <xen/lib.h>
-#include <xen/list.h>
-#include <xen/mm.h>
-#include <xen/sched.h>
-#include <xen/smp.h>
-#include <xen/softirq.h>
-#include <xen/spinlock.h>
-#include <xen/string.h>
-#include <xen/symbols.h>
-#include <xen/version.h>
-#include <xen/virtual_region.h>
-#include <xen/vmap.h>
-#include <xen/wait.h>
-#include <xen/xsplice_elf.h>
-#include <xen/xsplice.h>
-
-#include <asm/event.h>
-
-/*
- * Protects against payload_list operations and also allows only one
- * caller in schedule_work.
- */
-static DEFINE_SPINLOCK(payload_lock);
-static LIST_HEAD(payload_list);
-
-/*
- * Patches which have been applied. Need RCU in case we crash (and then
- * traps code would iterate via applied_list) when adding entries onthe list.
- */
-static DEFINE_RCU_READ_LOCK(rcu_applied_lock);
-static LIST_HEAD(applied_list);
-
-static unsigned int payload_cnt;
-static unsigned int payload_version = 1;
-
-/* To contain the ELF Note header. */
-struct xsplice_build_id {
- const void *p;
- unsigned int len;
-};
-
-struct payload {
- uint32_t state; /* One of the XSPLICE_STATE_*. */
- int32_t rc; /* 0 or -XEN_EXX. */
- 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 xsplice_patch_func *funcs; /* The array of functions to patch. */
- unsigned int nfuncs; /* Nr of functions to patch. */
- const struct xsplice_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 xsplice_build_id id; /* ELFNOTE_DESC(.note.gnu.build-id) of the payload. */
- struct xsplice_build_id dep; /* ELFNOTE_DESC(.xsplice.depends). */
- char name[XEN_XSPLICE_NAME_SIZE]; /* Name of it. */
-};
-
-/* Defines an outstanding patching action. */
-struct xsplice_work
-{
- atomic_t semaphore; /* Used to rendezvous CPUs in
- check_for_xsplice_work. */
- uint32_t timeout; /* Timeout to do the operation. */
- struct payload *data; /* The payload on which to act. */
- volatile bool_t do_work; /* Signals work to do. */
- volatile bool_t ready; /* Signals all CPUs synchronized. */
- unsigned int cmd; /* Action request: XSPLICE_ACTION_* */
-};
-
-/* There can be only one outstanding patching action. */
-static struct xsplice_work xsplice_work;
-
-/*
- * Indicate whether the CPU needs to consult xsplice_work structure.
- * We want an per-cpu data structure otherwise the check_for_xsplice_work
- * would hammer a global xsplice_work structure on every guest VMEXIT.
- * Having an per-cpu lessens the load.
- */
-static DEFINE_PER_CPU(bool_t, work_to_do);
-
-static int get_name(const xen_xsplice_name_t *name, char *n)
-{
- if ( !name->size || name->size > XEN_XSPLICE_NAME_SIZE )
- return -EINVAL;
-
- if ( name->pad[0] || name->pad[1] || name->pad[2] )
- return -EINVAL;
-
- if ( copy_from_guest(n, name->name, name->size) )
- return -EFAULT;
-
- if ( n[name->size - 1] )
- return -EINVAL;
-
- return 0;
-}
-
-static int verify_payload(const xen_sysctl_xsplice_upload_t *upload, char *n)
-{
- if ( get_name(&upload->name, n) )
- return -EINVAL;
-
- if ( !upload->size )
- return -EINVAL;
-
- if ( upload->size > MB(2) )
- return -EINVAL;
-
- if ( !guest_handle_okay(upload->payload, upload->size) )
- return -EFAULT;
-
- return 0;
-}
-
-bool_t is_patch(const void *ptr)
-{
- const struct payload *data;
- bool_t r = 0;
-
- /*
- * Only RCU locking since this list is only ever changed during apply
- * or revert context. And in case it dies there we need an safe list.
- */
- rcu_read_lock(&rcu_applied_lock);
- list_for_each_entry_rcu ( data, &applied_list, applied_list )
- {
- if ( (ptr >= data->rw_addr &&
- ptr < (data->rw_addr + data->rw_size)) ||
- (ptr >= data->ro_addr &&
- ptr < (data->ro_addr + data->ro_size)) ||
- (ptr >= data->text_addr &&
- ptr < (data->text_addr + data->text_size)) )
- {
- r = 1;
- break;
- }
-
- }
- rcu_read_unlock(&rcu_applied_lock);
-
- return r;
-}
-
-unsigned long xsplice_symbols_lookup_by_name(const char *symname)
-{
- const struct payload *data;
-
- ASSERT(spin_is_locked(&payload_lock));
- list_for_each_entry ( data, &payload_list, list )
- {
- unsigned int i;
-
- for ( i = 0; i < data->nsyms; i++ )
- {
- if ( !data->symtab[i].new_symbol )
- continue;
-
- if ( !strcmp(data->symtab[i].name, symname) )
- return data->symtab[i].value;
- }
- }
-
- return 0;
-}
-
-static const char *xsplice_symbols_lookup(unsigned long addr,
- unsigned long *symbolsize,
- unsigned long *offset,
- char *namebuf)
-{
- const struct payload *data;
- unsigned int i, best;
- const void *va = (const void *)addr;
- const char *n = NULL;
-
- /*
- * Only RCU locking since this list is only ever changed during apply
- * or revert context. And in case it dies there we need an safe list.
- */
- rcu_read_lock(&rcu_applied_lock);
- list_for_each_entry_rcu ( data, &applied_list, applied_list )
- {
- if ( va < data->text_addr ||
- va >= (data->text_addr + data->text_size) )
- continue;
-
- best = UINT_MAX;
-
- for ( i = 0; i < data->nsyms; i++ )
- {
- if ( data->symtab[i].value <= addr &&
- (best == UINT_MAX ||
- data->symtab[best].value < data->symtab[i].value) )
- best = i;
- }
-
- if ( best == UINT_MAX )
- break;
-
- if ( symbolsize )
- *symbolsize = data->symtab[best].size;
- if ( offset )
- *offset = addr - data->symtab[best].value;
- if ( namebuf )
- strlcpy(namebuf, data->name, KSYM_NAME_LEN);
-
- n = data->symtab[best].name;
- break;
- }
- rcu_read_unlock(&rcu_applied_lock);
-
- return n;
-}
-
-static struct payload *find_payload(const char *name)
-{
- struct payload *data, *found = NULL;
-
- ASSERT(spin_is_locked(&payload_lock));
- list_for_each_entry ( data, &payload_list, list )
- {
- if ( !strcmp(data->name, name) )
- {
- found = data;
- break;
- }
- }
-
- return found;
-}
-
-/*
- * Functions related to XEN_SYSCTL_XSPLICE_UPLOAD (see xsplice_upload), and
- * freeing payload (XEN_SYSCTL_XSPLICE_ACTION:XSPLICE_ACTION_UNLOAD).
- */
-
-static void free_payload_data(struct payload *payload)
-{
- /* Set to zero until "move_payload". */
- if ( !payload->pages )
- return;
-
- vfree((void *)payload->text_addr);
-
- payload->pages = 0;
-}
-
-/*
-* calc_section computes the size (taking into account section alignment).
-*
-* Furthermore the offset is set with the offset from the start of the virtual
-* address space for the payload (using passed in size). This is used in
-* move_payload to figure out the destination location (load_addr).
-*/
-static void calc_section(const struct xsplice_elf_sec *sec, size_t *size,
- unsigned int *offset)
-{
- const Elf_Shdr *s = sec->sec;
- size_t align_size;
-
- align_size = ROUNDUP(*size, s->sh_addralign);
- *offset = align_size;
- *size = s->sh_size + align_size;
-}
-
-static int move_payload(struct payload *payload, struct xsplice_elf *elf)
-{
- void *text_buf, *ro_buf, *rw_buf;
- unsigned int i;
- size_t size = 0;
- unsigned int *offset;
- int rc = 0;
-
- offset = xmalloc_array(unsigned int, elf->hdr->e_shnum);
- if ( !offset )
- return -ENOMEM;
-
- /* Compute size of different regions. */
- for ( i = 1; i < elf->hdr->e_shnum; i++ )
- {
- /*
- * Do nothing. These are .rel.text, rel.*, .symtab, .strtab,
- * and .shstrtab. For the non-relocate we allocate and copy these
- * via other means - and the .rel we can ignore as we only use it
- * once during loading.
- */
- if ( !(elf->sec[i].sec->sh_flags & SHF_ALLOC) )
- offset[i] = UINT_MAX;
- else if ( (elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
- !(elf->sec[i].sec->sh_flags & SHF_WRITE) )
- calc_section(&elf->sec[i], &payload->text_size, &offset[i]);
- else if ( !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
- (elf->sec[i].sec->sh_flags & SHF_WRITE) )
- calc_section(&elf->sec[i], &payload->rw_size, &offset[i]);
- else if ( !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
- !(elf->sec[i].sec->sh_flags & SHF_WRITE) )
- calc_section(&elf->sec[i], &payload->ro_size, &offset[i]);
- else
- {
- dprintk(XENLOG_DEBUG, XSPLICE "%s: Not supporting %s section!\n",
- elf->name, elf->sec[i].name);
- rc = -EOPNOTSUPP;
- goto out;
- }
- }
-
- /*
- * Total of all three regions - RX, RW, and RO. We have to have
- * keep them in seperate pages so we PAGE_ALIGN the RX and RW to have
- * them on seperate pages. The last one will by default fall on its
- * own page.
- */
- size = PAGE_ALIGN(payload->text_size) + PAGE_ALIGN(payload->rw_size) +
- payload->ro_size;
-
- size = PFN_UP(size); /* Nr of pages. */
- text_buf = vmalloc_xen(size * PAGE_SIZE);
- if ( !text_buf )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Could not allocate memory for payload!\n",
- elf->name);
- rc = -ENOMEM;
- goto out;
- }
- rw_buf = text_buf + PAGE_ALIGN(payload->text_size);
- ro_buf = rw_buf + PAGE_ALIGN(payload->rw_size);
-
- payload->pages = size;
- payload->text_addr = text_buf;
- payload->rw_addr = rw_buf;
- payload->ro_addr = ro_buf;
-
- for ( i = 1; i < elf->hdr->e_shnum; i++ )
- {
- if ( elf->sec[i].sec->sh_flags & SHF_ALLOC )
- {
- void *buf;
-
- if ( elf->sec[i].sec->sh_flags & SHF_EXECINSTR )
- buf = text_buf;
- else if ( elf->sec[i].sec->sh_flags & SHF_WRITE )
- buf = rw_buf;
- else
- buf = ro_buf;
-
- ASSERT(offset[i] != UINT_MAX);
-
- elf->sec[i].load_addr = buf + offset[i];
-
- /* Don't copy NOBITS - such as BSS. */
- if ( elf->sec[i].sec->sh_type != SHT_NOBITS )
- {
- memcpy(elf->sec[i].load_addr, elf->sec[i].data,
- elf->sec[i].sec->sh_size);
- dprintk(XENLOG_DEBUG, XSPLICE "%s: Loaded %s at %p\n",
- elf->name, elf->sec[i].name, elf->sec[i].load_addr);
- }
- else
- memset(elf->sec[i].load_addr, 0, elf->sec[i].sec->sh_size);
- }
- }
-
- out:
- xfree(offset);
-
- return rc;
-}
-
-static int secure_payload(struct payload *payload, struct xsplice_elf *elf)
-{
- int rc;
- unsigned int text_pages, rw_pages, ro_pages;
-
- text_pages = PFN_UP(payload->text_size);
- ASSERT(text_pages);
-
- rc = arch_xsplice_secure(payload->text_addr, text_pages, XSPLICE_VA_RX);
- if ( rc )
- return rc;
-
- rw_pages = PFN_UP(payload->rw_size);
- if ( rw_pages )
- {
- rc = arch_xsplice_secure(payload->rw_addr, rw_pages, XSPLICE_VA_RW);
- if ( rc )
- return rc;
- }
-
- ro_pages = PFN_UP(payload->ro_size);
- if ( ro_pages )
- rc = arch_xsplice_secure(payload->ro_addr, ro_pages, XSPLICE_VA_RO);
-
- ASSERT(ro_pages + rw_pages + text_pages == payload->pages);
-
- return rc;
-}
-
-static int check_special_sections(const struct xsplice_elf *elf)
-{
- unsigned int i;
- static const char *const names[] = { ELF_XSPLICE_FUNC,
- ELF_XSPLICE_DEPENDS,
- ELF_BUILD_ID_NOTE};
- DECLARE_BITMAP(found, ARRAY_SIZE(names)) = { 0 };
-
- for ( i = 0; i < ARRAY_SIZE(names); i++ )
- {
- const struct xsplice_elf_sec *sec;
-
- sec = xsplice_elf_sec_by_name(elf, names[i]);
- if ( !sec )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: %s is missing!\n",
- elf->name, names[i]);
- return -EINVAL;
- }
-
- if ( !sec->sec->sh_size )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: %s is empty!\n",
- elf->name, names[i]);
- return -EINVAL;
- }
-
- if ( test_and_set_bit(i, found) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: %s was seen more than once!\n",
- elf->name, names[i]);
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static int prepare_payload(struct payload *payload,
- struct xsplice_elf *elf)
-{
- const struct xsplice_elf_sec *sec;
- unsigned int i;
- struct xsplice_patch_func *f;
- struct virtual_region *region;
- const Elf_Note *n;
-
- sec = xsplice_elf_sec_by_name(elf, ELF_XSPLICE_FUNC);
- ASSERT(sec);
- if ( sec->sec->sh_size % sizeof(*payload->funcs) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Wrong size of "ELF_XSPLICE_FUNC"!\n",
- elf->name);
- 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 != XSPLICE_PAYLOAD_VERSION )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Wrong version (%u). Expected %d!\n",
- elf->name, f->version, XSPLICE_PAYLOAD_VERSION);
- return -EOPNOTSUPP;
- }
-
- if ( !f->new_addr || !f->new_size )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Address or size fields are zero!\n",
- elf->name);
- return -EINVAL;
- }
-
- rc = arch_xsplice_verify_func(f);
- if ( rc )
- return rc;
-
- /* Lookup function's old address if not already resolved. */
- if ( !f->old_addr )
- {
- f->old_addr = (void *)symbols_lookup_by_name(f->name);
- if ( !f->old_addr )
- {
- f->old_addr = (void *)xsplice_symbols_lookup_by_name(f->name);
- if ( !f->old_addr )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Could not resolve old address of %s\n",
- elf->name, f->name);
- return -ENOENT;
- }
- }
- dprintk(XENLOG_DEBUG, XSPLICE "%s: Resolved old address %s => %p\n",
- elf->name, f->name, f->old_addr);
- }
- }
-
- sec = xsplice_elf_sec_by_name(elf, ELF_BUILD_ID_NOTE);
- if ( sec )
- {
- const struct payload *data;
-
- n = sec->load_addr;
-
- if ( sec->sec->sh_size <= sizeof(*n) )
- return -EINVAL;
-
- if ( xen_build_id_check(n, sec->sec->sh_size,
- &payload->id.p, &payload->id.len) )
- return -EINVAL;
-
- if ( !payload->id.len || !payload->id.p )
- return -EINVAL;
-
- /* Make sure it is not a duplicate. */
- list_for_each_entry ( data, &payload_list, list )
- {
- /* No way _this_ payload is on the list. */
- ASSERT(data != payload);
- if ( data->id.len == payload->id.len &&
- !memcmp(data->id.p, payload->id.p, data->id.len) )
- {
- dprintk(XENLOG_DEBUG, XSPLICE "%s: Already loaded as %s!\n",
- elf->name, data->name);
- return -EEXIST;
- }
- }
- }
-
- sec = xsplice_elf_sec_by_name(elf, ELF_XSPLICE_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->dep.p, &payload->dep.len) )
- return -EINVAL;
-
- if ( !payload->dep.len || !payload->dep.p )
- return -EINVAL;
- }
-
- /* Setup the virtual region with proper data. */
- region = &payload->region;
-
- region->symbols_lookup = xsplice_symbols_lookup;
- region->start = payload->text_addr;
- region->end = payload->text_addr + payload->text_size;
-
- /* Optional sections. */
- for ( i = 0; i < BUGFRAME_NR; i++ )
- {
- char str[14];
-
- snprintf(str, sizeof(str), ".bug_frames.%u", i);
- sec = xsplice_elf_sec_by_name(elf, str);
- if ( !sec )
- continue;
-
- if ( sec->sec->sh_size % sizeof(*region->frame[i].bugs) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Wrong size of .bug_frames.%u!\n",
- elf->name, i);
- return -EINVAL;
- }
-
- region->frame[i].bugs = sec->load_addr;
- region->frame[i].n_bugs = sec->sec->sh_size /
- sizeof(*region->frame[i].bugs);
- }
-
-#ifndef CONFIG_ARM
- sec = xsplice_elf_sec_by_name(elf, ".altinstructions");
- if ( sec )
- {
- struct alt_instr *a, *start, *end;
-
- if ( sec->sec->sh_size % sizeof(*a) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Size of .alt_instr is not multiple of %zu!\n",
- elf->name, sizeof(*a));
- return -EINVAL;
- }
-
- start = sec->load_addr;
- end = sec->load_addr + sec->sec->sh_size;
-
- for ( a = start; a < end; a++ )
- {
- const void *instr = &a->instr_offset + a->instr_offset;
- const void *replacement = &a->repl_offset + a->repl_offset;
-
- if ( (instr < region->start && instr >= region->end) ||
- (replacement < region->start && replacement >= region->end) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s Alt patching outside payload: %p!\n",
- elf->name, instr);
- return -EINVAL;
- }
- }
- apply_alternatives_nocheck(start, end);
- }
-
- sec = xsplice_elf_sec_by_name(elf, ".ex_table");
- if ( sec )
- {
- struct exception_table_entry *s, *e;
-
- if ( !sec->sec->sh_size ||
- (sec->sec->sh_size % sizeof(*region->ex)) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Wrong size of .ex_table (exp:%lu vs %lu)!\n",
- elf->name, sizeof(*region->ex),
- sec->sec->sh_size);
- return -EINVAL;
- }
-
- s = sec->load_addr;
- e = sec->load_addr + sec->sec->sh_size;
-
- sort_exception_table(s ,e);
-
- region->ex = s;
- region->ex_end = e;
- }
-#endif
-
- return 0;
-}
-
-static bool_t is_payload_symbol(const struct xsplice_elf *elf,
- const struct xsplice_elf_sym *sym)
-{
- if ( sym->sym->st_shndx == SHN_UNDEF ||
- sym->sym->st_shndx >= elf->hdr->e_shnum )
- return 0;
-
- /*
- * The payload is not a final image as we dynmically link against it.
- * As such the linker has left symbols we don't care about and which
- * binutils would have removed had it be a final image. Hence we:
- * - For SHF_ALLOC - ignore symbols referring to sections that are not
- * loaded.
- */
- if ( !(elf->sec[sym->sym->st_shndx].sec->sh_flags & SHF_ALLOC) )
- return 0;
-
- /* - And ignore empty symbols (\0). */
- if ( *sym->name == '\0' )
- return 0;
-
- /*
- * - For SHF_MERGE - ignore local symbols referring to mergeable sections.
- * (ld squashes them all in one section and discards the symbols) when
- * those symbols start with '.L' (like .LCx). Those are intermediate
- * artifacts of assembly.
- *
- * See elf_link_input_bfd and _bfd_elf_is_local_label_name in binutils.
- */
- if ( (elf->sec[sym->sym->st_shndx].sec->sh_flags & SHF_MERGE) &&
- !strncmp(sym->name, ".L", 2) )
- return 0;
-
- return 1;
-}
-
-static int build_symbol_table(struct payload *payload,
- const struct xsplice_elf *elf)
-{
- unsigned int i, j, nsyms = 0;
- size_t strtab_len = 0;
- struct xsplice_symbol *symtab;
- char *strtab;
-
- ASSERT(payload->nfuncs);
-
- /* Recall that section @0 is always NULL. */
- for ( i = 1; i < elf->nsym; i++ )
- {
- if ( is_payload_symbol(elf, elf->sym + i) )
- {
- nsyms++;
- strtab_len += strlen(elf->sym[i].name) + 1;
- }
- }
-
- symtab = xmalloc_array(struct xsplice_symbol, nsyms);
- strtab = xmalloc_array(char, strtab_len);
-
- if ( !strtab || !symtab )
- {
- xfree(strtab);
- xfree(symtab);
- return -ENOMEM;
- }
-
- nsyms = 0;
- strtab_len = 0;
- for ( i = 1; i < elf->nsym; i++ )
- {
- if ( is_payload_symbol(elf, elf->sym + i) )
- {
- symtab[nsyms].name = strtab + strtab_len;
- symtab[nsyms].size = elf->sym[i].sym->st_size;
- symtab[nsyms].value = elf->sym[i].sym->st_value;
- symtab[nsyms].new_symbol = 0; /* May be overwritten below. */
- strtab_len += strlcpy(strtab + strtab_len, elf->sym[i].name,
- KSYM_NAME_LEN) + 1;
- nsyms++;
- }
- }
-
- for ( i = 0; i < nsyms; i++ )
- {
- bool_t found = 0;
-
- for ( j = 0; j < payload->nfuncs; j++ )
- {
- if ( symtab[i].value == (unsigned long)payload->funcs[j].new_addr )
- {
- found = 1;
- break;
- }
- }
-
- if ( !found )
- {
- if ( symbols_lookup_by_name(symtab[i].name) ||
- xsplice_symbols_lookup_by_name(symtab[i].name) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: duplicate new symbol: %s\n",
- elf->name, symtab[i].name);
- xfree(symtab);
- xfree(strtab);
- return -EEXIST;
- }
- symtab[i].new_symbol = 1;
- dprintk(XENLOG_DEBUG, XSPLICE "%s: new symbol %s\n",
- elf->name, symtab[i].name);
- }
- else
- {
- /* new_symbol is not set. */
- dprintk(XENLOG_DEBUG, XSPLICE "%s: overriding symbol %s\n",
- elf->name, symtab[i].name);
- }
- }
-
- payload->symtab = symtab;
- payload->strtab = strtab;
- payload->nsyms = nsyms;
-
- return 0;
-}
-
-static void free_payload(struct payload *data)
-{
- ASSERT(spin_is_locked(&payload_lock));
- list_del(&data->list);
- payload_cnt--;
- payload_version++;
- free_payload_data(data);
- xfree((void *)data->symtab);
- xfree((void *)data->strtab);
- xfree(data);
-}
-
-static int load_payload_data(struct payload *payload, void *raw, size_t len)
-{
- struct xsplice_elf elf = { .name = payload->name, .len = len };
- int rc = 0;
-
- rc = xsplice_elf_load(&elf, raw);
- if ( rc )
- goto out;
-
- rc = move_payload(payload, &elf);
- if ( rc )
- goto out;
-
- rc = xsplice_elf_resolve_symbols(&elf);
- if ( rc )
- goto out;
-
- rc = xsplice_elf_perform_relocs(&elf);
- if ( rc )
- goto out;
-
- rc = check_special_sections(&elf);
- if ( rc )
- goto out;
-
- rc = prepare_payload(payload, &elf);
- if ( rc )
- goto out;
-
- rc = build_symbol_table(payload, &elf);
- if ( rc )
- goto out;
-
- rc = secure_payload(payload, &elf);
-
- out:
- if ( rc )
- free_payload_data(payload);
-
- /* Free our temporary data structure. */
- xsplice_elf_free(&elf);
-
- return rc;
-}
-
-static int xsplice_upload(xen_sysctl_xsplice_upload_t *upload)
-{
- struct payload *data, *found;
- char n[XEN_XSPLICE_NAME_SIZE];
- void *raw_data;
- int rc;
-
- rc = verify_payload(upload, n);
- if ( rc )
- return rc;
-
- data = xzalloc(struct payload);
- raw_data = vmalloc(upload->size);
-
- spin_lock(&payload_lock);
-
- found = find_payload(n);
- if ( IS_ERR(found) )
- rc = PTR_ERR(found);
- else if ( found )
- rc = -EEXIST;
- else if ( !data || !raw_data )
- rc = -ENOMEM;
- else if ( __copy_from_guest(raw_data, upload->payload, upload->size) )
- rc = -EFAULT;
- else
- {
- memcpy(data->name, n, strlen(n));
-
- rc = load_payload_data(data, raw_data, upload->size);
- if ( rc )
- goto out;
-
- data->state = XSPLICE_STATE_CHECKED;
- INIT_LIST_HEAD(&data->list);
- INIT_LIST_HEAD(&data->applied_list);
-
- list_add_tail(&data->list, &payload_list);
- payload_cnt++;
- payload_version++;
- }
-
- out:
- spin_unlock(&payload_lock);
-
- vfree(raw_data);
-
- if ( rc && data )
- {
- xfree((void *)data->symtab);
- xfree((void *)data->strtab);
- xfree(data);
- }
-
- return rc;
-}
-
-static int xsplice_get(xen_sysctl_xsplice_get_t *get)
-{
- struct payload *data;
- int rc;
- char n[XEN_XSPLICE_NAME_SIZE];
-
- rc = get_name(&get->name, n);
- if ( rc )
- return rc;
-
- spin_lock(&payload_lock);
-
- data = find_payload(n);
- if ( IS_ERR_OR_NULL(data) )
- {
- spin_unlock(&payload_lock);
-
- if ( !data )
- return -ENOENT;
-
- return PTR_ERR(data);
- }
-
- get->status.state = data->state;
- get->status.rc = data->rc;
-
- spin_unlock(&payload_lock);
-
- return 0;
-}
-
-static int xsplice_list(xen_sysctl_xsplice_list_t *list)
-{
- xen_xsplice_status_t status;
- struct payload *data;
- unsigned int idx = 0, i = 0;
- int rc = 0;
-
- if ( list->nr > 1024 )
- return -E2BIG;
-
- if ( list->pad )
- return -EINVAL;
-
- if ( list->nr &&
- (!guest_handle_okay(list->status, list->nr) ||
- !guest_handle_okay(list->name, XEN_XSPLICE_NAME_SIZE * list->nr) ||
- !guest_handle_okay(list->len, list->nr)) )
- return -EINVAL;
-
- spin_lock(&payload_lock);
- if ( list->idx >= payload_cnt && payload_cnt )
- {
- spin_unlock(&payload_lock);
- return -EINVAL;
- }
-
- if ( list->nr )
- {
- list_for_each_entry( data, &payload_list, list )
- {
- uint32_t len;
-
- if ( list->idx > i++ )
- continue;
-
- status.state = data->state;
- status.rc = data->rc;
- len = strlen(data->name) + 1;
-
- /* N.B. 'idx' != 'i'. */
- if ( __copy_to_guest_offset(list->name, idx * XEN_XSPLICE_NAME_SIZE,
- data->name, len) ||
- __copy_to_guest_offset(list->len, idx, &len, 1) ||
- __copy_to_guest_offset(list->status, idx, &status, 1) )
- {
- rc = -EFAULT;
- break;
- }
-
- idx++;
-
- if ( (idx >= list->nr) || hypercall_preempt_check() )
- break;
- }
- }
- list->nr = payload_cnt - i; /* Remaining amount. */
- list->version = payload_version;
- spin_unlock(&payload_lock);
-
- /* And how many we have processed. */
- return rc ? : idx;
-}
-
-/*
- * The following functions get the CPUs into an appropriate state and
- * apply (or revert) each of the payload's functions. This is needed
- * for XEN_SYSCTL_XSPLICE_ACTION operation (see xsplice_action).
- */
-
-static int apply_payload(struct payload *data)
-{
- unsigned int i;
-
- printk(XENLOG_INFO XSPLICE "%s: Applying %u functions\n",
- data->name, data->nfuncs);
-
- arch_xsplice_patching_enter();
-
- for ( i = 0; i < data->nfuncs; i++ )
- arch_xsplice_apply_jmp(&data->funcs[i]);
-
- arch_xsplice_patching_leave();
-
- /*
- * We need RCU variant (which has barriers) in case we crash here.
- * The applied_list is iterated by the trap code.
- */
- list_add_tail_rcu(&data->applied_list, &applied_list);
- register_virtual_region(&data->region);
-
- return 0;
-}
-
-static int revert_payload(struct payload *data)
-{
- unsigned int i;
-
- printk(XENLOG_INFO XSPLICE "%s: Reverting\n", data->name);
-
- arch_xsplice_patching_enter();
-
- for ( i = 0; i < data->nfuncs; i++ )
- arch_xsplice_revert_jmp(&data->funcs[i]);
-
- arch_xsplice_patching_leave();
-
- /*
- * We need RCU variant (which has barriers) in case we crash here.
- * The applied_list is iterated by the trap code.
- */
- list_del_rcu(&data->applied_list);
- unregister_virtual_region(&data->region);
-
- return 0;
-}
-
-/*
- * This function is executed having all other CPUs with no deep stack (we may
- * have cpu_idle on it) and IRQs disabled.
- */
-static void xsplice_do_action(void)
-{
- int rc;
- struct payload *data, *other, *tmp;
-
- data = xsplice_work.data;
- /*
- * This function and the transition from asm to C code should be the only
- * one on any stack. No need to lock the payload list or applied list.
- */
- switch ( xsplice_work.cmd )
- {
- case XSPLICE_ACTION_APPLY:
- rc = apply_payload(data);
- if ( rc == 0 )
- data->state = XSPLICE_STATE_APPLIED;
- break;
-
- case XSPLICE_ACTION_REVERT:
- rc = revert_payload(data);
- if ( rc == 0 )
- data->state = XSPLICE_STATE_CHECKED;
- break;
-
- case XSPLICE_ACTION_REPLACE:
- rc = 0;
- /*
- * N.B: Use 'applied_list' member, not 'list'. We also abuse the
- * the 'normal' list iterator as the list is an RCU one.
- */
- list_for_each_entry_safe_reverse ( other, tmp, &applied_list, applied_list )
- {
- other->rc = revert_payload(other);
- if ( other->rc == 0 )
- other->state = XSPLICE_STATE_CHECKED;
- else
- {
- rc = -EINVAL;
- break;
- }
- }
-
- if ( rc == 0 )
- {
- rc = apply_payload(data);
- if ( rc == 0 )
- data->state = XSPLICE_STATE_APPLIED;
- }
- break;
-
- default:
- rc = -EINVAL; /* Make GCC5 happy. */
- ASSERT_UNREACHABLE();
- break;
- }
-
- /* We must set rc as xsplice_action sets it to -EAGAIN when kicking of. */
- data->rc = rc;
-}
-
-static bool_t is_work_scheduled(const struct payload *data)
-{
- ASSERT(spin_is_locked(&payload_lock));
-
- return xsplice_work.do_work && xsplice_work.data == data;
-}
-
-static int schedule_work(struct payload *data, uint32_t cmd, uint32_t timeout)
-{
- ASSERT(spin_is_locked(&payload_lock));
-
- /* Fail if an operation is already scheduled. */
- if ( xsplice_work.do_work )
- return -EBUSY;
-
- if ( !get_cpu_maps() )
- {
- printk(XENLOG_ERR XSPLICE "%s: unable to get cpu_maps lock!\n",
- data->name);
- return -EBUSY;
- }
-
- xsplice_work.cmd = cmd;
- xsplice_work.data = data;
- xsplice_work.timeout = timeout ?: MILLISECS(30);
-
- dprintk(XENLOG_DEBUG, XSPLICE "%s: timeout is %"PRI_stime"ms\n",
- data->name, xsplice_work.timeout / MILLISECS(1));
-
- atomic_set(&xsplice_work.semaphore, -1);
-
- xsplice_work.ready = 0;
-
- smp_wmb();
-
- xsplice_work.do_work = 1;
- this_cpu(work_to_do) = 1;
-
- put_cpu_maps();
-
- return 0;
-}
-
-static void reschedule_fn(void *unused)
-{
- this_cpu(work_to_do) = 1;
- raise_softirq(SCHEDULE_SOFTIRQ);
-}
-
-static int xsplice_spin(atomic_t *counter, s_time_t timeout,
- unsigned int cpus, const char *s)
-{
- int rc = 0;
-
- while ( atomic_read(counter) != cpus && NOW() < timeout )
- cpu_relax();
-
- /* Log & abort. */
- if ( atomic_read(counter) != cpus )
- {
- printk(XENLOG_ERR XSPLICE "%s: Timed out on semaphore in %s quiesce phase %u/%u\n",
- xsplice_work.data->name, s, atomic_read(counter), cpus);
- rc = -EBUSY;
- xsplice_work.data->rc = rc;
- smp_wmb();
- xsplice_work.do_work = 0;
- }
-
- return rc;
-}
-
-/*
- * The main function which manages the work of quiescing the system and
- * patching code.
- */
-void check_for_xsplice_work(void)
-{
-#define ACTION(x) [XSPLICE_ACTION_##x] = #x
- static const char *const names[] = {
- ACTION(APPLY),
- ACTION(REVERT),
- ACTION(REPLACE),
- };
-#undef ACTION
- unsigned int cpu = smp_processor_id();
- s_time_t timeout;
- unsigned long flags;
-
- /* Fast path: no work to do. */
- if ( !per_cpu(work_to_do, cpu ) )
- return;
-
- smp_rmb();
- /* In case we aborted, other CPUs can skip right away. */
- if ( !xsplice_work.do_work )
- {
- per_cpu(work_to_do, cpu) = 0;
- return;
- }
-
- ASSERT(local_irq_is_enabled());
-
- /* Set at -1, so will go up to num_online_cpus - 1. */
- if ( atomic_inc_and_test(&xsplice_work.semaphore) )
- {
- struct payload *p;
- unsigned int cpus;
-
- p = xsplice_work.data;
- if ( !get_cpu_maps() )
- {
- printk(XENLOG_ERR XSPLICE "%s: CPU%u - unable to get cpu_maps lock!\n",
- p->name, cpu);
- per_cpu(work_to_do, cpu) = 0;
- xsplice_work.data->rc = -EBUSY;
- smp_wmb();
- xsplice_work.do_work = 0;
- /*
- * Do NOT decrement xsplice_work.semaphore down - as that may cause
- * the other CPU (which may be at this point ready to increment it)
- * to assume the role of master and then needlessly time out
- * out (as do_work is zero).
- */
- return;
- }
- /* "Mask" NMIs. */
- arch_xsplice_mask();
-
- barrier(); /* MUST do it after get_cpu_maps. */
- cpus = num_online_cpus() - 1;
-
- if ( cpus )
- {
- dprintk(XENLOG_DEBUG, XSPLICE "%s: CPU%u - IPIing the other %u CPUs\n",
- p->name, cpu, cpus);
- smp_call_function(reschedule_fn, NULL, 0);
- }
-
- timeout = xsplice_work.timeout + NOW();
- if ( xsplice_spin(&xsplice_work.semaphore, timeout, cpus, "CPU") )
- goto abort;
-
- /* All CPUs are waiting, now signal to disable IRQs. */
- atomic_set(&xsplice_work.semaphore, 0);
- /*
- * MUST have a barrier after semaphore so that the other CPUs don't
- * leak out of the 'Wait for all CPUs to rendezvous' loop and increment
- * 'semaphore' before we set it to zero.
- */
- smp_wmb();
- xsplice_work.ready = 1;
-
- if ( !xsplice_spin(&xsplice_work.semaphore, timeout, cpus, "IRQ") )
- {
- local_irq_save(flags);
- /* Do the patching. */
- xsplice_do_action();
- /* Serialize and flush out the CPU via CPUID instruction (on x86). */
- arch_xsplice_post_action();
- local_irq_restore(flags);
- }
-
- abort:
- arch_xsplice_unmask();
-
- per_cpu(work_to_do, cpu) = 0;
- xsplice_work.do_work = 0;
-
- /* put_cpu_maps has an barrier(). */
- put_cpu_maps();
-
- printk(XENLOG_INFO XSPLICE "%s finished %s with rc=%d\n",
- p->name, names[xsplice_work.cmd], p->rc);
- }
- else
- {
- /* Wait for all CPUs to rendezvous. */
- while ( xsplice_work.do_work && !xsplice_work.ready )
- cpu_relax();
-
- /* Disable IRQs and signal. */
- local_irq_save(flags);
- /*
- * We re-use the sempahore, so MUST have it reset by master before
- * we exit the loop above.
- */
- atomic_inc(&xsplice_work.semaphore);
-
- /* Wait for patching to complete. */
- while ( xsplice_work.do_work )
- cpu_relax();
-
- /* To flush out pipeline. */
- arch_xsplice_post_action();
- local_irq_restore(flags);
-
- per_cpu(work_to_do, cpu) = 0;
- }
-}
-
-/*
- * Only allow dependent payload is applied on top of the correct
- * build-id.
- *
- * This enforces an stacking order - the first payload MUST be against the
- * hypervisor. The second against the first payload, and so on.
- *
- * Unless the 'internal' parameter is used - in which case we only
- * check against the hypervisor.
- */
-static int build_id_dep(struct payload *payload, bool_t internal)
-{
- const void *id = NULL;
- unsigned int len = 0;
- int rc;
- const char *name = "hypervisor";
-
- ASSERT(payload->dep.len && payload->dep.p);
-
- /* First time user is against hypervisor. */
- if ( internal )
- {
- rc = xen_build_id(&id, &len);
- if ( rc )
- return rc;
- }
- else
- {
- /* We should be against the last applied one. */
- const struct payload *data;
-
- data = list_last_entry(&applied_list, struct payload, applied_list);
-
- id = data->id.p;
- len = data->id.len;
- name = data->name;
- }
-
- if ( payload->dep.len != len ||
- memcmp(id, payload->dep.p, len) )
- {
- dprintk(XENLOG_ERR, "%s%s: check against %s build-id failed!\n",
- XSPLICE, payload->name, name);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int xsplice_action(xen_sysctl_xsplice_action_t *action)
-{
- struct payload *data;
- char n[XEN_XSPLICE_NAME_SIZE];
- int rc;
-
- rc = get_name(&action->name, n);
- if ( rc )
- return rc;
-
- spin_lock(&payload_lock);
-
- data = find_payload(n);
- if ( IS_ERR_OR_NULL(data) )
- {
- spin_unlock(&payload_lock);
-
- if ( !data )
- return -ENOENT;
-
- return PTR_ERR(data);
- }
-
- if ( is_work_scheduled(data) )
- {
- rc = -EBUSY;
- goto out;
- }
-
- switch ( action->cmd )
- {
- case XSPLICE_ACTION_UNLOAD:
- if ( data->state == XSPLICE_STATE_CHECKED )
- {
- free_payload(data);
- /* No touching 'data' from here on! */
- data = NULL;
- }
- else
- rc = -EINVAL;
- break;
-
- case XSPLICE_ACTION_REVERT:
- if ( data->state == XSPLICE_STATE_APPLIED )
- {
- const struct payload *p;
-
- p = list_last_entry(&applied_list, struct payload, applied_list);
- ASSERT(p);
- /* We should be the last applied one. */
- if ( p != data )
- {
- dprintk(XENLOG_ERR, "%s%s: can't unload. Top is %s!\n",
- XSPLICE, data->name, p->name);
- rc = -EBUSY;
- break;
- }
- data->rc = -EAGAIN;
- rc = schedule_work(data, action->cmd, action->timeout);
- }
- break;
-
- case XSPLICE_ACTION_APPLY:
- if ( data->state == XSPLICE_STATE_CHECKED )
- {
- rc = build_id_dep(data, !!list_empty(&applied_list));
- if ( rc )
- break;
- data->rc = -EAGAIN;
- rc = schedule_work(data, action->cmd, action->timeout);
- }
- break;
-
- case XSPLICE_ACTION_REPLACE:
- if ( data->state == XSPLICE_STATE_CHECKED )
- {
- rc = build_id_dep(data, 1 /* against hypervisor. */);
- if ( rc )
- break;
- data->rc = -EAGAIN;
- rc = schedule_work(data, action->cmd, action->timeout);
- }
- break;
-
- default:
- rc = -EOPNOTSUPP;
- break;
- }
-
- out:
- spin_unlock(&payload_lock);
-
- return rc;
-}
-
-int xsplice_op(xen_sysctl_xsplice_op_t *xsplice)
-{
- int rc;
-
- if ( xsplice->pad )
- return -EINVAL;
-
- switch ( xsplice->cmd )
- {
- case XEN_SYSCTL_XSPLICE_UPLOAD:
- rc = xsplice_upload(&xsplice->u.upload);
- break;
-
- case XEN_SYSCTL_XSPLICE_GET:
- rc = xsplice_get(&xsplice->u.get);
- break;
-
- case XEN_SYSCTL_XSPLICE_LIST:
- rc = xsplice_list(&xsplice->u.list);
- break;
-
- case XEN_SYSCTL_XSPLICE_ACTION:
- rc = xsplice_action(&xsplice->u.action);
- break;
-
- default:
- rc = -EOPNOTSUPP;
- break;
- }
-
- return rc;
-}
-
-static const char *state2str(unsigned int state)
-{
-#define STATE(x) [XSPLICE_STATE_##x] = #x
- static const char *const names[] = {
- STATE(CHECKED),
- STATE(APPLIED),
- };
-#undef STATE
-
- if ( state >= ARRAY_SIZE(names) || !names[state] )
- return "unknown";
-
- return names[state];
-}
-
-static void xsplice_printall(unsigned char key)
-{
- struct payload *data;
- const void *binary_id = NULL;
- unsigned int len = 0;
- unsigned int i;
-
- printk("'%c' pressed - Dumping all xsplice patches\n", key);
-
- if ( !xen_build_id(&binary_id, &len) )
- printk("build-id: %*phN\n", len, binary_id);
-
- if ( !spin_trylock(&payload_lock) )
- {
- printk("Lock held. Try again.\n");
- return;
- }
-
- list_for_each_entry ( data, &payload_list, list )
- {
- printk(" name=%s state=%s(%d) %p (.data=%p, .rodata=%p) using %u pages.\n",
- data->name, state2str(data->state), data->state, data->text_addr,
- data->rw_addr, data->ro_addr, data->pages);
-
- for ( i = 0; i < data->nfuncs; i++ )
- {
- struct xsplice_patch_func *f = &(data->funcs[i]);
- printk(" %s patch %p(%u) with %p (%u)\n",
- f->name, f->old_addr, f->old_size, f->new_addr, f->new_size);
-
- if ( i && !(i % 64) )
- {
- spin_unlock(&payload_lock);
- process_pending_softirqs();
- if ( spin_trylock(&payload_lock) )
- {
- printk("Couldn't reacquire lock. Try again.\n");
- return;
- }
- }
- }
- if ( data->id.len )
- printk("build-id=%*phN\n", data->id.len, data->id.p);
-
- if ( data->dep.len )
- printk("depend-on=%*phN\n", data->dep.len, data->dep.p);
- }
-
- spin_unlock(&payload_lock);
-}
-
-static int __init xsplice_init(void)
-{
- const void *binary_id;
- unsigned int len;
-
- if ( !xen_build_id(&binary_id, &len) )
- printk(XENLOG_INFO XSPLICE ": build-id: %*phN\n", len, binary_id);
-
- register_keyhandler('x', xsplice_printall, "print xsplicing info", 1);
-
- arch_xsplice_init();
- return 0;
-}
-__initcall(xsplice_init);
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 Citrix Systems R&D Ltd.
- */
-
-#include <xen/errno.h>
-#include <xen/lib.h>
-#include <xen/symbols.h>
-#include <xen/xsplice_elf.h>
-#include <xen/xsplice.h>
-
-const struct xsplice_elf_sec *xsplice_elf_sec_by_name(const struct xsplice_elf *elf,
- const char *name)
-{
- unsigned int i;
-
- for ( i = 1; i < elf->hdr->e_shnum; i++ )
- {
- if ( !strcmp(name, elf->sec[i].name) )
- return &elf->sec[i];
- }
-
- return NULL;
-}
-
-static int elf_verify_strtab(const struct xsplice_elf_sec *sec)
-{
- const Elf_Shdr *s;
- const char *contents;
-
- s = sec->sec;
-
- if ( s->sh_type != SHT_STRTAB )
- return -EINVAL;
-
- if ( !s->sh_size )
- return -EINVAL;
-
- contents = sec->data;
-
- if ( contents[0] || contents[s->sh_size - 1] )
- return -EINVAL;
-
- return 0;
-}
-
-static int elf_resolve_sections(struct xsplice_elf *elf, const void *data)
-{
- struct xsplice_elf_sec *sec;
- unsigned int i;
- Elf_Off delta;
- int rc;
-
- /* xsplice_elf_load sanity checked e_shnum. */
- sec = xmalloc_array(struct xsplice_elf_sec, elf->hdr->e_shnum);
- if ( !sec )
- {
- dprintk(XENLOG_ERR, XSPLICE"%s: Could not allocate memory for section table!\n",
- elf->name);
- return -ENOMEM;
- }
-
- elf->sec = sec;
-
- /* e_shoff and e_shnum overflow checks are done in xsplice_header_check. */
- delta = elf->hdr->e_shoff + elf->hdr->e_shnum * elf->hdr->e_shentsize;
- ASSERT(delta <= elf->len);
-
- for ( i = 1; i < elf->hdr->e_shnum; i++ )
- {
- delta = elf->hdr->e_shoff + i * elf->hdr->e_shentsize;
-
- sec[i].sec = data + delta;
-
- delta = sec[i].sec->sh_offset;
- /*
- * N.B. elf_resolve_section_names, elf_get_sym skip this check as
- * we do it here.
- */
- if ( delta < sizeof(Elf_Ehdr) ||
- (sec[i].sec->sh_type != SHT_NOBITS && /* Skip SHT_NOBITS */
- (delta > elf->len || (delta + sec[i].sec->sh_size > elf->len))) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section [%u] data %s of payload!\n",
- elf->name, i,
- delta < sizeof(Elf_Ehdr) ? "at ELF header" : "is past end");
- return -EINVAL;
- }
-
- sec[i].data = data + delta;
- /* Name is populated in elf_resolve_section_names. */
- sec[i].name = NULL;
-
- if ( sec[i].sec->sh_type == SHT_SYMTAB )
- {
- if ( elf->symtab )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Unsupported multiple symbol tables!\n",
- elf->name);
- return -EOPNOTSUPP;
- }
-
- elf->symtab = &sec[i];
-
- elf->symtab_idx = i;
- /*
- * elf->symtab->sec->sh_link would point to the right section
- * but we hadn't finished parsing all the sections.
- */
- if ( elf->symtab->sec->sh_link >= elf->hdr->e_shnum )
- {
- dprintk(XENLOG_ERR, XSPLICE
- "%s: Symbol table idx (%u) to strtab past end (%u)\n",
- elf->name, elf->symtab->sec->sh_link,
- elf->hdr->e_shnum);
- return -EINVAL;
- }
- }
- }
-
- if ( !elf->symtab )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: No symbol table found!\n",
- elf->name);
- return -EINVAL;
- }
-
- if ( !elf->symtab->sec->sh_size ||
- elf->symtab->sec->sh_entsize < sizeof(Elf_Sym) ||
- elf->symtab->sec->sh_size % elf->symtab->sec->sh_entsize )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Symbol table header is corrupted!\n",
- elf->name);
- return -EINVAL;
- }
-
- /*
- * There can be multiple SHT_STRTAB (.shstrtab, .strtab) so pick the one
- * associated with the symbol table.
- */
- elf->strtab = &sec[elf->symtab->sec->sh_link];
-
- rc = elf_verify_strtab(elf->strtab);
- if ( rc )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: String table section is corrupted\n",
- elf->name);
- }
-
- return rc;
-}
-
-static int elf_resolve_section_names(struct xsplice_elf *elf, const void *data)
-{
- const char *shstrtab;
- unsigned int i;
- Elf_Off offset, delta;
- struct xsplice_elf_sec *sec;
- int rc;
-
- /*
- * The elf->sec[0 -> e_shnum] structures have been verified by
- * elf_resolve_sections. Find file offset for section string table
- * (normally called .shstrtab)
- */
- sec = &elf->sec[elf->hdr->e_shstrndx];
-
- rc = elf_verify_strtab(sec);
- if ( rc )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section string table is corrupted\n",
- elf->name);
- return rc;
- }
-
- /* Verified in elf_resolve_sections but just in case. */
- offset = sec->sec->sh_offset;
- ASSERT(offset < elf->len && (offset + sec->sec->sh_size <= elf->len));
-
- shstrtab = data + offset;
-
- for ( i = 1; i < elf->hdr->e_shnum; i++ )
- {
- delta = elf->sec[i].sec->sh_name;
-
- /* Boundary check on offset of name within the .shstrtab. */
- if ( delta >= sec->sec->sh_size )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section %u name is not within .shstrtab!\n",
- elf->name, i);
- return -EINVAL;
- }
-
- elf->sec[i].name = shstrtab + delta;
- }
-
- return 0;
-}
-
-static int elf_get_sym(struct xsplice_elf *elf, const void *data)
-{
- const struct xsplice_elf_sec *symtab_sec, *strtab_sec;
- struct xsplice_elf_sym *sym;
- unsigned int i, nsym;
- Elf_Off offset;
- Elf_Word delta;
-
- symtab_sec = elf->symtab;
- strtab_sec = elf->strtab;
-
- /* Pointers arithmetic to get file offset. */
- offset = strtab_sec->data - data;
-
- /* Checked already in elf_resolve_sections, but just in case. */
- ASSERT(offset == strtab_sec->sec->sh_offset);
- ASSERT(offset < elf->len && (offset + strtab_sec->sec->sh_size <= elf->len));
-
- /* symtab_sec->data was computed in elf_resolve_sections. */
- ASSERT((symtab_sec->sec->sh_offset + data) == symtab_sec->data);
-
- /* No need to check values as elf_resolve_sections did it. */
- nsym = symtab_sec->sec->sh_size / symtab_sec->sec->sh_entsize;
-
- sym = xmalloc_array(struct xsplice_elf_sym, nsym);
- if ( !sym )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Could not allocate memory for symbols\n",
- elf->name);
- return -ENOMEM;
- }
-
- /* So we don't leak memory. */
- elf->sym = sym;
-
- for ( i = 1; i < nsym; i++ )
- {
- const Elf_Sym *s = symtab_sec->data + symtab_sec->sec->sh_entsize * i;
-
- delta = s->st_name;
- /* Boundary check within the .strtab. */
- if ( delta >= strtab_sec->sec->sh_size )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Symbol [%u] name is not within .strtab!\n",
- elf->name, i);
- return -EINVAL;
- }
-
- sym[i].sym = s;
- sym[i].name = strtab_sec->data + delta;
- }
- elf->nsym = nsym;
-
- return 0;
-}
-
-int xsplice_elf_resolve_symbols(struct xsplice_elf *elf)
-{
- unsigned int i;
- int rc = 0;
-
- ASSERT(elf->sym);
-
- for ( i = 1; i < elf->nsym; i++ )
- {
- unsigned int idx = elf->sym[i].sym->st_shndx;
- const Elf_Sym *sym = elf->sym[i].sym;
- Elf_Addr st_value = sym->st_value;
-
- switch ( idx )
- {
- case SHN_COMMON:
- dprintk(XENLOG_ERR, XSPLICE "%s: Unexpected common symbol: %s\n",
- elf->name, elf->sym[i].name);
- rc = -EINVAL;
- break;
-
- case SHN_UNDEF:
- st_value = symbols_lookup_by_name(elf->sym[i].name);
- if ( !st_value )
- {
- st_value = xsplice_symbols_lookup_by_name(elf->sym[i].name);
- if ( !st_value )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Unknown symbol: %s\n",
- elf->name, elf->sym[i].name);
- rc = -ENOENT;
- break;
- }
- }
- dprintk(XENLOG_DEBUG, XSPLICE "%s: Undefined symbol resolved: %s => %#"PRIxElfAddr"\n",
- elf->name, elf->sym[i].name, st_value);
- break;
-
- case SHN_ABS:
- dprintk(XENLOG_DEBUG, XSPLICE "%s: Absolute symbol: %s => %#"PRIxElfAddr"\n",
- elf->name, elf->sym[i].name, sym->st_value);
- break;
-
- default:
- /* SHN_COMMON and SHN_ABS are above. */
- if ( idx >= SHN_LORESERVE )
- rc = -EOPNOTSUPP;
- else if ( idx >= elf->hdr->e_shnum )
- rc = -EINVAL;
-
- if ( rc )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Out of bounds symbol section %#x\n",
- elf->name, idx);
- break;
- }
-
- /* Matches 'move_payload' which ignores such sections. */
- if ( !(elf->sec[idx].sec->sh_flags & SHF_ALLOC) )
- break;
-
- st_value += (unsigned long)elf->sec[idx].load_addr;
- if ( elf->sym[i].name )
- dprintk(XENLOG_DEBUG, XSPLICE "%s: Symbol resolved: %s => %#"PRIxElfAddr" (%s)\n",
- elf->name, elf->sym[i].name,
- st_value, elf->sec[idx].name);
- }
-
- if ( rc )
- break;
-
- ((Elf_Sym *)sym)->st_value = st_value;
- }
-
- return rc;
-}
-
-int xsplice_elf_perform_relocs(struct xsplice_elf *elf)
-{
- struct xsplice_elf_sec *r, *base;
- unsigned int i;
- int rc = 0;
-
- ASSERT(elf->sym);
-
- for ( i = 1; i < elf->hdr->e_shnum; i++ )
- {
- r = &elf->sec[i];
-
- if ( (r->sec->sh_type != SHT_RELA) &&
- (r->sec->sh_type != SHT_REL) )
- continue;
-
- /* Is it a valid relocation section? */
- if ( r->sec->sh_info >= elf->hdr->e_shnum )
- continue;
-
- base = &elf->sec[r->sec->sh_info];
-
- /* Don't relocate non-allocated sections. */
- if ( !(base->sec->sh_flags & SHF_ALLOC) )
- continue;
-
- if ( r->sec->sh_link != elf->symtab_idx )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Relative link of %s is incorrect (%d, expected=%d)\n",
- elf->name, r->name, r->sec->sh_link, elf->symtab_idx);
- rc = -EINVAL;
- break;
- }
-
- if ( r->sec->sh_type == SHT_RELA )
- rc = arch_xsplice_perform_rela(elf, base, r);
- else /* SHT_REL */
- rc = arch_xsplice_perform_rel(elf, base, r);
-
- if ( rc )
- break;
- }
-
- return rc;
-}
-
-static int xsplice_header_check(const struct xsplice_elf *elf)
-{
- const Elf_Ehdr *hdr = elf->hdr;
- int rc;
-
- if ( sizeof(*elf->hdr) > elf->len )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section header is bigger than payload!\n",
- elf->name);
- return -EINVAL;
- }
-
- if ( !IS_ELF(*hdr) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Not an ELF payload!\n", elf->name);
- return -EINVAL;
- }
-
- /* EI_CLASS, EI_DATA, and e_flags are platform specific. */
- if ( hdr->e_version != EV_CURRENT ||
- hdr->e_ident[EI_VERSION] != EV_CURRENT ||
- hdr->e_ident[EI_ABIVERSION] != 0 ||
- (hdr->e_ident[EI_OSABI] != ELFOSABI_NONE &&
- hdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD) ||
- hdr->e_type != ET_REL ||
- hdr->e_phnum != 0 )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Invalid ELF payload!\n", elf->name);
- return -EOPNOTSUPP;
- }
-
- rc = arch_xsplice_verify_elf(elf);
- if ( rc )
- return rc;
-
- if ( elf->hdr->e_shstrndx == SHN_UNDEF )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section name idx is undefined!?\n",
- elf->name);
- return -EINVAL;
- }
-
- /* Arbitrary boundary limit. */
- if ( elf->hdr->e_shnum >= 1024 )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Too many (%u) sections!\n",
- elf->name, elf->hdr->e_shnum);
- return -EOPNOTSUPP;
- }
-
- /* Check that section name index is within the sections. */
- if ( elf->hdr->e_shstrndx >= elf->hdr->e_shnum )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section name idx (%u) is past end of sections (%u)!\n",
- elf->name, elf->hdr->e_shstrndx, elf->hdr->e_shnum);
- return -EINVAL;
- }
-
- if ( elf->hdr->e_shoff >= elf->len )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Bogus e_shoff!\n", elf->name);
- return -EINVAL;
- }
-
- if ( elf->hdr->e_shentsize < sizeof(Elf_Shdr) )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section header size is %u! Expected %zu!?\n",
- elf->name, elf->hdr->e_shentsize, sizeof(Elf_Shdr));
- return -EINVAL;
- }
-
- if ( ((elf->len - elf->hdr->e_shoff) / elf->hdr->e_shentsize) <
- elf->hdr->e_shnum )
- {
- dprintk(XENLOG_ERR, XSPLICE "%s: Section header size is corrupted!\n",
- elf->name);
- return -EINVAL;
- }
-
- return 0;
-}
-
-int xsplice_elf_load(struct xsplice_elf *elf, const void *data)
-{
- int rc;
-
- elf->hdr = data;
-
- rc = xsplice_header_check(elf);
- if ( rc )
- return rc;
-
- rc = elf_resolve_sections(elf, data);
- if ( rc )
- return rc;
-
- rc = elf_resolve_section_names(elf, data);
- if ( rc )
- return rc;
-
- rc = elf_get_sym(elf, data);
- if ( rc )
- return rc;
-
- return 0;
-}
-
-void xsplice_elf_free(struct xsplice_elf *elf)
-{
- xfree(elf->sec);
- elf->sec = NULL;
- xfree(elf->sym);
- elf->sym = NULL;
- elf->nsym = 0;
- elf->name = NULL;
- elf->len = 0;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
unsigned long get_stack_trace_bottom(unsigned long sp);
unsigned long get_stack_dump_bottom (unsigned long sp);
-#ifdef CONFIG_XSPLICE
-# define CHECK_FOR_XSPLICE_WORK "call check_for_xsplice_work;"
+#ifdef CONFIG_LIVEPATCH
+# define CHECK_FOR_LIVEPATCH_WORK "call check_for_livepatch_work;"
#else
-# define CHECK_FOR_XSPLICE_WORK ""
+# define CHECK_FOR_LIVEPATCH_WORK ""
#endif
#define reset_stack_and_jump(__fn) \
({ \
__asm__ __volatile__ ( \
"mov %0,%%"__OP"sp;" \
- CHECK_FOR_XSPLICE_WORK \
+ CHECK_FOR_LIVEPATCH_WORK \
"jmp %c1" \
: : "r" (guest_cpu_user_regs()), "i" (__fn) : "memory" ); \
unreachable(); \
DEFINE_XEN_GUEST_HANDLE(xen_sysctl_featureset_t);
/*
- * XEN_SYSCTL_XSPLICE_op
+ * XEN_SYSCTL_LIVEPATCH_op
*
- * Refer to the docs/unstable/misc/xsplice.markdown
+ * Refer to the docs/unstable/misc/livepatch.markdown
* for the design details of this hypercall.
*
* There are four sub-ops:
- * XEN_SYSCTL_XSPLICE_UPLOAD (0)
- * XEN_SYSCTL_XSPLICE_GET (1)
- * XEN_SYSCTL_XSPLICE_LIST (2)
- * XEN_SYSCTL_XSPLICE_ACTION (3)
+ * XEN_SYSCTL_LIVEPATCH_UPLOAD (0)
+ * XEN_SYSCTL_LIVEPATCH_GET (1)
+ * XEN_SYSCTL_LIVEPATCH_LIST (2)
+ * XEN_SYSCTL_LIVEPATCH_ACTION (3)
*
* The normal sequence of sub-ops is to:
- * 1) XEN_SYSCTL_XSPLICE_UPLOAD to upload the payload. If errors STOP.
- * 2) XEN_SYSCTL_XSPLICE_GET to check the `->rc`. If -XEN_EAGAIN spin.
+ * 1) XEN_SYSCTL_LIVEPATCH_UPLOAD to upload the payload. If errors STOP.
+ * 2) XEN_SYSCTL_LIVEPATCH_GET to check the `->rc`. If -XEN_EAGAIN spin.
* If zero go to next step.
- * 3) XEN_SYSCTL_XSPLICE_ACTION with XSPLICE_ACTION_APPLY to apply the patch.
- * 4) XEN_SYSCTL_XSPLICE_GET to check the `->rc`. If in -XEN_EAGAIN spin.
+ * 3) XEN_SYSCTL_LIVEPATCH_ACTION with LIVEPATCH_ACTION_APPLY to apply the patch.
+ * 4) XEN_SYSCTL_LIVEPATCH_GET to check the `->rc`. If in -XEN_EAGAIN spin.
* If zero exit with success.
*/
-#define XSPLICE_PAYLOAD_VERSION 1
+#define LIVEPATCH_PAYLOAD_VERSION 1
/*
- * .xsplice.funcs structure layout defined in the `Payload format`
- * section in the xSplice design document.
+ * .livepatch.funcs structure layout defined in the `Payload format`
+ * section in the Live Patch design document.
*
* We guard this with __XEN__ as toolstacks SHOULD not use it.
*/
#ifdef __XEN__
-struct xsplice_patch_func {
+struct livepatch_func {
const char *name; /* Name of function to be patched. */
void *new_addr;
void *old_addr;
uint32_t new_size;
uint32_t old_size;
- uint8_t version; /* MUST be XSPLICE_PAYLOAD_VERSION. */
+ uint8_t version; /* MUST be LIVEPATCH_PAYLOAD_VERSION. */
uint8_t opaque[31];
};
-typedef struct xsplice_patch_func xsplice_patch_func_t;
+typedef struct livepatch_func livepatch_func_t;
#endif
/*
* Structure describing an ELF payload. Uniquely identifies the
* payload. Should be human readable.
- * Recommended length is upto XEN_XSPLICE_NAME_SIZE.
+ * Recommended length is upto XEN_LIVEPATCH_NAME_SIZE.
* Includes the NUL terminator.
*/
-#define XEN_XSPLICE_NAME_SIZE 128
-struct xen_xsplice_name {
+#define XEN_LIVEPATCH_NAME_SIZE 128
+struct xen_livepatch_name {
XEN_GUEST_HANDLE_64(char) name; /* IN: pointer to name. */
uint16_t size; /* IN: size of name. May be upto
- XEN_XSPLICE_NAME_SIZE. */
+ XEN_LIVEPATCH_NAME_SIZE. */
uint16_t pad[3]; /* IN: MUST be zero. */
};
-typedef struct xen_xsplice_name xen_xsplice_name_t;
-DEFINE_XEN_GUEST_HANDLE(xen_xsplice_name_t);
+typedef struct xen_livepatch_name xen_livepatch_name_t;
+DEFINE_XEN_GUEST_HANDLE(xen_livepatch_name_t);
/*
* Upload a payload to the hypervisor. The payload is verified
* against basic checks and if there are any issues the proper return code
* will be returned. The payload is not applied at this time - that is
- * controlled by XEN_SYSCTL_XSPLICE_ACTION.
+ * controlled by XEN_SYSCTL_LIVEPATCH_ACTION.
*
* The return value is zero if the payload was succesfully uploaded.
* Otherwise an EXX return value is provided. Duplicate `name` are not
* The payload at this point is verified against basic checks.
*
* The `payload` is the ELF payload as mentioned in the `Payload format`
- * section in the xSplice design document.
+ * section in the Live Patch design document.
*/
-#define XEN_SYSCTL_XSPLICE_UPLOAD 0
-struct xen_sysctl_xsplice_upload {
- xen_xsplice_name_t name; /* IN, name of the patch. */
+#define XEN_SYSCTL_LIVEPATCH_UPLOAD 0
+struct xen_sysctl_livepatch_upload {
+ xen_livepatch_name_t name; /* IN, name of the patch. */
uint64_t size; /* IN, size of the ELF file. */
XEN_GUEST_HANDLE_64(uint8) payload; /* IN, the ELF file. */
};
-typedef struct xen_sysctl_xsplice_upload xen_sysctl_xsplice_upload_t;
-DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_upload_t);
+typedef struct xen_sysctl_livepatch_upload xen_sysctl_livepatch_upload_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_livepatch_upload_t);
/*
* Retrieve an status of an specific payload.
*
- * Upon completion the `struct xen_xsplice_status` is updated.
+ * Upon completion the `struct xen_livepatch_status` is updated.
*
* The return value is zero on success and XEN_EXX on failure. This operation
* is synchronous and does not require preemption.
*/
-#define XEN_SYSCTL_XSPLICE_GET 1
+#define XEN_SYSCTL_LIVEPATCH_GET 1
-struct xen_xsplice_status {
-#define XSPLICE_STATE_CHECKED 1
-#define XSPLICE_STATE_APPLIED 2
- uint32_t state; /* OUT: XSPLICE_STATE_*. */
+struct xen_livepatch_status {
+#define LIVEPATCH_STATE_CHECKED 1
+#define LIVEPATCH_STATE_APPLIED 2
+ uint32_t state; /* OUT: LIVEPATCH_STATE_*. */
int32_t rc; /* OUT: 0 if no error, otherwise -XEN_EXX. */
};
-typedef struct xen_xsplice_status xen_xsplice_status_t;
-DEFINE_XEN_GUEST_HANDLE(xen_xsplice_status_t);
+typedef struct xen_livepatch_status xen_livepatch_status_t;
+DEFINE_XEN_GUEST_HANDLE(xen_livepatch_status_t);
-struct xen_sysctl_xsplice_get {
- xen_xsplice_name_t name; /* IN, name of the payload. */
- xen_xsplice_status_t status; /* IN/OUT, state of it. */
+struct xen_sysctl_livepatch_get {
+ xen_livepatch_name_t name; /* IN, name of the payload. */
+ xen_livepatch_status_t status; /* IN/OUT, state of it. */
};
-typedef struct xen_sysctl_xsplice_get xen_sysctl_xsplice_get_t;
-DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_get_t);
+typedef struct xen_sysctl_livepatch_get xen_sysctl_livepatch_get_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_livepatch_get_t);
/*
* Retrieve an array of abbreviated status and names of payloads that are
* data and start from scratch. It is OK for the toolstack to use the new
* `version` field.
*/
-#define XEN_SYSCTL_XSPLICE_LIST 2
-struct xen_sysctl_xsplice_list {
+#define XEN_SYSCTL_LIVEPATCH_LIST 2
+struct xen_sysctl_livepatch_list {
uint32_t version; /* OUT: Hypervisor stamps value.
If varies between calls, we are
* getting stale data. */
amount of payloads and version.
OUT: How many payloads left. */
uint32_t pad; /* IN: Must be zero. */
- XEN_GUEST_HANDLE_64(xen_xsplice_status_t) status; /* OUT. Must have enough
+ 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_XSPLICE_NAME_SIZE in size.
+ MUST XEN_LIVEPATCH_NAME_SIZE in size.
Must have nr of them. */
XEN_GUEST_HANDLE_64(uint32) len; /* OUT: Array of lengths of name's.
Must have nr of them. */
};
-typedef struct xen_sysctl_xsplice_list xen_sysctl_xsplice_list_t;
-DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_list_t);
+typedef struct xen_sysctl_livepatch_list xen_sysctl_livepatch_list_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_livepatch_list_t);
/*
* Perform an operation on the payload structure referenced by the `name` field.
* The operation request is asynchronous and the status should be retrieved
- * by using either XEN_SYSCTL_XSPLICE_GET or XEN_SYSCTL_XSPLICE_LIST hypercall.
+ * by using either XEN_SYSCTL_LIVEPATCH_GET or XEN_SYSCTL_LIVEPATCH_LIST hypercall.
*/
-#define XEN_SYSCTL_XSPLICE_ACTION 3
-struct xen_sysctl_xsplice_action {
- xen_xsplice_name_t name; /* IN, name of the patch. */
-#define XSPLICE_ACTION_UNLOAD 1
-#define XSPLICE_ACTION_REVERT 2
-#define XSPLICE_ACTION_APPLY 3
-#define XSPLICE_ACTION_REPLACE 4
- uint32_t cmd; /* IN: XSPLICE_ACTION_*. */
+#define XEN_SYSCTL_LIVEPATCH_ACTION 3
+struct xen_sysctl_livepatch_action {
+ xen_livepatch_name_t name; /* IN, name of the patch. */
+#define LIVEPATCH_ACTION_UNLOAD 1
+#define LIVEPATCH_ACTION_REVERT 2
+#define LIVEPATCH_ACTION_APPLY 3
+#define LIVEPATCH_ACTION_REPLACE 4
+ uint32_t cmd; /* IN: LIVEPATCH_ACTION_*. */
uint32_t timeout; /* IN: Zero if no timeout. */
/* Or upper bound of time (ms) */
/* for operation to take. */
};
-typedef struct xen_sysctl_xsplice_action xen_sysctl_xsplice_action_t;
-DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_action_t);
+typedef struct xen_sysctl_livepatch_action xen_sysctl_livepatch_action_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_livepatch_action_t);
-struct xen_sysctl_xsplice_op {
- uint32_t cmd; /* IN: XEN_SYSCTL_XSPLICE_*. */
+struct xen_sysctl_livepatch_op {
+ uint32_t cmd; /* IN: XEN_SYSCTL_LIVEPATCH_*. */
uint32_t pad; /* IN: Always zero. */
union {
- xen_sysctl_xsplice_upload_t upload;
- xen_sysctl_xsplice_list_t list;
- xen_sysctl_xsplice_get_t get;
- xen_sysctl_xsplice_action_t action;
+ xen_sysctl_livepatch_upload_t upload;
+ xen_sysctl_livepatch_list_t list;
+ xen_sysctl_livepatch_get_t get;
+ xen_sysctl_livepatch_action_t action;
} u;
};
-typedef struct xen_sysctl_xsplice_op xen_sysctl_xsplice_op_t;
-DEFINE_XEN_GUEST_HANDLE(xen_sysctl_xsplice_op_t);
+typedef struct xen_sysctl_livepatch_op xen_sysctl_livepatch_op_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_livepatch_op_t);
struct xen_sysctl {
uint32_t cmd;
#define XEN_SYSCTL_tmem_op 24
#define XEN_SYSCTL_get_cpu_levelling_caps 25
#define XEN_SYSCTL_get_cpu_featureset 26
-#define XEN_SYSCTL_xsplice_op 27
+#define XEN_SYSCTL_livepatch_op 27
uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */
union {
struct xen_sysctl_readconsole readconsole;
struct xen_sysctl_tmem_op tmem_op;
struct xen_sysctl_cpu_levelling_caps cpu_levelling_caps;
struct xen_sysctl_cpu_featureset cpu_featureset;
- struct xen_sysctl_xsplice_op xsplice;
+ struct xen_sysctl_livepatch_op livepatch;
uint8_t pad[128];
} u;
};
#define ELF64_R_TYPE(info) ((info) & 0xFFFFFFFF)
#define ELF64_R_INFO(s,t) (((s) << 32) + (u_int32_t)(t))
-/* x86-64 relocation types. We list only the ones xSplice implements. */
+/* x86-64 relocation types. We list only the ones Live Patch implements. */
#define R_X86_64_NONE 0 /* No reloc */
#define R_X86_64_64 1 /* Direct 64 bit */
#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
--- /dev/null
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ *
+ */
+
+#ifndef __XEN_LIVEPATCH_H__
+#define __XEN_LIVEPATCH_H__
+
+struct livepatch_elf;
+struct livepatch_elf_sec;
+struct livepatch_elf_sym;
+struct xen_sysctl_livepatch_op;
+
+#include <xen/elfstructs.h>
+#ifdef CONFIG_LIVEPATCH
+
+/*
+ * We use alternative and exception table code - which by default are __init
+ * only, however we need them during runtime. These macros allows us to build
+ * the image with these functions built-in. (See the #else below).
+ */
+#define init_or_livepatch_const
+#define init_or_livepatch_constrel
+#define init_or_livepatch_data
+#define init_or_livepatch
+
+/* 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"
+
+struct livepatch_symbol {
+ const char *name;
+ unsigned long value;
+ unsigned int size;
+ bool_t new_symbol;
+};
+
+int livepatch_op(struct xen_sysctl_livepatch_op *);
+void check_for_livepatch_work(void);
+unsigned long livepatch_symbols_lookup_by_name(const char *symname);
+bool_t is_patch(const void *addr);
+int xen_build_id_check(const Elf_Note *n, unsigned int n_sz,
+ const void **p, unsigned int *len);
+
+/* Arch hooks. */
+int arch_livepatch_verify_elf(const struct livepatch_elf *elf);
+int arch_livepatch_perform_rel(struct livepatch_elf *elf,
+ const struct livepatch_elf_sec *base,
+ const struct livepatch_elf_sec *rela);
+int arch_livepatch_perform_rela(struct livepatch_elf *elf,
+ const struct livepatch_elf_sec *base,
+ const struct livepatch_elf_sec *rela);
+enum va_type {
+ LIVEPATCH_VA_RX, /* .text */
+ LIVEPATCH_VA_RW, /* .data */
+ LIVEPATCH_VA_RO, /* .rodata */
+};
+
+/*
+ * Function to secure the allocate pages (from arch_livepatch_alloc_payload)
+ * with the right page permissions.
+ */
+int arch_livepatch_secure(const void *va, unsigned int pages, enum va_type types);
+
+void arch_livepatch_init(void);
+
+#include <public/sysctl.h> /* For struct livepatch_func. */
+int arch_livepatch_verify_func(const struct livepatch_func *func);
+/*
+ * These functions are called around the critical region patching live code,
+ * for an architecture to take make appropratie global state adjustments.
+ */
+void arch_livepatch_quiesce(void);
+void arch_livepatch_revive(void);
+
+void arch_livepatch_apply_jmp(struct livepatch_func *func);
+void arch_livepatch_revert_jmp(const struct livepatch_func *func);
+void arch_livepatch_post_action(void);
+
+void arch_livepatch_mask(void);
+void arch_livepatch_unmask(void);
+#else
+
+/*
+ * If not compiling with Live Patch certain functionality should stay as
+ * __init.
+ */
+#define init_or_livepatch_const __initconst
+#define init_or_livepatch_constrel __initconstrel
+#define init_or_livepatch_data __initdata
+#define init_or_livepatch __init
+
+#include <xen/errno.h> /* For -ENOSYS */
+static inline int livepatch_op(struct xen_sysctl_livepatch_op *op)
+{
+ return -ENOSYS;
+}
+
+static inline void check_for_livepatch_work(void) { };
+static inline bool_t is_patch(const void *addr)
+{
+ return 0;
+}
+#endif /* CONFIG_LIVEPATCH */
+
+#endif /* __XEN_LIVEPATCH_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2016 Citrix Systems R&D Ltd.
+ */
+
+#ifndef __XEN_LIVEPATCH_ELF_H__
+#define __XEN_LIVEPATCH_ELF_H__
+
+#include <xen/types.h>
+#include <xen/elfstructs.h>
+
+/* The following describes an Elf file as consumed by Xen Live Patch. */
+struct livepatch_elf_sec {
+ const Elf_Shdr *sec; /* Hooked up in elf_resolve_sections.*/
+ const char *name; /* Human readable name hooked in
+ elf_resolve_section_names. */
+ const void *data; /* Pointer to the section (done by
+ elf_resolve_sections). */
+ void *load_addr; /* A pointer to the allocated destination.
+ Done by load_payload_data. */
+};
+
+struct livepatch_elf_sym {
+ const Elf_Sym *sym;
+ const char *name;
+};
+
+struct livepatch_elf {
+ const char *name; /* Pointer to payload->name. */
+ size_t len; /* Length of the ELF file. */
+ const Elf_Ehdr *hdr; /* ELF file. */
+ struct livepatch_elf_sec *sec; /* Array of sections, allocated by us. */
+ struct livepatch_elf_sym *sym; /* Array of symbols , allocated by us. */
+ unsigned int nsym;
+ const struct livepatch_elf_sec *symtab;/* Pointer to .symtab section - aka to
+ sec[symtab_idx]. */
+ const struct livepatch_elf_sec *strtab;/* Pointer to .strtab section. */
+ unsigned int symtab_idx;
+};
+
+const struct livepatch_elf_sec *
+livepatch_elf_sec_by_name(const struct livepatch_elf *elf,
+ const char *name);
+int livepatch_elf_load(struct livepatch_elf *elf, const void *data);
+void livepatch_elf_free(struct livepatch_elf *elf);
+
+int livepatch_elf_resolve_symbols(struct livepatch_elf *elf);
+int livepatch_elf_perform_relocs(struct livepatch_elf *elf);
+
+#endif /* __XEN_LIVEPATCH_ELF_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+++ /dev/null
-/*
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
- *
- */
-
-#ifndef __XEN_XSPLICE_H__
-#define __XEN_XSPLICE_H__
-
-struct xsplice_elf;
-struct xsplice_elf_sec;
-struct xsplice_elf_sym;
-struct xen_sysctl_xsplice_op;
-
-#include <xen/elfstructs.h>
-#ifdef CONFIG_XSPLICE
-
-/*
- * We use alternative and exception table code - which by default are __init
- * only, however we need them during runtime. These macros allows us to build
- * the image with these functions built-in. (See the #else below).
- */
-#define init_or_xsplice_const
-#define init_or_xsplice_constrel
-#define init_or_xsplice_data
-#define init_or_xsplice
-
-/* Convenience define for printk. */
-#define XSPLICE "xsplice: "
-/* ELF payload special section names. */
-#define ELF_XSPLICE_FUNC ".xsplice.funcs"
-#define ELF_XSPLICE_DEPENDS ".xsplice.depends"
-#define ELF_BUILD_ID_NOTE ".note.gnu.build-id"
-
-struct xsplice_symbol {
- const char *name;
- unsigned long value;
- unsigned int size;
- bool_t new_symbol;
-};
-
-int xsplice_op(struct xen_sysctl_xsplice_op *);
-void check_for_xsplice_work(void);
-unsigned long xsplice_symbols_lookup_by_name(const char *symname);
-bool_t is_patch(const void *addr);
-int xen_build_id_check(const Elf_Note *n, unsigned int n_sz,
- const void **p, unsigned int *len);
-
-/* Arch hooks. */
-int arch_xsplice_verify_elf(const struct xsplice_elf *elf);
-int arch_xsplice_perform_rel(struct xsplice_elf *elf,
- const struct xsplice_elf_sec *base,
- const struct xsplice_elf_sec *rela);
-int arch_xsplice_perform_rela(struct xsplice_elf *elf,
- const struct xsplice_elf_sec *base,
- const struct xsplice_elf_sec *rela);
-enum va_type {
- XSPLICE_VA_RX, /* .text */
- XSPLICE_VA_RW, /* .data */
- XSPLICE_VA_RO, /* .rodata */
-};
-
-/*
- * Function to secure the allocate pages (from arch_xsplice_alloc_payload)
- * with the right page permissions.
- */
-int arch_xsplice_secure(const void *va, unsigned int pages, enum va_type types);
-
-void arch_xsplice_init(void);
-
-#include <public/sysctl.h> /* For struct xsplice_patch_func. */
-int arch_xsplice_verify_func(const struct xsplice_patch_func *func);
-/*
- * These functions are called around the critical region patching live code,
- * for an architecture to take make appropratie global state adjustments.
- */
-void arch_xsplice_patching_enter(void);
-void arch_xsplice_patching_leave(void);
-
-void arch_xsplice_apply_jmp(struct xsplice_patch_func *func);
-void arch_xsplice_revert_jmp(const struct xsplice_patch_func *func);
-void arch_xsplice_post_action(void);
-
-void arch_xsplice_mask(void);
-void arch_xsplice_unmask(void);
-#else
-
-/*
- * If not compiling with xSplice certain functionality should stay as
- * __init.
- */
-#define init_or_xsplice_const __initconst
-#define init_or_xsplice_constrel __initconstrel
-#define init_or_xsplice_data __initdata
-#define init_or_xsplice __init
-
-#include <xen/errno.h> /* For -ENOSYS */
-static inline int xsplice_op(struct xen_sysctl_xsplice_op *op)
-{
- return -ENOSYS;
-}
-
-static inline void check_for_xsplice_work(void) { };
-static inline bool_t is_patch(const void *addr)
-{
- return 0;
-}
-#endif /* CONFIG_XSPLICE */
-
-#endif /* __XEN_XSPLICE_H__ */
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
+++ /dev/null
-/*
- * Copyright (C) 2016 Citrix Systems R&D Ltd.
- */
-
-#ifndef __XEN_XSPLICE_ELF_H__
-#define __XEN_XSPLICE_ELF_H__
-
-#include <xen/types.h>
-#include <xen/elfstructs.h>
-
-/* The following describes an Elf file as consumed by xSplice. */
-struct xsplice_elf_sec {
- const Elf_Shdr *sec; /* Hooked up in elf_resolve_sections.*/
- const char *name; /* Human readable name hooked in
- elf_resolve_section_names. */
- const void *data; /* Pointer to the section (done by
- elf_resolve_sections). */
- void *load_addr; /* A pointer to the allocated destination.
- Done by load_payload_data. */
-};
-
-struct xsplice_elf_sym {
- const Elf_Sym *sym;
- const char *name;
-};
-
-struct xsplice_elf {
- const char *name; /* Pointer to payload->name. */
- size_t len; /* Length of the ELF file. */
- const Elf_Ehdr *hdr; /* ELF file. */
- struct xsplice_elf_sec *sec; /* Array of sections, allocated by us. */
- struct xsplice_elf_sym *sym; /* Array of symbols , allocated by us. */
- unsigned int nsym;
- const struct xsplice_elf_sec *symtab;/* Pointer to .symtab section - aka to
- sec[symtab_idx]. */
- const struct xsplice_elf_sec *strtab;/* Pointer to .strtab section. */
- unsigned int symtab_idx;
-};
-
-const struct xsplice_elf_sec *xsplice_elf_sec_by_name(const struct xsplice_elf *elf,
- const char *name);
-int xsplice_elf_load(struct xsplice_elf *elf, const void *data);
-void xsplice_elf_free(struct xsplice_elf *elf);
-
-int xsplice_elf_resolve_symbols(struct xsplice_elf *elf);
-int xsplice_elf_perform_relocs(struct xsplice_elf *elf);
-
-#endif /* __XEN_XSPLICE_ELF_H__ */
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
case XEN_SYSCTL_get_cpu_featureset:
return domain_has_xen(current->domain, XEN2__GET_CPU_FEATURESET);
- case XEN_SYSCTL_xsplice_op:
+ case XEN_SYSCTL_livepatch_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
- XEN2__XSPLICE_OP, NULL);
+ XEN2__LIVEPATCH_OP, NULL);
default:
printk("flask_sysctl: Unknown op %d\n", cmd);
get_cpu_levelling_caps
# XEN_SYSCTL_get_cpu_featureset
get_cpu_featureset
-# XEN_SYSCTL_xsplice_op
- xsplice_op
+# XEN_SYSCTL_livepatch_op
+ livepatch_op
}
# Classes domain and domain2 consist of operations that a domain performs on