Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2020-11-19 19:08:46 -08:00
443 changed files with 3975 additions and 2050 deletions

View File

@@ -1 +0,0 @@
test_data/* binary

View File

@@ -11,7 +11,6 @@ import argparse
import sys
import os
import time
import shutil
from collections import namedtuple
from enum import Enum, auto
@@ -44,11 +43,6 @@ class KunitStatus(Enum):
BUILD_FAILURE = auto()
TEST_FAILURE = auto()
def create_default_kunitconfig():
if not os.path.exists(kunit_kernel.kunitconfig_path):
shutil.copyfile('arch/um/configs/kunit_defconfig',
kunit_kernel.kunitconfig_path)
def get_kernel_root_path():
parts = sys.argv[0] if not __file__ else __file__
parts = os.path.realpath(parts).split('tools/testing/kunit')
@@ -61,7 +55,6 @@ def config_tests(linux: kunit_kernel.LinuxSourceTree,
kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...')
config_start = time.time()
create_default_kunitconfig()
success = linux.build_reconfig(request.build_dir, request.make_options)
config_end = time.time()
if not success:
@@ -262,12 +255,12 @@ def main(argv, linux=None):
if not os.path.exists(cli_args.build_dir):
os.mkdir(cli_args.build_dir)
if not os.path.exists(kunit_kernel.kunitconfig_path):
create_default_kunitconfig()
if not linux:
linux = kunit_kernel.LinuxSourceTree()
linux.create_kunitconfig(cli_args.build_dir)
linux.read_kunitconfig(cli_args.build_dir)
request = KunitRequest(cli_args.raw_output,
cli_args.timeout,
cli_args.jobs,
@@ -283,12 +276,12 @@ def main(argv, linux=None):
not os.path.exists(cli_args.build_dir)):
os.mkdir(cli_args.build_dir)
if not os.path.exists(kunit_kernel.kunitconfig_path):
create_default_kunitconfig()
if not linux:
linux = kunit_kernel.LinuxSourceTree()
linux.create_kunitconfig(cli_args.build_dir)
linux.read_kunitconfig(cli_args.build_dir)
request = KunitConfigRequest(cli_args.build_dir,
cli_args.make_options)
result = config_tests(linux, request)
@@ -301,6 +294,9 @@ def main(argv, linux=None):
if not linux:
linux = kunit_kernel.LinuxSourceTree()
linux.create_kunitconfig(cli_args.build_dir)
linux.read_kunitconfig(cli_args.build_dir)
request = KunitBuildRequest(cli_args.jobs,
cli_args.build_dir,
cli_args.alltests,
@@ -315,6 +311,9 @@ def main(argv, linux=None):
if not linux:
linux = kunit_kernel.LinuxSourceTree()
linux.create_kunitconfig(cli_args.build_dir)
linux.read_kunitconfig(cli_args.build_dir)
exec_request = KunitExecRequest(cli_args.timeout,
cli_args.build_dir,
cli_args.alltests)
@@ -337,7 +336,7 @@ def main(argv, linux=None):
kunit_output = f.read().splitlines()
request = KunitParseRequest(cli_args.raw_output,
kunit_output,
cli_args.build_dir,
None,
cli_args.json)
result = parse_tests(request)
if result.status != KunitStatus.SUCCESS:

View File

@@ -6,10 +6,10 @@
# Author: Felix Guo <felixguoxiuping@gmail.com>
# Author: Brendan Higgins <brendanhiggins@google.com>
import logging
import subprocess
import os
import shutil
import signal
from contextlib import ExitStack
@@ -18,8 +18,10 @@ import kunit_config
import kunit_parser
KCONFIG_PATH = '.config'
kunitconfig_path = '.kunitconfig'
KUNITCONFIG_PATH = '.kunitconfig'
DEFAULT_KUNITCONFIG_PATH = 'arch/um/configs/kunit_defconfig'
BROKEN_ALLCONFIG_PATH = 'tools/testing/kunit/configs/broken_on_uml.config'
OUTFILE_PATH = 'test.log'
class ConfigError(Exception):
"""Represents an error trying to configure the Linux kernel."""
@@ -82,36 +84,51 @@ class LinuxSourceTreeOperations(object):
if build_dir:
command += ['O=' + build_dir]
try:
subprocess.check_output(command, stderr=subprocess.STDOUT)
proc = subprocess.Popen(command,
stderr=subprocess.PIPE,
stdout=subprocess.DEVNULL)
except OSError as e:
raise BuildError('Could not call execute make: ' + str(e))
except subprocess.CalledProcessError as e:
raise BuildError(e.output.decode())
raise BuildError('Could not call make command: ' + str(e))
_, stderr = proc.communicate()
if proc.returncode != 0:
raise BuildError(stderr.decode())
if stderr: # likely only due to build warnings
print(stderr.decode())
def linux_bin(self, params, timeout, build_dir, outfile):
def linux_bin(self, params, timeout, build_dir):
"""Runs the Linux UML binary. Must be named 'linux'."""
linux_bin = './linux'
if build_dir:
linux_bin = os.path.join(build_dir, 'linux')
outfile = get_outfile_path(build_dir)
with open(outfile, 'w') as output:
process = subprocess.Popen([linux_bin] + params,
stdout=output,
stderr=subprocess.STDOUT)
process.wait(timeout)
def get_kconfig_path(build_dir):
kconfig_path = KCONFIG_PATH
if build_dir:
kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
return kconfig_path
def get_kunitconfig_path(build_dir):
kunitconfig_path = KUNITCONFIG_PATH
if build_dir:
kunitconfig_path = os.path.join(build_dir, KUNITCONFIG_PATH)
return kunitconfig_path
def get_outfile_path(build_dir):
outfile_path = OUTFILE_PATH
if build_dir:
outfile_path = os.path.join(build_dir, OUTFILE_PATH)
return outfile_path
class LinuxSourceTree(object):
"""Represents a Linux kernel source tree with KUnit tests."""
def __init__(self):
self._kconfig = kunit_config.Kconfig()
self._kconfig.read_from_file(kunitconfig_path)
self._ops = LinuxSourceTreeOperations()
signal.signal(signal.SIGINT, self.signal_handler)
@@ -123,6 +140,16 @@ class LinuxSourceTree(object):
return False
return True
def create_kunitconfig(self, build_dir, defconfig=DEFAULT_KUNITCONFIG_PATH):
kunitconfig_path = get_kunitconfig_path(build_dir)
if not os.path.exists(kunitconfig_path):
shutil.copyfile(defconfig, kunitconfig_path)
def read_kunitconfig(self, build_dir):
kunitconfig_path = get_kunitconfig_path(build_dir)
self._kconfig = kunit_config.Kconfig()
self._kconfig.read_from_file(kunitconfig_path)
def validate_config(self, build_dir):
kconfig_path = get_kconfig_path(build_dir)
validated_kconfig = kunit_config.Kconfig()
@@ -178,8 +205,8 @@ class LinuxSourceTree(object):
def run_kernel(self, args=[], build_dir='', timeout=None):
args.extend(['mem=1G'])
outfile = 'test.log'
self._ops.linux_bin(args, timeout, build_dir, outfile)
self._ops.linux_bin(args, timeout, build_dir)
outfile = get_outfile_path(build_dir)
subprocess.call(['stty', 'sane'])
with open(outfile, 'r') as file:
for line in file:

View File

@@ -12,7 +12,7 @@ from collections import namedtuple
from datetime import datetime
from enum import Enum, auto
from functools import reduce
from typing import List
from typing import List, Optional, Tuple
TestResult = namedtuple('TestResult', ['status','suites','log'])
@@ -54,6 +54,7 @@ kunit_end_re = re.compile('(List of all partitions:|'
def isolate_kunit_output(kernel_output):
started = False
for line in kernel_output:
line = line.rstrip() # line always has a trailing \n
if kunit_start_re.search(line):
prefix_len = len(line.split('TAP version')[0])
started = True
@@ -65,7 +66,7 @@ def isolate_kunit_output(kernel_output):
def raw_output(kernel_output):
for line in kernel_output:
print(line)
print(line.rstrip())
DIVIDER = '=' * 60
@@ -151,7 +152,7 @@ def parse_diagnostic(lines: List[str], test_case: TestCase) -> bool:
else:
return False
def parse_test_case(lines: List[str]) -> TestCase:
def parse_test_case(lines: List[str]) -> Optional[TestCase]:
test_case = TestCase()
save_non_diagnositic(lines, test_case)
while parse_diagnostic(lines, test_case):
@@ -163,7 +164,7 @@ def parse_test_case(lines: List[str]) -> TestCase:
SUBTEST_HEADER = re.compile(r'^[\s]+# Subtest: (.*)$')
def parse_subtest_header(lines: List[str]) -> str:
def parse_subtest_header(lines: List[str]) -> Optional[str]:
consume_non_diagnositic(lines)
if not lines:
return None
@@ -176,7 +177,7 @@ def parse_subtest_header(lines: List[str]) -> str:
SUBTEST_PLAN = re.compile(r'[\s]+[0-9]+\.\.([0-9]+)')
def parse_subtest_plan(lines: List[str]) -> int:
def parse_subtest_plan(lines: List[str]) -> Optional[int]:
consume_non_diagnositic(lines)
match = SUBTEST_PLAN.match(lines[0])
if match:
@@ -230,7 +231,7 @@ def bubble_up_test_case_errors(test_suite: TestSuite) -> TestStatus:
max_test_case_status = bubble_up_errors(lambda x: x.status, test_suite.cases)
return max_status(max_test_case_status, test_suite.status)
def parse_test_suite(lines: List[str], expected_suite_index: int) -> TestSuite:
def parse_test_suite(lines: List[str], expected_suite_index: int) -> Optional[TestSuite]:
if not lines:
return None
consume_non_diagnositic(lines)
@@ -271,7 +272,7 @@ def parse_tap_header(lines: List[str]) -> bool:
TEST_PLAN = re.compile(r'[0-9]+\.\.([0-9]+)')
def parse_test_plan(lines: List[str]) -> int:
def parse_test_plan(lines: List[str]) -> Optional[int]:
consume_non_diagnositic(lines)
match = TEST_PLAN.match(lines[0])
if match:
@@ -310,7 +311,7 @@ def parse_test_result(lines: List[str]) -> TestResult:
else:
return TestResult(TestStatus.NO_TESTS, [], lines)
def print_and_count_results(test_result: TestResult) -> None:
def print_and_count_results(test_result: TestResult) -> Tuple[int, int, int]:
total_tests = 0
failed_tests = 0
crashed_tests = 0

View File

@@ -102,7 +102,7 @@ class KUnitParserTest(unittest.TestCase):
'test_data/test_output_isolated_correctly.log')
file = open(log_path)
result = kunit_parser.isolate_kunit_output(file.readlines())
self.assertContains('TAP version 14\n', result)
self.assertContains('TAP version 14', result)
self.assertContains(' # Subtest: example', result)
self.assertContains(' 1..2', result)
self.assertContains(' ok 1 - example_simple_test', result)
@@ -115,7 +115,7 @@ class KUnitParserTest(unittest.TestCase):
'test_data/test_pound_sign.log')
with open(log_path) as file:
result = kunit_parser.isolate_kunit_output(file.readlines())
self.assertContains('TAP version 14\n', result)
self.assertContains('TAP version 14', result)
self.assertContains(' # Subtest: kunit-resource-test', result)
self.assertContains(' 1..5', result)
self.assertContains(' ok 1 - kunit_resource_test_init_resources', result)

View File

@@ -33,6 +33,7 @@ typedef unsigned long dma_addr_t;
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a))
#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)

View File

@@ -52,9 +52,9 @@ int main(void)
{
const unsigned int sgmax = SCATTERLIST_MAX_SEGMENT;
struct test *test, tests[] = {
{ -EINVAL, 1, pfn(0), PAGE_SIZE, PAGE_SIZE + 1, 1 },
{ -EINVAL, 1, pfn(0), PAGE_SIZE, 0, 1 },
{ -EINVAL, 1, pfn(0), PAGE_SIZE, sgmax + 1, 1 },
{ 0, 1, pfn(0), PAGE_SIZE, PAGE_SIZE + 1, 1 },
{ 0, 1, pfn(0), PAGE_SIZE, sgmax + 1, 1 },
{ 0, 1, pfn(0), PAGE_SIZE, sgmax, 1 },
{ 0, 1, pfn(0), 1, sgmax, 1 },
{ 0, 2, pfn(0, 1), 2 * PAGE_SIZE, sgmax, 1 },

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include "test_probe_read_user_str.skel.h"
static const char str1[] = "mestring";
static const char str2[] = "mestringalittlebigger";
static const char str3[] = "mestringblubblubblubblubblub";
static int test_one_str(struct test_probe_read_user_str *skel, const char *str,
size_t len)
{
int err, duration = 0;
char buf[256];
/* Ensure bytes after string are ones */
memset(buf, 1, sizeof(buf));
memcpy(buf, str, len);
/* Give prog our userspace pointer */
skel->bss->user_ptr = buf;
/* Trigger tracepoint */
usleep(1);
/* Did helper fail? */
if (CHECK(skel->bss->ret < 0, "prog_ret", "prog returned: %ld\n",
skel->bss->ret))
return 1;
/* Check that string was copied correctly */
err = memcmp(skel->bss->buf, str, len);
if (CHECK(err, "memcmp", "prog copied wrong string"))
return 1;
/* Now check that no extra trailing bytes were copied */
memset(buf, 0, sizeof(buf));
err = memcmp(skel->bss->buf + len, buf, sizeof(buf) - len);
if (CHECK(err, "memcmp", "trailing bytes were not stripped"))
return 1;
return 0;
}
void test_probe_read_user_str(void)
{
struct test_probe_read_user_str *skel;
int err, duration = 0;
skel = test_probe_read_user_str__open_and_load();
if (CHECK(!skel, "test_probe_read_user_str__open_and_load",
"skeleton open and load failed\n"))
return;
/* Give pid to bpf prog so it doesn't read from anyone else */
skel->bss->pid = getpid();
err = test_probe_read_user_str__attach(skel);
if (CHECK(err, "test_probe_read_user_str__attach",
"skeleton attach failed: %d\n", err))
goto out;
if (test_one_str(skel, str1, sizeof(str1)))
goto out;
if (test_one_str(skel, str2, sizeof(str2)))
goto out;
if (test_one_str(skel, str3, sizeof(str3)))
goto out;
out:
test_probe_read_user_str__destroy(skel);
}

