]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
s390/alternatives: Add debug functionality
authorHeiko Carstens <hca@linux.ibm.com>
Fri, 7 Feb 2025 14:49:11 +0000 (15:49 +0100)
committerVasily Gorbik <gor@linux.ibm.com>
Tue, 4 Mar 2025 16:18:08 +0000 (17:18 +0100)
Similar to x86 and loongarch add a "debug-alternative" command line
parameter, which allows for alternative debugging. The parameter
itself comes with architecture specific semantics:

"debug-alternative"
 -> print debug message for every single alternative

"debug-alternative=0;2"
-> print debug message for all alternatives with type 0 and 2

"debug-alternative=0:0-7"
-> print debug message for all alternatives with type 0 which have a
   facility number within the range of 0-7

"debug-alternative=0:!8;1"
-> print debug message for all alternatives with type 0, for all
   facility numbers, except facility 8, and in addition print all
   alternatives with type 1

A defconfig build currently results in a kernel with more than 20.000
alternatives, where the majority is for the niai alternative (spinlocks),
and the relocated lowcore alternative. The following kernel command like
options limit alternative debug output, and enable dynamic debug messages:

debug-alternative=0:!49;1:!0
earlyprintk
bootdebug
ignore_loglevel
loglevel=8
dyndbg="file alternative.c +p"

This results in output like this:

 alt: [0/ 11] 0000021b9ce8680cc0f400000089 -> c00400000000
 alt: [0/ 64] 0000021b9ce87e60c0f400000043 -> c00400000000
 alt: [0/133] 0000021b9ce88c56c0f400000027 -> c00400000000
 alt: [0/ 74] 0000021b9ce89410c0f40000002a -> c00400000000
 alt: [0/ 40] 0000021b9dc3720a47000000 -> b280d398
 alt: [0/193] 0000021b9dc3730647000000 -> b201d2b0
 alt: [0/193] 0000021b9dc37354c00400000000 -> d20720c0d2b0
 alt: [1/  5] 0000038d720d7bf2c0f400000016 -> c00400000000

With

[<alternative type>/<alternative data>] <address> oldcode -> newcode

Alternative data depends on the alternative type: for type 0
(ALT_TYPE_FACILITY) data is the facility. For type 1 (ALT_TYPE_FEATURE)
data is the corresponding machine feature.

Acked-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/boot/alternative.c
arch/s390/boot/boot.h
arch/s390/boot/ipl_parm.c
arch/s390/kernel/alternative.c
arch/s390/kernel/early.c

index abc08d2c873d27d2eb09724f2bec21f31120d34a..19ea7934b9184ae4e1f482fb549b189b2abf20a4 100644 (file)
@@ -1,3 +1,138 @@
 // SPDX-License-Identifier: GPL-2.0
+#define boot_fmt(fmt)  "alt: " fmt
+#include "boot.h"
+
+#define a_debug                boot_debug
 
 #include "../kernel/alternative.c"
