|
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | +/* |
| 3 | + * kernel/rh_waived.c |
| 4 | + * |
| 5 | + * rh_waived cmdline parameter support. |
| 6 | + * |
| 7 | + * Copyright (C) 2024, Red Hat, Inc. Ricardo Robaina <rrobaina@redhat.com> |
| 8 | + */ |
| 9 | +#include <linux/types.h> |
| 10 | +#include <linux/init.h> |
| 11 | +#include <linux/printk.h> |
| 12 | +#include <linux/string.h> |
| 13 | +#include <linux/panic.h> |
| 14 | +#include <linux/module.h> |
| 15 | +#include <linux/kernel.h> |
| 16 | +#include <linux/rh_flags.h> |
| 17 | +#include <linux/rh_waived.h> |
| 18 | + |
| 19 | +/* |
| 20 | + * * RH_INSERT_WAIVED_ITEM |
| 21 | + * This macro is intended to be used to insert items into the |
| 22 | + * rh_waived_list array. It expects to get an item from |
| 23 | + * enum rh_waived_items as its first argument, and a string |
| 24 | + * holding the feature name as its second argument. |
| 25 | + * |
| 26 | + * The feature name is also utilized as the token for the |
| 27 | + * boot parameter parser. |
| 28 | + * |
| 29 | + * Example usage: |
| 30 | + * struct rh_waived_item foo[RH_WAIVED_FEAT_ITEMS] = { |
| 31 | + * RH_INSERT_WAIVED_ITEM(FOO_FEAT, "foo_feat_short_str", "alias", RH_WAIVED_FEAT), |
| 32 | + * }; |
| 33 | + */ |
| 34 | +#define RH_INSERT_WAIVED_ITEM(enum_item, item, item_alt, class) \ |
| 35 | + [(enum_item)] = { .name = (item), .alias = (item_alt), \ |
| 36 | + .type = (class), .waived = 0, } |
| 37 | + |
| 38 | +/* Indicates if the rh_flag 'rh_waived' should be added. */ |
| 39 | +bool __initdata add_rh_flag = false; |
| 40 | + |
| 41 | +typedef enum { |
| 42 | + RH_WAIVED_FEAT, |
| 43 | + RH_WAIVED_CVE, |
| 44 | + RH_WAIVED_ANY |
| 45 | +} rh_waived_t; |
| 46 | + |
| 47 | +struct rh_waived_item { |
| 48 | + char *name, *alias; |
| 49 | + rh_waived_t type; |
| 50 | + unsigned int waived; |
| 51 | + |
| 52 | +}; |
| 53 | + |
| 54 | +/* Always use the marco RH_INSERT_WAIVED to insert items to this array. */ |
| 55 | +struct rh_waived_item rh_waived_list[RH_WAIVED_ITEMS] = { |
| 56 | +}; |
| 57 | + |
| 58 | +/* |
| 59 | + * is_rh_waived() - Checks if a given item has been marked as waived. |
| 60 | + * |
| 61 | + * @item: waived item. |
| 62 | + */ |
| 63 | +__inline__ bool is_rh_waived(enum rh_waived_items item) |
| 64 | +{ |
| 65 | + return !!rh_waived_list[item].waived; |
| 66 | +} |
| 67 | +EXPORT_SYMBOL(is_rh_waived); |
| 68 | + |
| 69 | +static void __init rh_waived_parser(char *s, rh_waived_t type) |
| 70 | +{ |
| 71 | + int i; |
| 72 | + char *token; |
| 73 | + |
| 74 | + pr_info(KERN_CONT "rh_waived: "); |
| 75 | + |
| 76 | + if (!s) { |
| 77 | + for (i = 0; i < RH_WAIVED_ITEMS; i++) { |
| 78 | + if (type != RH_WAIVED_ANY && rh_waived_list[i].type != type) |
| 79 | + continue; |
| 80 | + |
| 81 | + rh_waived_list[i].waived = 1; |
| 82 | + pr_info(KERN_CONT "%s%s", rh_waived_list[i].name, |
| 83 | + i < RH_WAIVED_ITEMS - 1 ? " " : "\n"); |
| 84 | + } |
| 85 | + |
| 86 | + add_rh_flag = true; |
| 87 | + return; |
| 88 | + } |
| 89 | + |
| 90 | + while ((token = strsep(&s, ",")) != NULL) { |
| 91 | + for (i = 0; i < RH_WAIVED_ITEMS; i++) { |
| 92 | + char *alias = rh_waived_list[i].alias; |
| 93 | + |
| 94 | + if (type != RH_WAIVED_ANY && rh_waived_list[i].type != type) |
| 95 | + continue; |
| 96 | + |
| 97 | + if (!strcmp(token, rh_waived_list[i].name) || |
| 98 | + (alias && !strcmp(token, alias))) { |
| 99 | + rh_waived_list[i].waived = 1; |
| 100 | + pr_info(KERN_CONT "%s ", rh_waived_list[i].name); |
| 101 | + } |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + pr_info(KERN_CONT "\n"); |
| 106 | + add_rh_flag = true; |
| 107 | +} |
| 108 | + |
| 109 | +static int __init rh_waived_setup(char *s) |
| 110 | +{ |
| 111 | + /* |
| 112 | + * originally, if no string was passed to the cmdline option |
| 113 | + * all listed features would be waived, so we keep that same |
| 114 | + * compromise with the new contract. |
| 115 | + */ |
| 116 | + if (!s || !strcmp(s, "features")) { |
| 117 | + rh_waived_parser(NULL, RH_WAIVED_FEAT); |
| 118 | + return 0; |
| 119 | + } |
| 120 | + |
| 121 | + /* waive all possible mitigations in the list */ |
| 122 | + if (!strcmp(s, "cves")) { |
| 123 | + rh_waived_parser(NULL, RH_WAIVED_CVE); |
| 124 | + return 0; |
| 125 | + } |
| 126 | + |
| 127 | + /* otherwise, just deal with the enumerated waive list */ |
| 128 | + rh_waived_parser(s, RH_WAIVED_ANY); |
| 129 | + |
| 130 | + return 0; |
| 131 | +} |
| 132 | +early_param("rh_waived", rh_waived_setup); |
| 133 | + |
| 134 | +/* |
| 135 | + * rh_flags is initialized at subsys_initcall, calling rh_add_flag() |
| 136 | + * from rh_waived_setup() would result in a can't boot situation. |
| 137 | + * Deffering the inclusion 'rh_waived' rh_flag to late_initcall to |
| 138 | + * avoid this issue. |
| 139 | + */ |
| 140 | +static int __init __add_rh_flag(void) |
| 141 | +{ |
| 142 | + if (add_rh_flag) |
| 143 | + rh_add_flag("rh_waived"); |
| 144 | + |
| 145 | + return 0; |
| 146 | +} |
| 147 | +late_initcall(__add_rh_flag); |
0 commit comments