Documentation/srso: Document a method for checking safe RET operates properly
Add a method to quickly verify whether safe RET operates properly on a given system using perf tool. Also, add a selftest which does the same thing. Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Link: https://lore.kernel.org/r/20240731160531.28640-1-bp@kernel.org
This commit is contained in:
parent
225f2bd064
commit
4015350525
@ -158,3 +158,72 @@ poisoned BTB entry and using that safe one for all function returns.
|
|||||||
In older Zen1 and Zen2, this is accomplished using a reinterpretation
|
In older Zen1 and Zen2, this is accomplished using a reinterpretation
|
||||||
technique similar to Retbleed one: srso_untrain_ret() and
|
technique similar to Retbleed one: srso_untrain_ret() and
|
||||||
srso_safe_ret().
|
srso_safe_ret().
|
||||||
|
|
||||||
|
Checking the safe RET mitigation actually works
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
In case one wants to validate whether the SRSO safe RET mitigation works
|
||||||
|
on a kernel, one could use two performance counters
|
||||||
|
|
||||||
|
* PMC_0xc8 - Count of RET/RET lw retired
|
||||||
|
* PMC_0xc9 - Count of RET/RET lw retired mispredicted
|
||||||
|
|
||||||
|
and compare the number of RETs retired properly vs those retired
|
||||||
|
mispredicted, in kernel mode. Another way of specifying those events
|
||||||
|
is::
|
||||||
|
|
||||||
|
# perf list ex_ret_near_ret
|
||||||
|
|
||||||
|
List of pre-defined events (to be used in -e or -M):
|
||||||
|
|
||||||
|
core:
|
||||||
|
ex_ret_near_ret
|
||||||
|
[Retired Near Returns]
|
||||||
|
ex_ret_near_ret_mispred
|
||||||
|
[Retired Near Returns Mispredicted]
|
||||||
|
|
||||||
|
Either the command using the event mnemonics::
|
||||||
|
|
||||||
|
# perf stat -e ex_ret_near_ret:k -e ex_ret_near_ret_mispred:k sleep 10s
|
||||||
|
|
||||||
|
or using the raw PMC numbers::
|
||||||
|
|
||||||
|
# perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
|
||||||
|
|
||||||
|
should give the same amount. I.e., every RET retired should be
|
||||||
|
mispredicted::
|
||||||
|
|
||||||
|
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
|
||||||
|
|
||||||
|
Performance counter stats for 'sleep 10s':
|
||||||
|
|
||||||
|
137,167 cpu/event=0xc8,umask=0/k
|
||||||
|
137,173 cpu/event=0xc9,umask=0/k
|
||||||
|
|
||||||
|
10.004110303 seconds time elapsed
|
||||||
|
|
||||||
|
0.000000000 seconds user
|
||||||
|
0.004462000 seconds sys
|
||||||
|
|
||||||
|
vs the case when the mitigation is disabled (spec_rstack_overflow=off)
|
||||||
|
or not functioning properly, showing usually a lot smaller number of
|
||||||
|
mispredicted retired RETs vs the overall count of retired RETs during
|
||||||
|
a workload::
|
||||||
|
|
||||||
|
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
|
||||||
|
|
||||||
|
Performance counter stats for 'sleep 10s':
|
||||||
|
|
||||||
|
201,627 cpu/event=0xc8,umask=0/k
|
||||||
|
4,074 cpu/event=0xc9,umask=0/k
|
||||||
|
|
||||||
|
10.003267252 seconds time elapsed
|
||||||
|
|
||||||
|
0.002729000 seconds user
|
||||||
|
0.000000000 seconds sys
|
||||||
|
|
||||||
|
Also, there is a selftest which performs the above, go to
|
||||||
|
tools/testing/selftests/x86/ and do::
|
||||||
|
|
||||||
|
make srso
|
||||||
|
./srso
|
||||||
|
@ -77,7 +77,7 @@ all_32: $(BINARIES_32)
|
|||||||
|
|
||||||
all_64: $(BINARIES_64)
|
all_64: $(BINARIES_64)
|
||||||
|
|
||||||
EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
|
EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64) srso
|
||||||
|
|
||||||
$(BINARIES_32): $(OUTPUT)/%_32: %.c helpers.h
|
$(BINARIES_32): $(OUTPUT)/%_32: %.c helpers.h
|
||||||
$(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $< $(EXTRA_FILES) -lrt -ldl -lm
|
$(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $< $(EXTRA_FILES) -lrt -ldl -lm
|
||||||
|
70
tools/testing/selftests/x86/srso.c
Normal file
70
tools/testing/selftests/x86/srso.c
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include <linux/perf_event.h>
|
||||||
|
#include <cpuid.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct perf_event_attr ret_attr, mret_attr;
|
||||||
|
long long count_rets, count_rets_mispred;
|
||||||
|
int rrets_fd, mrrets_fd;
|
||||||
|
unsigned int cpuid1_eax, b, c, d;
|
||||||
|
|
||||||
|
__cpuid(1, cpuid1_eax, b, c, d);
|
||||||
|
|
||||||
|
if (cpuid1_eax < 0x00800f00 ||
|
||||||
|
cpuid1_eax > 0x00afffff) {
|
||||||
|
fprintf(stderr, "This needs to run on a Zen[1-4] machine (CPUID(1).EAX: 0x%x). Exiting...\n", cpuid1_eax);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ret_attr, 0, sizeof(struct perf_event_attr));
|
||||||
|
memset(&mret_attr, 0, sizeof(struct perf_event_attr));
|
||||||
|
|
||||||
|
ret_attr.type = mret_attr.type = PERF_TYPE_RAW;
|
||||||
|
ret_attr.size = mret_attr.size = sizeof(struct perf_event_attr);
|
||||||
|
ret_attr.config = 0xc8;
|
||||||
|
mret_attr.config = 0xc9;
|
||||||
|
ret_attr.disabled = mret_attr.disabled = 1;
|
||||||
|
ret_attr.exclude_user = mret_attr.exclude_user = 1;
|
||||||
|
ret_attr.exclude_hv = mret_attr.exclude_hv = 1;
|
||||||
|
|
||||||
|
rrets_fd = syscall(SYS_perf_event_open, &ret_attr, 0, -1, -1, 0);
|
||||||
|
if (rrets_fd == -1) {
|
||||||
|
perror("opening retired RETs fd");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mrrets_fd = syscall(SYS_perf_event_open, &mret_attr, 0, -1, -1, 0);
|
||||||
|
if (mrrets_fd == -1) {
|
||||||
|
perror("opening retired mispredicted RETs fd");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl(rrets_fd, PERF_EVENT_IOC_RESET, 0);
|
||||||
|
ioctl(mrrets_fd, PERF_EVENT_IOC_RESET, 0);
|
||||||
|
|
||||||
|
ioctl(rrets_fd, PERF_EVENT_IOC_ENABLE, 0);
|
||||||
|
ioctl(mrrets_fd, PERF_EVENT_IOC_ENABLE, 0);
|
||||||
|
|
||||||
|
printf("Sleeping for 10 seconds\n");
|
||||||
|
sleep(10);
|
||||||
|
|
||||||
|
ioctl(rrets_fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
ioctl(mrrets_fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
|
||||||
|
read(rrets_fd, &count_rets, sizeof(long long));
|
||||||
|
read(mrrets_fd, &count_rets_mispred, sizeof(long long));
|
||||||
|
|
||||||
|
printf("RETs: (%lld retired <-> %lld mispredicted)\n",
|
||||||
|
count_rets, count_rets_mispred);
|
||||||
|
printf("SRSO Safe-RET mitigation works correctly if both counts are almost equal.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user