View File

@@ -138,7 +138,8 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
*/
buf = 0x40;
if (setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1) < 0) {
err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
if (err < 0) {
log_err("Failed to call setsockopt(IP_TOS)");
goto detach;
}

View File

@@ -3,12 +3,14 @@
#include <test_progs.h>
#include <time.h>
#include "test_subprogs.skel.h"
#include "test_subprogs_unused.skel.h"
static int duration;
void test_subprogs(void)
{
struct test_subprogs *skel;
struct test_subprogs_unused *skel2;
int err;
skel = test_subprogs__open_and_load();
@@ -26,6 +28,10 @@ void test_subprogs(void)
CHECK(skel->bss->res3 != 19, "res3", "got %d, exp %d\n", skel->bss->res3, 19);
CHECK(skel->bss->res4 != 36, "res4", "got %d, exp %d\n", skel->bss->res4, 36);
skel2 = test_subprogs_unused__open_and_load();
ASSERT_OK_PTR(skel2, "unused_progs_skel");
test_subprogs_unused__destroy(skel2);
cleanup:
test_subprogs__destroy(skel);
}

View File

@@ -60,6 +60,7 @@ void test_test_global_funcs(void)
{ "test_global_func5.o" , "expected pointer to ctx, but got PTR" },
{ "test_global_func6.o" , "modified ctx ptr R2" },
{ "test_global_func7.o" , "foo() doesn't return scalar" },
{ "test_global_func8.o" },
};
libbpf_print_fn_t old_print_fn = NULL;
int err, i, duration = 0;

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020 Facebook */
#include <stddef.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
__noinline int foo(struct __sk_buff *skb)
{
return bpf_get_prandom_u32();
}
SEC("cgroup_skb/ingress")
int test_cls(struct __sk_buff *skb)
{
if (!foo(skb))
return 0;
return 1;
}

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <sys/types.h>
pid_t pid = 0;
long ret = 0;
void *user_ptr = 0;
char buf[256] = {};
SEC("tracepoint/syscalls/sys_enter_nanosleep")
int on_write(void *ctx)
{
if (pid != (bpf_get_current_pid_tgid() >> 32))
return 0;
ret = bpf_probe_read_user_str(buf, sizeof(buf), user_ptr);
return 0;
}
char _license[] SEC("license") = "GPL";

View File

@@ -0,0 +1,21 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
const char LICENSE[] SEC("license") = "GPL";
__attribute__((unused)) __noinline int unused1(int x)
{
return x + 1;
}
static __attribute__((unused)) __noinline int unused2(int x)
{
return x + 2;
}
SEC("raw_tp/sys_enter")
int main_prog(void *ctx)
{
return 0;
}

View File

@@ -42,6 +42,11 @@ int perf_event_enable(int fd);
int perf_event_disable(int fd);
int perf_event_reset(int fd);
struct perf_event_read {
__u64 nr;
__u64 l1d_misses;
};
#if !defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 30)
#include <unistd.h>
#include <sys/syscall.h>

View File

@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
rfi_flush
entry_flush

View File

@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+
TEST_GEN_PROGS := rfi_flush spectre_v2
TEST_GEN_PROGS := rfi_flush entry_flush spectre_v2
top_srcdir = ../../../../..
CFLAGS += -I../../../../../usr/include
@@ -11,3 +11,5 @@ $(TEST_GEN_PROGS): ../harness.c ../utils.c
$(OUTPUT)/spectre_v2: CFLAGS += -m64
$(OUTPUT)/spectre_v2: ../pmu/event.c branch_loops.S
$(OUTPUT)/rfi_flush: flush_utils.c
$(OUTPUT)/entry_flush: flush_utils.c