+
+static void alt_debug_all(int type)
+{
+       int i;
+
+       switch (type) {
+       case ALT_TYPE_FACILITY:
+               for (i = 0; i < ARRAY_SIZE(alt_debug.facilities); i++)
+                       alt_debug.facilities[i] = -1UL;
+               break;
+       case ALT_TYPE_FEATURE:
+               for (i = 0; i < ARRAY_SIZE(alt_debug.mfeatures); i++)
+                       alt_debug.mfeatures[i] = -1UL;
+               break;
+       case ALT_TYPE_SPEC:
+               alt_debug.spec = 1;
+               break;
+       }
+}
+
+static void alt_debug_modify(int type, unsigned int nr, bool clear)
+{
+       switch (type) {
+       case ALT_TYPE_FACILITY:
+               if (clear)
+                       __clear_facility(nr, alt_debug.facilities);
+               else
+                       __set_facility(nr, alt_debug.facilities);
+               break;
+       case ALT_TYPE_FEATURE:
+               if (clear)
+                       __clear_machine_feature(nr, alt_debug.mfeatures);
+               else
+                       __set_machine_feature(nr, alt_debug.mfeatures);
+               break;
+       }
+}
+
+static char *alt_debug_parse(int type, char *str)
+{
+       unsigned long val, endval;
+       char *endp;
+       bool clear;
+       int i;
+
+       if (*str == ':') {
+               str++;
+       } else {
+               alt_debug_all(type);
+               return str;
+       }
+       clear = false;
+       if (*str == '!') {
+               alt_debug_all(type);
+               clear = true;
+               str++;
+       }
+       while (*str) {
+               val = simple_strtoull(str, &endp, 0);
+               if (str == endp)
+                       break;
+               str = endp;
+               if (*str == '-') {
+                       str++;
+                       endval = simple_strtoull(str, &endp, 0);
+                       if (str == endp)
+                               break;
+                       str = endp;
+                       while (val <= endval) {
+                               alt_debug_modify(type, val, clear);
+                               val++;
+                       }
+               } else {
+                       alt_debug_modify(type, val, clear);
+               }
+               if (*str != ',')
+                       break;
+               str++;
+       }
+       return str;
+}
+
+/*
+ * Use debug-alternative command line parameter for debugging:
+ * "debug-alternative"
+ *  -> print debug message for every single alternative
+ *
+ * "debug-alternative=0;2"
+ * -> print debug message for all alternatives with type 0 and 2
+ *
+ * "debug-alternative=0:0-7"
+ * -> print debug message for all alternatives with type 0 and with
+ *    facility numbers within the range of 0-7
+ *    (if type 0 is ALT_TYPE_FACILITY)
+ *
+ * "debug-alternative=0:!8;1"
+ * -> print debug message for all alternatives with type 0, for all
+ *    facility number, except facility 8, and in addition print all
+ *    alternatives with type 1
+ */
+void alt_debug_setup(char *str)
+{
+       unsigned long type;
+       char *endp;
+       int i;
+
+       if (!str) {
+               alt_debug_all(ALT_TYPE_FACILITY);
+               alt_debug_all(ALT_TYPE_FEATURE);
+               alt_debug_all(ALT_TYPE_SPEC);
+               return;
+       }
+       while (*str) {
+               type = simple_strtoull(str, &endp, 0);
+               if (str == endp)
+                       break;
+               str = endp;
+               switch (type) {
+               case ALT_TYPE_FACILITY:
+               case ALT_TYPE_FEATURE:
+                       str = alt_debug_parse(type, str);
+                       break;
+               case ALT_TYPE_SPEC:
+                       alt_debug_all(ALT_TYPE_SPEC);
+                       break;
+               }
+               if (*str != ';')
+                       break;
+               str++;
+       }
+}
index f4af23c142cc1d8e079e930916b970a0c8cd731d..bb6bd9d6d99111357580825ea18e6be0c406e049 100644 (file)
@@ -64,6 +64,7 @@ void parse_boot_command_line(void);
 void verify_facilities(void);
 void print_missing_facilities(void);
 void sclp_early_setup_buffer(void);
+void alt_debug_setup(char *str);
 void print_pgm_check_info(void);
 unsigned long randomize_within_range(unsigned long size, unsigned long align,
                                     unsigned long min, unsigned long max);
index 13316bfa589c01402f8e61236c9214e1b89f598c..86cfb16dd63da2d94c252bf421f1bba812917490 100644 (file)
@@ -296,6 +296,9 @@ void parse_boot_command_line(void)
                if (!strcmp(param, "facilities") && val)
                        modify_fac_list(val);
 
+               if (!strcmp(param, "debug-alternative"))
+                       alt_debug_setup(val);
+
                if (!strcmp(param, "nokaslr"))
                        __kaslr_enabled = 0;
 
index 2f56649bbde8707643f0351e21609ec697bb8ce0..90c0e6408992f5967e5dade35ae22a8dff98179c 100644 (file)
@@ -1,6 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
 
+#ifndef pr_fmt
+#define pr_fmt(fmt)    "alt: " fmt
+#endif
+
 #include <linux/uaccess.h>
+#include <linux/printk.h>
 #include <asm/nospec-branch.h>
 #include <asm/abs_lowcore.h>
 #include <asm/alternative.h>
 #include <asm/sections.h>
 #include <asm/machine.h>
 
+#ifndef a_debug
+#define a_debug                pr_debug
+#endif
+
+#ifndef __kernel_va
+#define __kernel_va(x) (void *)(x)
+#endif
+
 unsigned long __bootdata_preserved(machine_features[1]);
 
+struct alt_debug {
+       unsigned long facilities[MAX_FACILITY_BIT / BITS_PER_LONG];
+       unsigned long mfeatures[MAX_MFEATURE_BIT / BITS_PER_LONG];
+       int spec;
+};
+
+static struct alt_debug __bootdata_preserved(alt_debug);
+
+static void alternative_dump(u8 *old, u8 *new, unsigned int len, unsigned int type, unsigned int data)
+{
+       char oinsn[33], ninsn[33];
+       unsigned long kptr;
+       unsigned int pos;
+
+       for (pos = 0; pos < len && 2 * pos < sizeof(oinsn) - 3; pos++)
+               hex_byte_pack(&oinsn[2 * pos], old[pos]);
+       oinsn[2 * pos] = 0;
+       for (pos = 0; pos < len && 2 * pos < sizeof(ninsn) - 3; pos++)
+               hex_byte_pack(&ninsn[2 * pos], new[pos]);
+       ninsn[2 * pos] = 0;
+       kptr = (unsigned long)__kernel_va(old);
+       a_debug("[%d/%3d] %016lx: %s -> %s\n", type, data, kptr, oinsn, ninsn);
+}
+
 void __apply_alternatives(struct alt_instr *start, struct alt_instr *end, unsigned int ctx)
 {
-       u8 *instr, *replacement;
+       struct alt_debug *d;
        struct alt_instr *a;
-       bool replace;
+       bool debug, replace;
+       u8 *old, *new;
 
        /*
         * The scan order should be from start to end. A later scanned
         * alternative code can overwrite previously scanned alternative code.
         */
+       d = &alt_debug;
        for (a = start; a < end; a++) {
                if (!(a->ctx & ctx))
                        continue;
                switch (a->type) {
                case ALT_TYPE_FACILITY:
                        replace = test_facility(a->data);
+                       debug = __test_facility(a->data, d->facilities);
                        break;
                case ALT_TYPE_FEATURE:
                        replace = test_machine_feature(a->data);
+                       debug = __test_machine_feature(a->data, d->mfeatures);
                        break;
                case ALT_TYPE_SPEC:
                        replace = nobp_enabled();
+                       debug = d->spec;
                        break;
                default:
                        replace = false;
+                       debug = false;
                }
                if (!replace)
                        continue;
-               instr = (u8 *)&a->instr_offset + a->instr_offset;
-               replacement = (u8 *)&a->repl_offset + a->repl_offset;
-               s390_kernel_write(instr, replacement, a->instrlen);
+               old = (u8 *)&a->instr_offset + a->instr_offset;
+               new = (u8 *)&a->repl_offset + a->repl_offset;
+               if (debug)
+                       alternative_dump(old, new, a->instrlen, a->type, a->data);
+               s390_kernel_write(old, new, a->instrlen);
        }
 }
index 8d0291c2b68c6a521e1462cf620beeace5ccd16c..b6d3c7a6209db940e3a995b596904a245ebf9712 100644 (file)
@@ -55,6 +55,7 @@ decompressor_handled_param(nokaslr);
 decompressor_handled_param(cmma);
 decompressor_handled_param(relocate_lowcore);
 decompressor_handled_param(bootdebug);
+__decompressor_handled_param(debug_alternative, debug-alternative);
 #if IS_ENABLED(CONFIG_KVM)
 decompressor_handled_param(prot_virt);
 #endif