linux/kernel/livepatch/state.c
<<
>>
Prefs
   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 */
  31struct 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}
  42EXPORT_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 */
  64struct 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
  81out:
  82        return last_state;
  83}
  84EXPORT_SYMBOL_GPL(klp_get_prev_state);
  85
  86/* Check if the patch is able to deal with the existing system state. */
  87static 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 */
 106bool 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