Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 893e42c

Browse files
committedMay 29, 2024·
add msrscan poc
1 parent 422d0c3 commit 893e42c

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed
 

‎pocs/cpus/misc/msrscan/Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
KDIR := /lib/modules/$(shell uname -r)/build
2+
3+
obj-m += msrscan.o
4+
5+
default:
6+
$(MAKE) -C $(KDIR) M='$(PWD)' modules
7+
8+
clean:
9+
$(MAKE) -C $(KDIR) M='$(PWD)' clean

‎pocs/cpus/misc/msrscan/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
This is experimental code to test if we can automatically scan for chicken
2+
bits that might avoid a problematic CPU behaviour.
3+
4+
The problem is that setting or unsetting chicken bits often introduces
5+
immediate system instability, so we can't just set them all and see what
6+
happens.
7+
8+
This is sample code for scanning for useful chicken bits, with a list of
9+
known-unstable bits blocked. The theory is we can automatically flip a bit,
10+
check if that changed the behaviour we were interested in, and then flip it
11+
back.
12+
13+
A human would that manually examine the result to see if it's interesting.
14+
15+
This code is intended specifically for CPU research on dedicated test hardware.

‎pocs/cpus/misc/msrscan/msrscan.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#include <linux/kernel.h>
2+
#include <linux/module.h>
3+
#include <linux/slab.h>
4+
#include <linux/kthread.h>
5+
#include <linux/atomic.h>
6+
#include <linux/delay.h>
7+
8+
#define UINT32_MAX U32_MAX
9+
10+
// How many CPU cores to scan MSRs on.
11+
#define NUM_THREADS 8
12+
13+
// The first and last MSR you want to test.
14+
const static uint32_t kFirstMSR = 0xc0000000;
15+
const static uint32_t kFinalMSR = 0xc0100000;
16+
17+
struct msr_scan_range {
18+
uint32_t start;
19+
uint32_t stop;
20+
};
21+
22+
static struct task_struct * kmsrscan[NUM_THREADS];
23+
static struct msr_scan_range kmsrrange[NUM_THREADS];
24+
25+
// This is a blocklist of chicken bits known to cause instability just by
26+
// flipping them, and so should not be tested.
27+
static const bool msr_c001_range_unsafe[][64] = {
28+
[0x0010][21 ... 22] = true, // MSR_AMD64_SYSCFG verified
29+
[0x0017][11] = true, // verified
30+
[0x0019][11] = true, // verified
31+
[0x001d][34] = true, // MSR_K8_TOP_MEM2 verified
32+
[0x001d][35] = true, // crashes rembrandt
33+
[0x0020][0 ... 63] = true, // MSR_AMD64_PATCH_LOADER verified
34+
[0x0058][5] = true, // MSR_FAM10H_MMIO_CONF_BASE verified
35+
[0x1029][27] = true, // MSR_AMD64_DE_CFG verified
36+
[0x1092][27] = true, // verified
37+
[0x10e1][14 ... 17] = true, // verified
38+
[0x10e1][20] = true, // verified
39+
[0x10e1][22 ... 25] = true, // verified
40+
[0x10e1][32 ... 38] = true, // verified
41+
[0x10e1][52 ... 57] = true, // verified
42+
[0x10e1][58] = true, // verified
43+
[0x10e1][59 ... 62] = true, // verified
44+
};
45+
static const bool msr_c000_range_unsafe[][64] = {
46+
[0x0080][0] = true, // MSR_EFER
47+
[0x0080][8] = true,
48+
[0x0080][10 ... 14] = true,
49+
[0x0080][21] = true,
50+
[0x0081][0 ... 63] = true, // MSR_STAR
51+
[0x0082][0 ... 63] = true, // MSR_LSTAR
52+
[0x0083][0 ... 63] = true, // MSR_CSTAR
53+
[0x0084][0 ... 63] = true, // MSR_SYSCALL_MASK
54+
[0x0100][0 ... 63] = true, // MSR_FS_BASE
55+
[0x0101][0 ... 63] = true, // MSR_GS_BASE
56+
[0x0102][0 ... 63] = true, // MSR_KERNEL_GS_BASE
57+
[0x0103][0 ... 63] = true, // MSR_TSC_AUX
58+
[0x0104][0 ... 63] = true, // MSR_AMD64_TSC_RATIO
59+
};
60+
static const bool msr_0000_range_unsafe[][64] = {
61+
[0x001a][0 ... 63] = true,
62+
[0x001b][0 ... 63] = true, // MSR_IA32_APICBASE
63+
[0x0174][0 ... 63] = true, // MSR_IA32_SYSENTER_CS
64+
[0x0175][0 ... 63] = true, // MSR_IA32_SYSENTER_ESP
65+
[0x0176][0 ... 63] = true, // MSR_IA32_SYSENTER_EIP
66+
[0x01a6][0 ... 53] = true, // MSR_OFFCORE_RSP_0
67+
[0x01a7][0 ... 53] = true, // MSR_OFFCORE_RSP_1
68+
[0x01ad][0 ... 53] = true, // MSR_TURBO_RAIO_LIMIT
69+
[0x01ae][0 ... 53] = true, // MSR_TURBO_RAIO_LIMIT1
70+
[0x01af][0 ... 53] = true, // MSR_TURBO_RAIO_LIMIT2
71+
[0x01fc][0 ... 63] = true, // MSR_IA32_POWER_CTL
72+
[0x01ff][0 ... 63] = true,
73+
[0x0200][0 ... 63] = true,
74+
[0x0206][0 ... 63] = true,
75+
[0x0207][0 ... 63] = true,
76+
[0x0209][0 ... 63] = true,
77+
[0x020a][0 ... 63] = true,
78+
[0x020b][0 ... 63] = true,
79+
[0x020c][0 ... 63] = true,
80+
[0x020d][0 ... 63] = true,
81+
[0x020e][0 ... 63] = true,
82+
[0x020f][0 ... 63] = true,
83+
[0x02ff][0 ... 63] = true,
84+
};
85+
86+
static inline bool check_msr_unsafe(uint32_t msr, uint8_t bit)
87+
{
88+
uint16_t hi = msr >> 16;
89+
uint16_t lo = msr & 0xffff;
90+
91+
if (hi == 0xc001 && lo < ARRAY_SIZE(msr_c001_range_unsafe)) {
92+
return msr_c001_range_unsafe[lo][bit];
93+
}
94+
95+
if (hi == 0xc000 && lo < ARRAY_SIZE(msr_c000_range_unsafe)) {
96+
return msr_c000_range_unsafe[lo][bit];
97+
}
98+
99+
if (hi == 0x0000 && lo < ARRAY_SIZE(msr_0000_range_unsafe)) {
100+
return msr_0000_range_unsafe[lo][bit];
101+
}
102+
103+
return false;
104+
}
105+
106+
// This is where the thread sets random chicken bits.
107+
static int kthread_scan_msrs(struct msr_scan_range *rng)
108+
{
109+
uint64_t value;
110+
uint32_t msr;
111+
uint8_t bit;
112+
113+
for (msr = rng->start; msr < rng->stop; msr++) {
114+
115+
// Check if we are supposed to abort.
116+
if (kthread_should_stop())
117+
break;
118+
119+
if ((msr & 0x1ff) == 0) {
120+
printk(KERN_INFO "cpu %d testing msr %#x... (%u%%)\n",
121+
smp_processor_id(),
122+
msr,
123+
(msr - rng->start) / ((rng->stop - rng->start) / 100));
124+
}
125+
126+
// Be nice to system...
127+
cond_resched();
128+
129+
// First learn the existing value.
130+
value = __rdmsr(msr);
131+
132+
// Disable interrupts during testing.
133+
local_irq_disable();
134+
135+
for (bit = 0; bit < 64; bit++) {
136+
// Check if we know this bit is bad.
137+
if (check_msr_unsafe(msr, bit))
138+
continue;
139+
140+
#if 1
141+
printk(KERN_INFO "msr %#x bit %u\n", msr, bit);
142+
msleep(50);
143+
cond_resched();
144+
#endif
145+
// Okay, begin the test.
146+
wrmsrl(msr, value ^ (1ULL << bit));
147+
148+
// XXX: PERFORM TESTING HERE
149+
// xxx
150+
// xxx
151+
// xxx
152+
153+
// Test complete, restore previous value.
154+
wrmsrl(msr, value);
155+
}
156+
157+
// Renenable interrupts.
158+
local_irq_enable();
159+
}
160+
161+
printk(KERN_INFO "cpu %d completed scanning msrs\n", smp_processor_id());
162+
163+
return 0;
164+
}
165+
166+
static int __init kmod_init(void)
167+
{
168+
uint32_t range;
169+
170+
printk(KERN_INFO "kernel module is loaded\n");
171+
172+
// How many msrs we have to test.
173+
range = kFinalMSR - kFirstMSR;
174+
175+
for (int i = 0; i < NUM_THREADS; i++) {
176+
// Yes, this might miss a few.
177+
kmsrrange[i].start = kFirstMSR + ((i + 0) * (range / NUM_THREADS));
178+
kmsrrange[i].stop = kFirstMSR + ((i + 1) * (range / NUM_THREADS));
179+
180+
kmsrscan[i] = kthread_create((void *)kthread_scan_msrs, &kmsrrange[i], "kmsrscan");
181+
182+
// Assign this thread to a specific core.
183+
kthread_bind(kmsrscan[i], i);
184+
185+
// Okay, Go.
186+
wake_up_process(kmsrscan[i]);
187+
}
188+
return 0;
189+
}
190+
191+
static void __exit kmod_exit(void)
192+
{
193+
printk(KERN_INFO "kernel module is stopping threads...\n");
194+
195+
for (int i = 0; i < NUM_THREADS; i++)
196+
kthread_stop(kmsrscan[i]);
197+
198+
printk(KERN_INFO "Complete.\n");
199+
return;
200+
}
201+
202+
module_init(kmod_init);
203+
module_exit(kmod_exit);
204+
205+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)
Please sign in to comment.