-
-
Save glandium/01d54cefdb70561b5f6675e08f2990f2 to your computer and use it in GitHub Desktop.
obj-m = zen_workaround.o | |
all: | |
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules | |
clean: | |
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean |
#include <linux/module.h> | |
#include <linux/tracepoint.h> | |
#include <linux/suspend.h> | |
#define MODULE_NAME "zen_workaround" | |
#define SPECLOCKMAP_DISABLE BIT_64(54) | |
u64 set_speclockmap_disable(u64 msr) { | |
return msr | SPECLOCKMAP_DISABLE; | |
} | |
u64 unset_speclockmap_disable(u64 msr) { | |
return msr & ~SPECLOCKMAP_DISABLE; | |
} | |
typedef u64 (*edit_msr_func_t)(u64); | |
static void edit_ls_cfg_on_cpu(void *info) | |
{ | |
int cpu = get_cpu(); | |
u64 value = 0; | |
if (!rdmsrl_safe(MSR_AMD64_LS_CFG, &value)) { | |
edit_msr_func_t edit_msr = (edit_msr_func_t) info; | |
u64 new_value = edit_msr(value); | |
if (!wrmsrl_safe(MSR_AMD64_LS_CFG, new_value)) { | |
pr_info("MSR_AMD64_LS_CFG for cpu %d was 0x%llx, setting to 0x%llx\n", | |
cpu, value, new_value); | |
} else { | |
pr_err("MSR_AMD64_LS_CFG for cpu %d was 0x%llx, setting to 0x%llx failed\n", | |
cpu, value, new_value); | |
} | |
} | |
put_cpu(); | |
} | |
static void do_zen_workaround(edit_msr_func_t edit_msr) | |
{ | |
smp_call_function(edit_ls_cfg_on_cpu, edit_msr, 1); | |
edit_ls_cfg_on_cpu(edit_msr); | |
} | |
void on_write_msr(void *data, unsigned int msr, u64 val, int failed) | |
{ | |
if (msr == MSR_AMD64_LS_CFG && !(val & SPECLOCKMAP_DISABLE)) { | |
native_wrmsrl(MSR_AMD64_LS_CFG, set_speclockmap_disable(val)); | |
} | |
} | |
static int install_probe(void) | |
{ | |
return !boot_cpu_has(X86_FEATURE_AMD_SSBD) && !boot_cpu_has(X86_FEATURE_VIRT_SSBD); | |
} | |
static int enable_zen_workaround(void) | |
{ | |
if (install_probe()) { | |
int ret = tracepoint_probe_register(&__tracepoint_write_msr, on_write_msr, NULL); | |
if (ret) { | |
pr_err("Registering tracepoint probe failed\n"); | |
return ret; | |
} | |
} | |
do_zen_workaround(set_speclockmap_disable); | |
return 0; | |
} | |
static int pm_notification(struct notifier_block *this, unsigned long event, void *ptr) | |
{ | |
switch (event) { | |
case PM_POST_SUSPEND: | |
case PM_POST_HIBERNATION: | |
case PM_POST_RESTORE: | |
enable_zen_workaround(); | |
break; | |
case PM_HIBERNATION_PREPARE: | |
case PM_SUSPEND_PREPARE: | |
if (install_probe()) { | |
tracepoint_probe_unregister(&__tracepoint_write_msr, on_write_msr, NULL); | |
} | |
break; | |
} | |
return NOTIFY_DONE; | |
} | |
static struct notifier_block pm_notifier = { | |
.notifier_call = pm_notification, | |
}; | |
static int __init zen_workaround_init(void) | |
{ | |
if (!boot_cpu_has(X86_FEATURE_ZEN)) { | |
pr_err("Cannot use the Zen workaround on a non-Zen CPU\n"); | |
return -EINVAL; | |
} | |
enable_zen_workaround(); | |
register_pm_notifier(&pm_notifier); | |
return 0; | |
} | |
module_init(zen_workaround_init); | |
static void __exit zen_workaround_exit(void) | |
{ | |
unregister_pm_notifier(&pm_notifier); | |
if (install_probe()) { | |
tracepoint_probe_unregister(&__tracepoint_write_msr, on_write_msr, NULL); | |
} | |
do_zen_workaround(unset_speclockmap_disable); | |
} | |
module_exit(zen_workaround_exit); | |
MODULE_LICENSE("GPL"); |
outdated comment, click to open
(only applies to https://gist.github.com/glandium/01d54cefdb70561b5f6675e08f2990f2/6147e24ad62ba3b3023eabb6aedfd0bd592839da)
Note that you could make this work using System.map
for all symbols as long as you can import at least one symbol directly, I only used kallsyms_lookup_name
for convenience.
EDIT: more details in rr-debugger/rr#2034 (comment)
Latest version avoids requiring internal kernel symbols altogether.
On NixOS, as per https://nixos.wiki/wiki/Linux_kernel#Developing_kernel_modules, one should be able to just:
make -C $(nix-build -E '(import <nixpkgs> {}).linux.dev' --no-out-link)/lib/modules/*/build M=$(pwd) modules
The Makefile
in the gist is fine to use because the obj-m = zen_workaround.o
line will be used and the rest ignored.
In case you have SecureBoot enabled (tested on Ubuntu):
sign: $(obj-m)
/lib/modules/$(shell uname -r)/build/scripts/sign-file sha512 /var/lib/shim-signed/mok/MOK.priv /var/lib/shim-signed/mok/MOK.der zen_workaround.ko
I have packaged this into a NixOS module.
Kernel 6.5.8 on Ubuntu 23.10:
[ 9058.474358] BUG: scheduling while atomic: swapper/15/0/0x00000002
[ 9058.474360] Modules linked in:
[ 9058.474360] BUG: scheduling while atomic: swapper/5/0/0x00000002
[ 9058.474361] zen_workaround(O+)
[ 9058.474363] Modules linked in: zen_workaround(O+)
Fix:
--- zen_workaround.c.orig 2023-11-06 16:37:23.000582213 +0100
+++ zen_workaround.c 2023-11-06 16:37:24.628603272 +0100
@@ -32,6 +32,8 @@
cpu, value, new_value);
}
}
+
+ put_cpu();
}
static void do_zen_workaround(edit_msr_func_t edit_msr)
Fix:
--- zen_workaround.c.orig 2023-11-06 16:37:23.000582213 +0100 +++ zen_workaround.c 2023-11-06 16:37:24.628603272 +0100 @@ -32,6 +32,8 @@ cpu, value, new_value); } } + + put_cpu(); } static void do_zen_workaround(edit_msr_func_t edit_msr)
Applied. Thanks.
Could this module be moved to a proper repository? this would make it easier to track updates.
Maybe it should just be in the rr repo. Open an issue?
Inspired by https://gist.github.com/eddyb/b888bb87988ca97ead9abcf96aa49e15