| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* ----------------------------------------------------------------------- * |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright 2000-2008 H. Peter Anvin - All Rights Reserved |
|---|
| 4 | 5 | * Copyright 2009 Intel Corporation; author: H. Peter Anvin |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 8 | | - * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, |
|---|
| 9 | | - * USA; either version 2 of the License, or (at your option) any later |
|---|
| 10 | | - * version; incorporated herein by reference. |
|---|
| 11 | 6 | * |
|---|
| 12 | 7 | * ----------------------------------------------------------------------- */ |
|---|
| 13 | 8 | |
|---|
| .. | .. |
|---|
| 39 | 34 | #include <linux/notifier.h> |
|---|
| 40 | 35 | #include <linux/uaccess.h> |
|---|
| 41 | 36 | #include <linux/gfp.h> |
|---|
| 37 | +#include <linux/security.h> |
|---|
| 42 | 38 | |
|---|
| 43 | 39 | #include <asm/cpufeature.h> |
|---|
| 44 | 40 | #include <asm/msr.h> |
|---|
| 45 | 41 | |
|---|
| 46 | 42 | static struct class *msr_class; |
|---|
| 47 | 43 | static enum cpuhp_state cpuhp_msr_state; |
|---|
| 44 | + |
|---|
| 45 | +enum allow_write_msrs { |
|---|
| 46 | + MSR_WRITES_ON, |
|---|
| 47 | + MSR_WRITES_OFF, |
|---|
| 48 | + MSR_WRITES_DEFAULT, |
|---|
| 49 | +}; |
|---|
| 50 | + |
|---|
| 51 | +static enum allow_write_msrs allow_writes = MSR_WRITES_DEFAULT; |
|---|
| 48 | 52 | |
|---|
| 49 | 53 | static ssize_t msr_read(struct file *file, char __user *buf, |
|---|
| 50 | 54 | size_t count, loff_t *ppos) |
|---|
| .. | .. |
|---|
| 74 | 78 | return bytes ? bytes : err; |
|---|
| 75 | 79 | } |
|---|
| 76 | 80 | |
|---|
| 81 | +static int filter_write(u32 reg) |
|---|
| 82 | +{ |
|---|
| 83 | + /* |
|---|
| 84 | + * MSRs writes usually happen all at once, and can easily saturate kmsg. |
|---|
| 85 | + * Only allow one message every 30 seconds. |
|---|
| 86 | + * |
|---|
| 87 | + * It's possible to be smarter here and do it (for example) per-MSR, but |
|---|
| 88 | + * it would certainly be more complex, and this is enough at least to |
|---|
| 89 | + * avoid saturating the ring buffer. |
|---|
| 90 | + */ |
|---|
| 91 | + static DEFINE_RATELIMIT_STATE(fw_rs, 30 * HZ, 1); |
|---|
| 92 | + |
|---|
| 93 | + switch (allow_writes) { |
|---|
| 94 | + case MSR_WRITES_ON: return 0; |
|---|
| 95 | + case MSR_WRITES_OFF: return -EPERM; |
|---|
| 96 | + default: break; |
|---|
| 97 | + } |
|---|
| 98 | + |
|---|
| 99 | + if (!__ratelimit(&fw_rs)) |
|---|
| 100 | + return 0; |
|---|
| 101 | + |
|---|
| 102 | + if (reg == MSR_IA32_ENERGY_PERF_BIAS) |
|---|
| 103 | + return 0; |
|---|
| 104 | + |
|---|
| 105 | + pr_err("Write to unrecognized MSR 0x%x by %s (pid: %d). Please report to x86@kernel.org.\n", |
|---|
| 106 | + reg, current->comm, current->pid); |
|---|
| 107 | + |
|---|
| 108 | + return 0; |
|---|
| 109 | +} |
|---|
| 110 | + |
|---|
| 77 | 111 | static ssize_t msr_write(struct file *file, const char __user *buf, |
|---|
| 78 | 112 | size_t count, loff_t *ppos) |
|---|
| 79 | 113 | { |
|---|
| .. | .. |
|---|
| 84 | 118 | int err = 0; |
|---|
| 85 | 119 | ssize_t bytes = 0; |
|---|
| 86 | 120 | |
|---|
| 121 | + err = security_locked_down(LOCKDOWN_MSR); |
|---|
| 122 | + if (err) |
|---|
| 123 | + return err; |
|---|
| 124 | + |
|---|
| 125 | + err = filter_write(reg); |
|---|
| 126 | + if (err) |
|---|
| 127 | + return err; |
|---|
| 128 | + |
|---|
| 87 | 129 | if (count % 8) |
|---|
| 88 | 130 | return -EINVAL; /* Invalid chunk size */ |
|---|
| 89 | 131 | |
|---|
| .. | .. |
|---|
| 92 | 134 | err = -EFAULT; |
|---|
| 93 | 135 | break; |
|---|
| 94 | 136 | } |
|---|
| 137 | + |
|---|
| 138 | + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); |
|---|
| 139 | + |
|---|
| 95 | 140 | err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]); |
|---|
| 96 | 141 | if (err) |
|---|
| 97 | 142 | break; |
|---|
| 143 | + |
|---|
| 98 | 144 | tmp += 2; |
|---|
| 99 | 145 | bytes += 8; |
|---|
| 100 | 146 | } |
|---|
| .. | .. |
|---|
| 115 | 161 | err = -EBADF; |
|---|
| 116 | 162 | break; |
|---|
| 117 | 163 | } |
|---|
| 118 | | - if (copy_from_user(®s, uregs, sizeof regs)) { |
|---|
| 164 | + if (copy_from_user(®s, uregs, sizeof(regs))) { |
|---|
| 119 | 165 | err = -EFAULT; |
|---|
| 120 | 166 | break; |
|---|
| 121 | 167 | } |
|---|
| 122 | 168 | err = rdmsr_safe_regs_on_cpu(cpu, regs); |
|---|
| 123 | 169 | if (err) |
|---|
| 124 | 170 | break; |
|---|
| 125 | | - if (copy_to_user(uregs, ®s, sizeof regs)) |
|---|
| 171 | + if (copy_to_user(uregs, ®s, sizeof(regs))) |
|---|
| 126 | 172 | err = -EFAULT; |
|---|
| 127 | 173 | break; |
|---|
| 128 | 174 | |
|---|
| .. | .. |
|---|
| 131 | 177 | err = -EBADF; |
|---|
| 132 | 178 | break; |
|---|
| 133 | 179 | } |
|---|
| 134 | | - if (copy_from_user(®s, uregs, sizeof regs)) { |
|---|
| 180 | + if (copy_from_user(®s, uregs, sizeof(regs))) { |
|---|
| 135 | 181 | err = -EFAULT; |
|---|
| 136 | 182 | break; |
|---|
| 137 | 183 | } |
|---|
| 184 | + err = security_locked_down(LOCKDOWN_MSR); |
|---|
| 185 | + if (err) |
|---|
| 186 | + break; |
|---|
| 187 | + |
|---|
| 188 | + err = filter_write(regs[1]); |
|---|
| 189 | + if (err) |
|---|
| 190 | + return err; |
|---|
| 191 | + |
|---|
| 192 | + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); |
|---|
| 193 | + |
|---|
| 138 | 194 | err = wrmsr_safe_regs_on_cpu(cpu, regs); |
|---|
| 139 | 195 | if (err) |
|---|
| 140 | 196 | break; |
|---|
| 141 | | - if (copy_to_user(uregs, ®s, sizeof regs)) |
|---|
| 197 | + if (copy_to_user(uregs, ®s, sizeof(regs))) |
|---|
| 142 | 198 | err = -EFAULT; |
|---|
| 143 | 199 | break; |
|---|
| 144 | 200 | |
|---|
| .. | .. |
|---|
| 239 | 295 | } |
|---|
| 240 | 296 | module_exit(msr_exit) |
|---|
| 241 | 297 | |
|---|
| 298 | +static int set_allow_writes(const char *val, const struct kernel_param *cp) |
|---|
| 299 | +{ |
|---|
| 300 | + /* val is NUL-terminated, see kernfs_fop_write() */ |
|---|
| 301 | + char *s = strstrip((char *)val); |
|---|
| 302 | + |
|---|
| 303 | + if (!strcmp(s, "on")) |
|---|
| 304 | + allow_writes = MSR_WRITES_ON; |
|---|
| 305 | + else if (!strcmp(s, "off")) |
|---|
| 306 | + allow_writes = MSR_WRITES_OFF; |
|---|
| 307 | + else |
|---|
| 308 | + allow_writes = MSR_WRITES_DEFAULT; |
|---|
| 309 | + |
|---|
| 310 | + return 0; |
|---|
| 311 | +} |
|---|
| 312 | + |
|---|
| 313 | +static int get_allow_writes(char *buf, const struct kernel_param *kp) |
|---|
| 314 | +{ |
|---|
| 315 | + const char *res; |
|---|
| 316 | + |
|---|
| 317 | + switch (allow_writes) { |
|---|
| 318 | + case MSR_WRITES_ON: res = "on"; break; |
|---|
| 319 | + case MSR_WRITES_OFF: res = "off"; break; |
|---|
| 320 | + default: res = "default"; break; |
|---|
| 321 | + } |
|---|
| 322 | + |
|---|
| 323 | + return sprintf(buf, "%s\n", res); |
|---|
| 324 | +} |
|---|
| 325 | + |
|---|
| 326 | +static const struct kernel_param_ops allow_writes_ops = { |
|---|
| 327 | + .set = set_allow_writes, |
|---|
| 328 | + .get = get_allow_writes |
|---|
| 329 | +}; |
|---|
| 330 | + |
|---|
| 331 | +module_param_cb(allow_writes, &allow_writes_ops, NULL, 0600); |
|---|
| 332 | + |
|---|
| 242 | 333 | MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>"); |
|---|
| 243 | 334 | MODULE_DESCRIPTION("x86 generic MSR driver"); |
|---|
| 244 | 335 | MODULE_LICENSE("GPL"); |
|---|