Skip to content

Instantly share code, notes, and snippets.

@glandium
Last active February 17, 2024 20:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save glandium/01d54cefdb70561b5f6675e08f2990f2 to your computer and use it in GitHub Desktop.
Save glandium/01d54cefdb70561b5f6675e08f2990f2 to your computer and use it in GitHub Desktop.
Linux kernel module for Zen workaround for rr
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");
@glandium
Copy link
Author

@eddyb
Copy link

eddyb commented Sep 19, 2020

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)

@glandium
Copy link
Author

Latest version avoids requiring internal kernel symbols altogether.

@eddyb
Copy link

eddyb commented Sep 21, 2020

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.

@lissyx
Copy link

lissyx commented May 31, 2022

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

@bgamari
Copy link

bgamari commented Oct 21, 2022

I have packaged this into a NixOS module.

@lissyx
Copy link

lissyx commented Nov 6, 2023

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)

@glandium
Copy link
Author

glandium commented Nov 6, 2023

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.

@symphorien
Copy link

Could this module be moved to a proper repository? this would make it easier to track updates.

@glandium
Copy link
Author

Maybe it should just be in the rr repo. Open an issue?

@symphorien
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment