Skip to content

Instantly share code, notes, and snippets.

@drvink
Forked from glandium/Makefile
Created June 4, 2022 03:06
Show Gist options
  • Select an option

  • Save drvink/7fc4ff8698bd1f55c56ebe40bb20c644 to your computer and use it in GitHub Desktop.

Select an option

Save drvink/7fc4ff8698bd1f55c56ebe40bb20c644 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/kallsyms.h>
#define MODULE_NAME "zen_workaround"
typedef int set_memory_fn_t(unsigned long, int);
unsigned long need_symbol(const char *name)
{
unsigned long sym = kallsyms_lookup_name(name);
if (sym) {
pr_info("%s is 0x%lx\n", name, sym);
} else {
pr_err("could not find kernel symbol %s\n", name);
}
return sym;
}
static set_memory_fn_t *set_memory_ro, *set_memory_rw;
static _Atomic u64 *x86_amd_ls_cfg_base_ptr;
static _Atomic int *kernel_set_to_readonly_ptr;
u64 set_speclockmap_disable(u64 msr) {
return msr | BIT_64(54);
}
u64 unset_speclockmap_disable(u64 msr) {
return msr & ~BIT_64(54);
}
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);
}
}
}
static int do_zen_workaround(edit_msr_func_t edit_msr)
{
u64 old_value = *x86_amd_ls_cfg_base_ptr;
if (old_value) {
int ret;
u64 new_value = edit_msr(old_value);
*kernel_set_to_readonly_ptr = 0;
ret = set_memory_rw((unsigned long)x86_amd_ls_cfg_base_ptr, 1);
*kernel_set_to_readonly_ptr = 1;
if (ret) {
pr_err("set_memory_rw failed with %d\n", ret);
return -EPERM;
}
*x86_amd_ls_cfg_base_ptr = new_value;
pr_info("x86_amd_ls_cfg_base was 0x%llx, setting to 0x%llx\n", old_value, new_value);
*kernel_set_to_readonly_ptr = 0;
ret = set_memory_ro((unsigned long)x86_amd_ls_cfg_base_ptr, 1);
*kernel_set_to_readonly_ptr = 1;
if (ret) {
pr_err("set_memory_ro failed with %d\n", ret);
// Not returning early because we still presumably set x86_amd_ls_cfg_base.
}
} else {
pr_info("x86_amd_ls_cfg_base is unused.");
}
smp_call_function(edit_ls_cfg_on_cpu, edit_msr, 1);
edit_ls_cfg_on_cpu(edit_msr);
return 0;
}
static int __init zen_workaround_init(void)
{
if (!static_cpu_has(X86_FEATURE_ZEN)) {
pr_err("Cannot use the Zen workaround on a non-Zen CPU\n");
return -EINVAL;
}
set_memory_ro = (set_memory_fn_t*) need_symbol("set_memory_ro");
set_memory_rw = (set_memory_fn_t*) need_symbol("set_memory_rw");
x86_amd_ls_cfg_base_ptr = (_Atomic u64*) need_symbol("x86_amd_ls_cfg_base");
kernel_set_to_readonly_ptr = (_Atomic int*) need_symbol("kernel_set_to_readonly");
if (!set_memory_ro || !set_memory_rw || !x86_amd_ls_cfg_base_ptr ||
!kernel_set_to_readonly_ptr) {
return -ENOENT;
}
return do_zen_workaround(set_speclockmap_disable);
}
module_init(zen_workaround_init);
static void __exit zen_workaround_exit(void)
{
do_zen_workaround(unset_speclockmap_disable);
}
module_exit(zen_workaround_exit)
MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment