173727f4dSPetr Mladek // SPDX-License-Identifier: GPL-2.0-or-later
273727f4dSPetr Mladek /*
373727f4dSPetr Mladek * system_state.c - State of the system modified by livepatches
473727f4dSPetr Mladek *
573727f4dSPetr Mladek * Copyright (C) 2019 SUSE
673727f4dSPetr Mladek */
773727f4dSPetr Mladek
873727f4dSPetr Mladek #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
973727f4dSPetr Mladek
1073727f4dSPetr Mladek #include <linux/livepatch.h>
1173727f4dSPetr Mladek #include "core.h"
1292c9abf5SPetr Mladek #include "state.h"
1373727f4dSPetr Mladek #include "transition.h"
1473727f4dSPetr Mladek
1573727f4dSPetr Mladek #define klp_for_each_state(patch, state) \
1673727f4dSPetr Mladek for (state = patch->states; state && state->id; state++)
1773727f4dSPetr Mladek
1873727f4dSPetr Mladek /**
1973727f4dSPetr Mladek * klp_get_state() - get information about system state modified by
2073727f4dSPetr Mladek * the given patch
2173727f4dSPetr Mladek * @patch: livepatch that modifies the given system state
2273727f4dSPetr Mladek * @id: custom identifier of the modified system state
2373727f4dSPetr Mladek *
2473727f4dSPetr Mladek * Checks whether the given patch modifies the given system state.
2573727f4dSPetr Mladek *
2673727f4dSPetr Mladek * The function can be called either from pre/post (un)patch
2773727f4dSPetr Mladek * callbacks or from the kernel code added by the livepatch.
2873727f4dSPetr Mladek *
2973727f4dSPetr Mladek * Return: pointer to struct klp_state when found, otherwise NULL.
3073727f4dSPetr Mladek */
klp_get_state(struct klp_patch * patch,unsigned long id)3173727f4dSPetr Mladek struct klp_state *klp_get_state(struct klp_patch *patch, unsigned long id)
3273727f4dSPetr Mladek {
3373727f4dSPetr Mladek struct klp_state *state;
3473727f4dSPetr Mladek
3573727f4dSPetr Mladek klp_for_each_state(patch, state) {
3673727f4dSPetr Mladek if (state->id == id)
3773727f4dSPetr Mladek return state;
3873727f4dSPetr Mladek }
3973727f4dSPetr Mladek
4073727f4dSPetr Mladek return NULL;
4173727f4dSPetr Mladek }
4273727f4dSPetr Mladek EXPORT_SYMBOL_GPL(klp_get_state);
4373727f4dSPetr Mladek
4473727f4dSPetr Mladek /**
4573727f4dSPetr Mladek * klp_get_prev_state() - get information about system state modified by
4673727f4dSPetr Mladek * the already installed livepatches
4773727f4dSPetr Mladek * @id: custom identifier of the modified system state
4873727f4dSPetr Mladek *
4973727f4dSPetr Mladek * Checks whether already installed livepatches modify the given
5073727f4dSPetr Mladek * system state.
5173727f4dSPetr Mladek *
5273727f4dSPetr Mladek * The same system state can be modified by more non-cumulative
5373727f4dSPetr Mladek * livepatches. It is expected that the latest livepatch has
5473727f4dSPetr Mladek * the most up-to-date information.
5573727f4dSPetr Mladek *
5673727f4dSPetr Mladek * The function can be called only during transition when a new
5773727f4dSPetr Mladek * livepatch is being enabled or when such a transition is reverted.
58*7b7b8a2cSRandy Dunlap * It is typically called only from pre/post (un)patch
5973727f4dSPetr Mladek * callbacks.
6073727f4dSPetr Mladek *
6173727f4dSPetr Mladek * Return: pointer to the latest struct klp_state from already
6273727f4dSPetr Mladek * installed livepatches, NULL when not found.
6373727f4dSPetr Mladek */
klp_get_prev_state(unsigned long id)6473727f4dSPetr Mladek struct klp_state *klp_get_prev_state(unsigned long id)
6573727f4dSPetr Mladek {
6673727f4dSPetr Mladek struct klp_patch *patch;
6773727f4dSPetr Mladek struct klp_state *state, *last_state = NULL;
6873727f4dSPetr Mladek
6973727f4dSPetr Mladek if (WARN_ON_ONCE(!klp_transition_patch))
7073727f4dSPetr Mladek return NULL;
7173727f4dSPetr Mladek
7273727f4dSPetr Mladek klp_for_each_patch(patch) {
7373727f4dSPetr Mladek if (patch == klp_transition_patch)
7473727f4dSPetr Mladek goto out;
7573727f4dSPetr Mladek
7673727f4dSPetr Mladek state = klp_get_state(patch, id);
7773727f4dSPetr Mladek if (state)
7873727f4dSPetr Mladek last_state = state;
7973727f4dSPetr Mladek }
8073727f4dSPetr Mladek
8173727f4dSPetr Mladek out:
8273727f4dSPetr Mladek return last_state;
8373727f4dSPetr Mladek }
8473727f4dSPetr Mladek EXPORT_SYMBOL_GPL(klp_get_prev_state);
8592c9abf5SPetr Mladek
8692c9abf5SPetr Mladek /* Check if the patch is able to deal with the existing system state. */
klp_is_state_compatible(struct klp_patch * patch,struct klp_state * old_state)8792c9abf5SPetr Mladek static bool klp_is_state_compatible(struct klp_patch *patch,
8892c9abf5SPetr Mladek struct klp_state *old_state)
8992c9abf5SPetr Mladek {
9092c9abf5SPetr Mladek struct klp_state *state;
9192c9abf5SPetr Mladek
9292c9abf5SPetr Mladek state = klp_get_state(patch, old_state->id);
9392c9abf5SPetr Mladek
9492c9abf5SPetr Mladek /* A cumulative livepatch must handle all already modified states. */
9592c9abf5SPetr Mladek if (!state)
9692c9abf5SPetr Mladek return !patch->replace;
9792c9abf5SPetr Mladek
9892c9abf5SPetr Mladek return state->version >= old_state->version;
9992c9abf5SPetr Mladek }
10092c9abf5SPetr Mladek
10192c9abf5SPetr Mladek /*
10292c9abf5SPetr Mladek * Check that the new livepatch will not break the existing system states.
10392c9abf5SPetr Mladek * Cumulative patches must handle all already modified states.
10492c9abf5SPetr Mladek * Non-cumulative patches can touch already modified states.
10592c9abf5SPetr Mladek */
klp_is_patch_compatible(struct klp_patch * patch)10692c9abf5SPetr Mladek bool klp_is_patch_compatible(struct klp_patch *patch)
10792c9abf5SPetr Mladek {
10892c9abf5SPetr Mladek struct klp_patch *old_patch;
10992c9abf5SPetr Mladek struct klp_state *old_state;
11092c9abf5SPetr Mladek
11192c9abf5SPetr Mladek klp_for_each_patch(old_patch) {
11292c9abf5SPetr Mladek klp_for_each_state(old_patch, old_state) {
11392c9abf5SPetr Mladek if (!klp_is_state_compatible(patch, old_state))
11492c9abf5SPetr Mladek return false;
11592c9abf5SPetr Mladek }
11692c9abf5SPetr Mladek }
11792c9abf5SPetr Mladek
11892c9abf5SPetr Mladek return true;
11992c9abf5SPetr Mladek }
120