View File

@@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 IBM Corporation.
*/
#define __SANE_USERSPACE_TYPES__
#include <sys/types.h>
#include <stdint.h>
#include <malloc.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "utils.h"
#include "flush_utils.h"
int entry_flush_test(void)
{
char *p;
int repetitions = 10;
int fd, passes = 0, iter, rc = 0;
struct perf_event_read v;
__u64 l1d_misses_total = 0;
unsigned long iterations = 100000, zero_size = 24 * 1024;
unsigned long l1d_misses_expected;
int rfi_flush_orig;
int entry_flush, entry_flush_orig;
SKIP_IF(geteuid() != 0);
// The PMU event we use only works on Power7 or later
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
perror("Unable to read powerpc/rfi_flush debugfs file");
SKIP_IF(1);
}
if (read_debugfs_file("powerpc/entry_flush", &entry_flush_orig) < 0) {
perror("Unable to read powerpc/entry_flush debugfs file");
SKIP_IF(1);
}
if (rfi_flush_orig != 0) {
if (write_debugfs_file("powerpc/rfi_flush", 0) < 0) {
perror("error writing to powerpc/rfi_flush debugfs file");
FAIL_IF(1);
}
}
entry_flush = entry_flush_orig;
fd = perf_event_open_counter(PERF_TYPE_RAW, /* L1d miss */ 0x400f0, -1);
FAIL_IF(fd < 0);
p = (char *)memalign(zero_size, CACHELINE_SIZE);
FAIL_IF(perf_event_enable(fd));
// disable L1 prefetching
set_dscr(1);
iter = repetitions;
/*
* We expect to see l1d miss for each cacheline access when entry_flush
* is set. Allow a small variation on this.
*/
l1d_misses_expected = iterations * (zero_size / CACHELINE_SIZE - 2);
again:
FAIL_IF(perf_event_reset(fd));
syscall_loop(p, iterations, zero_size);
FAIL_IF(read(fd, &v, sizeof(v)) != sizeof(v));
if (entry_flush && v.l1d_misses >= l1d_misses_expected)
passes++;
else if (!entry_flush && v.l1d_misses < (l1d_misses_expected / 2))
passes++;
l1d_misses_total += v.l1d_misses;
while (--iter)
goto again;
if (passes < repetitions) {
printf("FAIL (L1D misses with entry_flush=%d: %llu %c %lu) [%d/%d failures]\n",
entry_flush, l1d_misses_total, entry_flush ? '<' : '>',
entry_flush ? repetitions * l1d_misses_expected :
repetitions * l1d_misses_expected / 2,
repetitions - passes, repetitions);
rc = 1;
} else {
printf("PASS (L1D misses with entry_flush=%d: %llu %c %lu) [%d/%d pass]\n",
entry_flush, l1d_misses_total, entry_flush ? '>' : '<',
entry_flush ? repetitions * l1d_misses_expected :
repetitions * l1d_misses_expected / 2,
passes, repetitions);
}
if (entry_flush == entry_flush_orig) {
entry_flush = !entry_flush_orig;
if (write_debugfs_file("powerpc/entry_flush", entry_flush) < 0) {
perror("error writing to powerpc/entry_flush debugfs file");
return 1;
}
iter = repetitions;
l1d_misses_total = 0;
passes = 0;
goto again;
}
perf_event_disable(fd);
close(fd);
set_dscr(0);
if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_orig) < 0) {
perror("unable to restore original value of powerpc/rfi_flush debugfs file");
return 1;
}
if (write_debugfs_file("powerpc/entry_flush", entry_flush_orig) < 0) {
perror("unable to restore original value of powerpc/entry_flush debugfs file");
return 1;
}
return rc;
}
int main(int argc, char *argv[])
{
return test_harness(entry_flush_test, "entry_flush_test");
}

View File

@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 IBM Corporation.
*/
#define __SANE_USERSPACE_TYPES__
#include <sys/types.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "utils.h"
#include "flush_utils.h"
static inline __u64 load(void *addr)
{
__u64 tmp;
asm volatile("ld %0,0(%1)" : "=r"(tmp) : "b"(addr));
return tmp;
}
void syscall_loop(char *p, unsigned long iterations,
unsigned long zero_size)
{
for (unsigned long i = 0; i < iterations; i++) {
for (unsigned long j = 0; j < zero_size; j += CACHELINE_SIZE)
load(p + j);
getppid();
}
}
static void sigill_handler(int signr, siginfo_t *info, void *unused)
{
static int warned;
ucontext_t *ctx = (ucontext_t *)unused;
unsigned long *pc = &UCONTEXT_NIA(ctx);
/* mtspr 3,RS to check for move to DSCR below */
if ((*((unsigned int *)*pc) & 0xfc1fffff) == 0x7c0303a6) {
if (!warned++)
printf("WARNING: Skipping over dscr setup. Consider running 'ppc64_cpu --dscr=1' manually.\n");
*pc += 4;
} else {
printf("SIGILL at %p\n", pc);
abort();
}
}
void set_dscr(unsigned long val)
{
static int init;
struct sigaction sa;
if (!init) {
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sigill_handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGILL, &sa, NULL))
perror("sigill_handler");
init = 1;
}
asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR));
}

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2018 IBM Corporation.
*/
#ifndef _SELFTESTS_POWERPC_SECURITY_FLUSH_UTILS_H
#define _SELFTESTS_POWERPC_SECURITY_FLUSH_UTILS_H
#define CACHELINE_SIZE 128
void syscall_loop(char *p, unsigned long iterations,
unsigned long zero_size);
void set_dscr(unsigned long val);
#endif /* _SELFTESTS_POWERPC_SECURITY_FLUSH_UTILS_H */

View File

@@ -10,71 +10,12 @@
#include <stdint.h>
#include <malloc.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "utils.h"
#include "flush_utils.h"
#define CACHELINE_SIZE 128
struct perf_event_read {
__u64 nr;
__u64 l1d_misses;
};
static inline __u64 load(void *addr)
{
__u64 tmp;
asm volatile("ld %0,0(%1)" : "=r"(tmp) : "b"(addr));
return tmp;
}
static void syscall_loop(char *p, unsigned long iterations,
unsigned long zero_size)
{
for (unsigned long i = 0; i < iterations; i++) {
for (unsigned long j = 0; j < zero_size; j += CACHELINE_SIZE)
load(p + j);
getppid();
}
}
static void sigill_handler(int signr, siginfo_t *info, void *unused)
{
static int warned = 0;
ucontext_t *ctx = (ucontext_t *)unused;
unsigned long *pc = &UCONTEXT_NIA(ctx);
/* mtspr 3,RS to check for move to DSCR below */
if ((*((unsigned int *)*pc) & 0xfc1fffff) == 0x7c0303a6) {
if (!warned++)
printf("WARNING: Skipping over dscr setup. Consider running 'ppc64_cpu --dscr=1' manually.\n");
*pc += 4;
} else {
printf("SIGILL at %p\n", pc);
abort();
}
}
static void set_dscr(unsigned long val)
{
static int init = 0;
struct sigaction sa;
if (!init) {
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sigill_handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGILL, &sa, NULL))
perror("sigill_handler");
init = 1;
}
asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR));
}
int rfi_flush_test(void)
{
@@ -85,19 +26,33 @@ int rfi_flush_test(void)
__u64 l1d_misses_total = 0;
unsigned long iterations = 100000, zero_size = 24 * 1024;
unsigned long l1d_misses_expected;
int rfi_flush_org, rfi_flush;
int rfi_flush_orig, rfi_flush;
int have_entry_flush, entry_flush_orig;
SKIP_IF(geteuid() != 0);
// The PMU event we use only works on Power7 or later
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_org)) {
if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
perror("Unable to read powerpc/rfi_flush debugfs file");
SKIP_IF(1);
}
rfi_flush = rfi_flush_org;
if (read_debugfs_file("powerpc/entry_flush", &entry_flush_orig) < 0) {
have_entry_flush = 0;
} else {
have_entry_flush = 1;
if (entry_flush_orig != 0) {
if (write_debugfs_file("powerpc/entry_flush", 0) < 0) {
perror("error writing to powerpc/entry_flush debugfs file");
return 1;
}
}
}
rfi_flush = rfi_flush_orig;
fd = perf_event_open_counter(PERF_TYPE_RAW, /* L1d miss */ 0x400f0, -1);
FAIL_IF(fd < 0);
@@ -106,6 +61,7 @@ int rfi_flush_test(void)
FAIL_IF(perf_event_enable(fd));
// disable L1 prefetching
set_dscr(1);
iter = repetitions;
@@ -147,8 +103,8 @@ again:
repetitions * l1d_misses_expected / 2,
passes, repetitions);
if (rfi_flush == rfi_flush_org) {
rfi_flush = !rfi_flush_org;
if (rfi_flush == rfi_flush_orig) {
rfi_flush = !rfi_flush_orig;
if (write_debugfs_file("powerpc/rfi_flush", rfi_flush) < 0) {
perror("error writing to powerpc/rfi_flush debugfs file");
return 1;
@@ -164,11 +120,19 @@ again:
set_dscr(0);
if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_org) < 0) {
if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_orig) < 0) {
perror("unable to restore original value of powerpc/rfi_flush debugfs file");
return 1;
}
if (have_entry_flush) {
if (write_debugfs_file("powerpc/entry_flush", entry_flush_orig) < 0) {
perror("unable to restore original value of powerpc/entry_flush "
"debugfs file");
return 1;
}
}
return rc;
}