1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * system_state.c - State of the system modified by livepatches 4 * 5 * Copyright (C) 2019 SUSE 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/livepatch.h> 11 #include "core.h" 12 #include "state.h" 13 #include "transition.h" 14 15 #define klp_for_each_state(patch, state) \ 16 for (state = patch->states; state && state->id; state++) 17 18 /** 19 * klp_get_state() - get information about system state modified by 20 * the given patch 21 * @patch: livepatch that modifies the given system state 22 * @id: custom identifier of the modified system state 23 * 24 * Checks whether the given patch modifies the given system state. 25 * 26 * The function can be called either from pre/post (un)patch 27 * callbacks or from the kernel code added by the livepatch. 28 * 29 * Return: pointer to struct klp_state when found, otherwise NULL. 30 */ 31 struct klp_state *klp_get_state(struct klp_patch *patch, unsigned long id) 32 { 33 struct klp_state *state; 34 35 klp_for_each_state(patch, state) { 36 if (state->id == id) 37 return state; 38 } 39 40 return NULL; 41 } 42 EXPORT_SYMBOL_GPL(klp_get_state); 43 44 /** 45 * klp_get_prev_state() - get information about system state modified by 46 * the already installed livepatches 47 * @id: custom identifier of the modified system state 48 * 49 * Checks whether already installed livepatches modify the given 50 * system state. 51 * 52 * The same system state can be modified by more non-cumulative 53 * livepatches. It is expected that the latest livepatch has 54 * the most up-to-date information. 55 * 56 * The function can be called only during transition when a new 57 * livepatch is being enabled or when such a transition is reverted. 58 * It is typically called only from pre/post (un)patch 59 * callbacks. 60 * 61 * Return: pointer to the latest struct klp_state from already 62 * installed livepatches, NULL when not found. 63 */ 64 struct klp_state *klp_get_prev_state(unsigned long id) 65 { 66 struct klp_patch *patch; 67 struct klp_state *state, *last_state = NULL; 68 69 if (WARN_ON_ONCE(!klp_transition_patch)) 70 return NULL; 71 72 klp_for_each_patch(patch) { 73 if (patch == klp_transition_patch) 74 goto out; 75 76 state = klp_get_state(patch, id); 77 if (state) 78 last_state = state; 79 } 80 81 out: 82 return last_state; 83 } 84 EXPORT_SYMBOL_GPL(klp_get_prev_state); 85 86 /* Check if the patch is able to deal with the existing system state. */ 87 static bool klp_is_state_compatible(struct klp_patch *patch, 88 struct klp_state *old_state) 89 { 90 struct klp_state *state; 91 92 state = klp_get_state(patch, old_state->id); 93 94 /* A cumulative livepatch must handle all already modified states. */ 95 if (!state) 96 return !patch->replace; 97 98 return state->version >= old_state->version; 99 } 100 101 /* 102 * Check that the new livepatch will not break the existing system states. 103 * Cumulative patches must handle all already modified states. 104 * Non-cumulative patches can touch already modified states. 105 */ 106 bool klp_is_patch_compatible(struct klp_patch *patch) 107 { 108 struct klp_patch *old_patch; 109 struct klp_state *old_state; 110 111 klp_for_each_patch(old_patch) { 112 klp_for_each_state(old_patch, old_state) { 113 if (!klp_is_state_compatible(patch, old_state)) 114 return false; 115 } 116 } 117 118 return true; 119 } 120
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.