Merge 5.10-rc5 into staging-testing
We want the staging/IIO fixes in here as well. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
@@ -1 +0,0 @@
|
||||
test_data/* binary
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,8 +66,7 @@ def isolate_kunit_output(kernel_output):
|
||||
|
||||
def raw_output(kernel_output):
|
||||
for line in kernel_output:
|
||||
print(line)
|
||||
yield line
|
||||
print(line.rstrip())
|
||||
|
||||
DIVIDER = '=' * 60
|
||||
|
||||
@@ -152,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):
|
||||
@@ -164,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
|
||||
@@ -177,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:
|
||||
@@ -231,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)
|
||||
@@ -242,7 +242,7 @@ def parse_test_suite(lines: List[str], expected_suite_index: int) -> TestSuite:
|
||||
return None
|
||||
test_suite.name = name
|
||||
expected_test_case_num = parse_subtest_plan(lines)
|
||||
if not expected_test_case_num:
|
||||
if expected_test_case_num is None:
|
||||
return None
|
||||
while expected_test_case_num > 0:
|
||||
test_case = parse_test_case(lines)
|
||||
@@ -272,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:
|
||||
@@ -311,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
|
||||
|
||||
@@ -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)
|
||||
@@ -179,7 +179,7 @@ class KUnitParserTest(unittest.TestCase):
|
||||
print_mock = mock.patch('builtins.print').start()
|
||||
result = kunit_parser.parse_run_tests(
|
||||
kunit_parser.isolate_kunit_output(file.readlines()))
|
||||
print_mock.assert_any_call(StrContains("no kunit output detected"))
|
||||
print_mock.assert_any_call(StrContains('no tests run!'))
|
||||
print_mock.stop()
|
||||
file.close()
|
||||
|
||||
@@ -198,39 +198,57 @@ class KUnitParserTest(unittest.TestCase):
|
||||
'test_data/test_config_printk_time.log')
|
||||
with open(prefix_log) as file:
|
||||
result = kunit_parser.parse_run_tests(file.readlines())
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
self.assertEqual(
|
||||
kunit_parser.TestStatus.SUCCESS,
|
||||
result.status)
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
|
||||
def test_ignores_multiple_prefixes(self):
|
||||
prefix_log = get_absolute_path(
|
||||
'test_data/test_multiple_prefixes.log')
|
||||
with open(prefix_log) as file:
|
||||
result = kunit_parser.parse_run_tests(file.readlines())
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
self.assertEqual(
|
||||
kunit_parser.TestStatus.SUCCESS,
|
||||
result.status)
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
|
||||
def test_prefix_mixed_kernel_output(self):
|
||||
mixed_prefix_log = get_absolute_path(
|
||||
'test_data/test_interrupted_tap_output.log')
|
||||
with open(mixed_prefix_log) as file:
|
||||
result = kunit_parser.parse_run_tests(file.readlines())
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
self.assertEqual(
|
||||
kunit_parser.TestStatus.SUCCESS,
|
||||
result.status)
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
|
||||
def test_prefix_poundsign(self):
|
||||
pound_log = get_absolute_path('test_data/test_pound_sign.log')
|
||||
with open(pound_log) as file:
|
||||
result = kunit_parser.parse_run_tests(file.readlines())
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
self.assertEqual(
|
||||
kunit_parser.TestStatus.SUCCESS,
|
||||
result.status)
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
|
||||
def test_kernel_panic_end(self):
|
||||
panic_log = get_absolute_path('test_data/test_kernel_panic_interrupt.log')
|
||||
with open(panic_log) as file:
|
||||
result = kunit_parser.parse_run_tests(file.readlines())
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
self.assertEqual(
|
||||
kunit_parser.TestStatus.TEST_CRASHED,
|
||||
result.status)
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
|
||||
def test_pound_no_prefix(self):
|
||||
pound_log = get_absolute_path('test_data/test_pound_no_prefix.log')
|
||||
with open(pound_log) as file:
|
||||
result = kunit_parser.parse_run_tests(file.readlines())
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
self.assertEqual(
|
||||
kunit_parser.TestStatus.SUCCESS,
|
||||
result.status)
|
||||
self.assertEqual('kunit-resource-test', result.suites[0].name)
|
||||
|
||||
class KUnitJsonTest(unittest.TestCase):
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[ 0.060000] printk: console [mc-1] enabled
|
||||
[ 0.060000] random: get_random_bytes called from init_oops_id+0x35/0x40 with crng_init=0
|
||||
[ 0.060000] TAP version 14
|
||||
[ 0.060000] 1..3
|
||||
[ 0.060000] # Subtest: kunit-resource-test
|
||||
[ 0.060000] 1..5
|
||||
[ 0.060000] ok 1 - kunit_resource_test_init_resources
|
||||
@@ -28,4 +29,4 @@
|
||||
[ 0.060000] Stack:
|
||||
[ 0.060000] 602086f8 601bc260 705c0000 705c0000
|
||||
[ 0.060000] 602086f8 6005fcec 705c0000 6002c6ab
|
||||
[ 0.060000] 6005fcec 601bc260 705c0000 3000000010
|
||||
[ 0.060000] 6005fcec 601bc260 705c0000 3000000010
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[ 0.060000] printk: console [mc-1] enabled
|
||||
[ 0.060000] random: get_random_bytes called from init_oops_id+0x35/0x40 with crng_init=0
|
||||
[ 0.060000] TAP version 14
|
||||
[ 0.060000] 1..3
|
||||
[ 0.060000] # Subtest: kunit-resource-test
|
||||
[ 0.060000] 1..5
|
||||
[ 0.060000] ok 1 - kunit_resource_test_init_resources
|
||||
@@ -34,4 +35,4 @@
|
||||
[ 0.060000] Stack:
|
||||
[ 0.060000] 602086f8 601bc260 705c0000 705c0000
|
||||
[ 0.060000] 602086f8 6005fcec 705c0000 6002c6ab
|
||||
[ 0.060000] 6005fcec 601bc260 705c0000 3000000010
|
||||
[ 0.060000] 6005fcec 601bc260 705c0000 3000000010
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[ 0.060000] printk: console [mc-1] enabled
|
||||
[ 0.060000] random: get_random_bytes called from init_oops_id+0x35/0x40 with crng_init=0
|
||||
[ 0.060000] TAP version 14
|
||||
[ 0.060000] 1..3
|
||||
[ 0.060000] # Subtest: kunit-resource-test
|
||||
[ 0.060000] 1..5
|
||||
[ 0.060000] ok 1 - kunit_resource_test_init_resources
|
||||
@@ -22,4 +23,4 @@
|
||||
[ 0.060000] Stack:
|
||||
[ 0.060000] 602086f8 601bc260 705c0000 705c0000
|
||||
[ 0.060000] 602086f8 6005fcec 705c0000 6002c6ab
|
||||
[ 0.060000] 6005fcec 601bc260 705c0000 3000000010
|
||||
[ 0.060000] 6005fcec 601bc260 705c0000 3000000010
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[ 0.060000][ T1] printk: console [mc-1] enabled
|
||||
[ 0.060000][ T1] random: get_random_bytes called from init_oops_id+0x35/0x40 with crng_init=0
|
||||
[ 0.060000][ T1] TAP version 14
|
||||
[ 0.060000][ T1] 1..3
|
||||
[ 0.060000][ T1] # Subtest: kunit-resource-test
|
||||
[ 0.060000][ T1] 1..5
|
||||
[ 0.060000][ T1] ok 1 - kunit_resource_test_init_resources
|
||||
@@ -28,4 +29,4 @@
|
||||
[ 0.060000][ T1] Stack:
|
||||
[ 0.060000][ T1] 602086f8 601bc260 705c0000 705c0000
|
||||
[ 0.060000][ T1] 602086f8 6005fcec 705c0000 6002c6ab
|
||||
[ 0.060000][ T1] 6005fcec 601bc260 705c0000 3000000010
|
||||
[ 0.060000][ T1] 6005fcec 601bc260 705c0000 3000000010
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
printk: console [mc-1] enabled
|
||||
random: get_random_bytes called from init_oops_id+0x35/0x40 with crng_init=0
|
||||
TAP version 14
|
||||
1..3
|
||||
# Subtest: kunit-resource-test
|
||||
1..5
|
||||
ok 1 - kunit_resource_test_init_resources
|
||||
@@ -30,4 +31,4 @@
|
||||
Stack:
|
||||
602086f8 601bc260 705c0000 705c0000
|
||||
602086f8 6005fcec 705c0000 6002c6ab
|
||||
6005fcec 601bc260 705c0000 3000000010
|
||||
6005fcec 601bc260 705c0000 3000000010
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[ 0.060000] printk: console [mc-1] enabled
|
||||
[ 0.060000] random: get_random_bytes called from init_oops_id+0x35/0x40 with crng_init=0
|
||||
[ 0.060000] TAP version 14
|
||||
[ 0.060000] 1..3
|
||||
[ 0.060000] # Subtest: kunit-resource-test
|
||||
[ 0.060000] 1..5
|
||||
[ 0.060000] ok 1 - kunit_resource_test_init_resources
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2020 Tessares SA <http://www.tessares.net> */
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "test_map_init.skel.h"
|
||||
|
||||
#define TEST_VALUE 0x1234
|
||||
#define FILL_VALUE 0xdeadbeef
|
||||
|
||||
static int nr_cpus;
|
||||
static int duration;
|
||||
|
||||
typedef unsigned long long map_key_t;
|
||||
typedef unsigned long long map_value_t;
|
||||
typedef struct {
|
||||
map_value_t v; /* padding */
|
||||
} __bpf_percpu_val_align pcpu_map_value_t;
|
||||
|
||||
|
||||
static int map_populate(int map_fd, int num)
|
||||
{
|
||||
pcpu_map_value_t value[nr_cpus];
|
||||
int i, err;
|
||||
map_key_t key;
|
||||
|
||||
for (i = 0; i < nr_cpus; i++)
|
||||
bpf_percpu(value, i) = FILL_VALUE;
|
||||
|
||||
for (key = 1; key <= num; key++) {
|
||||
err = bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST);
|
||||
if (!ASSERT_OK(err, "bpf_map_update_elem"))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct test_map_init *setup(enum bpf_map_type map_type, int map_sz,
|
||||
int *map_fd, int populate)
|
||||
{
|
||||
struct test_map_init *skel;
|
||||
int err;
|
||||
|
||||
skel = test_map_init__open();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_open"))
|
||||
return NULL;
|
||||
|
||||
err = bpf_map__set_type(skel->maps.hashmap1, map_type);
|
||||
if (!ASSERT_OK(err, "bpf_map__set_type"))
|
||||
goto error;
|
||||
|
||||
err = bpf_map__set_max_entries(skel->maps.hashmap1, map_sz);
|
||||
if (!ASSERT_OK(err, "bpf_map__set_max_entries"))
|
||||
goto error;
|
||||
|
||||
err = test_map_init__load(skel);
|
||||
if (!ASSERT_OK(err, "skel_load"))
|
||||
goto error;
|
||||
|
||||
*map_fd = bpf_map__fd(skel->maps.hashmap1);
|
||||
if (CHECK(*map_fd < 0, "bpf_map__fd", "failed\n"))
|
||||
goto error;
|
||||
|
||||
err = map_populate(*map_fd, populate);
|
||||
if (!ASSERT_OK(err, "map_populate"))
|
||||
goto error_map;
|
||||
|
||||
return skel;
|
||||
|
||||
error_map:
|
||||
close(*map_fd);
|
||||
error:
|
||||
test_map_init__destroy(skel);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* executes bpf program that updates map with key, value */
|
||||
static int prog_run_insert_elem(struct test_map_init *skel, map_key_t key,
|
||||
map_value_t value)
|
||||
{
|
||||
struct test_map_init__bss *bss;
|
||||
|
||||
bss = skel->bss;
|
||||
|
||||
bss->inKey = key;
|
||||
bss->inValue = value;
|
||||
bss->inPid = getpid();
|
||||
|
||||
if (!ASSERT_OK(test_map_init__attach(skel), "skel_attach"))
|
||||
return -1;
|
||||
|
||||
/* Let tracepoint trigger */
|
||||
syscall(__NR_getpgid);
|
||||
|
||||
test_map_init__detach(skel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_values_one_cpu(pcpu_map_value_t *value, map_value_t expected)
|
||||
{
|
||||
int i, nzCnt = 0;
|
||||
map_value_t val;
|
||||
|
||||
for (i = 0; i < nr_cpus; i++) {
|
||||
val = bpf_percpu(value, i);
|
||||
if (val) {
|
||||
if (CHECK(val != expected, "map value",
|
||||
"unexpected for cpu %d: 0x%llx\n", i, val))
|
||||
return -1;
|
||||
nzCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (CHECK(nzCnt != 1, "map value", "set for %d CPUs instead of 1!\n",
|
||||
nzCnt))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add key=1 elem with values set for all CPUs
|
||||
* Delete elem key=1
|
||||
* Run bpf prog that inserts new key=1 elem with value=0x1234
|
||||
* (bpf prog can only set value for current CPU)
|
||||
* Lookup Key=1 and check value is as expected for all CPUs:
|
||||
* value set by bpf prog for one CPU, 0 for all others
|
||||
*/
|
||||
static void test_pcpu_map_init(void)
|
||||
{
|
||||
pcpu_map_value_t value[nr_cpus];
|
||||
struct test_map_init *skel;
|
||||
int map_fd, err;
|
||||
map_key_t key;
|
||||
|
||||
/* max 1 elem in map so insertion is forced to reuse freed entry */
|
||||
skel = setup(BPF_MAP_TYPE_PERCPU_HASH, 1, &map_fd, 1);
|
||||
if (!ASSERT_OK_PTR(skel, "prog_setup"))
|
||||
return;
|
||||
|
||||
/* delete element so the entry can be re-used*/
|
||||
key = 1;
|
||||
err = bpf_map_delete_elem(map_fd, &key);
|
||||
if (!ASSERT_OK(err, "bpf_map_delete_elem"))
|
||||
goto cleanup;
|
||||
|
||||
/* run bpf prog that inserts new elem, re-using the slot just freed */
|
||||
err = prog_run_insert_elem(skel, key, TEST_VALUE);
|
||||
if (!ASSERT_OK(err, "prog_run_insert_elem"))
|
||||
goto cleanup;
|
||||
|
||||
/* check that key=1 was re-created by bpf prog */
|
||||
err = bpf_map_lookup_elem(map_fd, &key, value);
|
||||
if (!ASSERT_OK(err, "bpf_map_lookup_elem"))
|
||||
goto cleanup;
|
||||
|
||||
/* and has expected values */
|
||||
check_values_one_cpu(value, TEST_VALUE);
|
||||
|
||||
cleanup:
|
||||
test_map_init__destroy(skel);
|
||||
}
|
||||
|
||||
/* Add key=1 and key=2 elems with values set for all CPUs
|
||||
* Run bpf prog that inserts new key=3 elem
|
||||
* (only for current cpu; other cpus should have initial value = 0)
|
||||
* Lookup Key=1 and check value is as expected for all CPUs
|
||||
*/
|
||||
static void test_pcpu_lru_map_init(void)
|
||||
{
|
||||
pcpu_map_value_t value[nr_cpus];
|
||||
struct test_map_init *skel;
|
||||
int map_fd, err;
|
||||
map_key_t key;
|
||||
|
||||
/* Set up LRU map with 2 elements, values filled for all CPUs.
|
||||
* With these 2 elements, the LRU map is full
|
||||
*/
|
||||
skel = setup(BPF_MAP_TYPE_LRU_PERCPU_HASH, 2, &map_fd, 2);
|
||||
if (!ASSERT_OK_PTR(skel, "prog_setup"))
|
||||
return;
|
||||
|
||||
/* run bpf prog that inserts new key=3 element, re-using LRU slot */
|
||||
key = 3;
|
||||
err = prog_run_insert_elem(skel, key, TEST_VALUE);
|
||||
if (!ASSERT_OK(err, "prog_run_insert_elem"))
|
||||
goto cleanup;
|
||||
|
||||
/* check that key=3 replaced one of earlier elements */
|
||||
err = bpf_map_lookup_elem(map_fd, &key, value);
|
||||
if (!ASSERT_OK(err, "bpf_map_lookup_elem"))
|
||||
goto cleanup;
|
||||
|
||||
/* and has expected values */
|
||||
check_values_one_cpu(value, TEST_VALUE);
|
||||
|
||||
cleanup:
|
||||
test_map_init__destroy(skel);
|
||||
}
|
||||
|
||||
void test_map_init(void)
|
||||
{
|
||||
nr_cpus = bpf_num_possible_cpus();
|
||||
if (nr_cpus <= 1) {
|
||||
printf("%s:SKIP: >1 cpu needed for this test\n", __func__);
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (test__start_subtest("pcpu_map_init"))
|
||||
test_pcpu_map_init();
|
||||
if (test__start_subtest("pcpu_lru_map_init"))
|
||||
test_pcpu_lru_map_init();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -243,7 +243,10 @@ static ino_t get_inode_from_kernfs(struct kernfs_node* node)
|
||||
}
|
||||
}
|
||||
|
||||
int pids_cgrp_id = 1;
|
||||
extern bool CONFIG_CGROUP_PIDS __kconfig __weak;
|
||||
enum cgroup_subsys_id___local {
|
||||
pids_cgrp_id___local = 123, /* value doesn't matter */
|
||||
};
|
||||
|
||||
static INLINE void* populate_cgroup_info(struct cgroup_data_t* cgroup_data,
|
||||
struct task_struct* task,
|
||||
@@ -253,7 +256,9 @@ static INLINE void* populate_cgroup_info(struct cgroup_data_t* cgroup_data,
|
||||
BPF_CORE_READ(task, nsproxy, cgroup_ns, root_cset, dfl_cgrp, kn);
|
||||
struct kernfs_node* proc_kernfs = BPF_CORE_READ(task, cgroups, dfl_cgrp, kn);
|
||||
|
||||
if (ENABLE_CGROUP_V1_RESOLVER) {
|
||||
if (ENABLE_CGROUP_V1_RESOLVER && CONFIG_CGROUP_PIDS) {
|
||||
int cgrp_id = bpf_core_enum_value(enum cgroup_subsys_id___local,
|
||||
pids_cgrp_id___local);
|
||||
#ifdef UNROLL
|
||||
#pragma unroll
|
||||
#endif
|
||||
@@ -262,7 +267,7 @@ static INLINE void* populate_cgroup_info(struct cgroup_data_t* cgroup_data,
|
||||
BPF_CORE_READ(task, cgroups, subsys[i]);
|
||||
if (subsys != NULL) {
|
||||
int subsys_id = BPF_CORE_READ(subsys, ss, id);
|
||||
if (subsys_id == pids_cgrp_id) {
|
||||
if (subsys_id == cgrp_id) {
|
||||
proc_kernfs = BPF_CORE_READ(subsys, cgroup, kn);
|
||||
root_kernfs = BPF_CORE_READ(subsys, ss, root, kf_root, kn);
|
||||
break;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Tessares SA <http://www.tessares.net> */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
__u64 inKey = 0;
|
||||
__u64 inValue = 0;
|
||||
__u32 inPid = 0;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, __u64);
|
||||
__type(value, __u64);
|
||||
} hashmap1 SEC(".maps");
|
||||
|
||||
|
||||
SEC("tp/syscalls/sys_enter_getpgid")
|
||||
int sysenter_getpgid(const void *ctx)
|
||||
{
|
||||
/* Just do it for once, when called from our own test prog. This
|
||||
* ensures the map value is only updated for a single CPU.
|
||||
*/
|
||||
int cur_pid = bpf_get_current_pid_tgid() >> 32;
|
||||
|
||||
if (cur_pid == inPid)
|
||||
bpf_map_update_elem(&hashmap1, &inKey, &inValue, BPF_NOEXIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -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";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -145,7 +145,7 @@ TEST(clone3_cap_checkpoint_restore)
|
||||
test_clone3_supported();
|
||||
|
||||
EXPECT_EQ(getuid(), 0)
|
||||
XFAIL(return, "Skipping all tests as non-root\n");
|
||||
SKIP(return, "Skipping all tests as non-root");
|
||||
|
||||
memset(&set_tid, 0, sizeof(set_tid));
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ TEST(close_range)
|
||||
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||
ASSERT_GE(fd, 0) {
|
||||
if (errno == ENOENT)
|
||||
XFAIL(return, "Skipping test since /dev/null does not exist");
|
||||
SKIP(return, "Skipping test since /dev/null does not exist");
|
||||
}
|
||||
|
||||
open_fds[i] = fd;
|
||||
@@ -52,7 +52,7 @@ TEST(close_range)
|
||||
|
||||
EXPECT_EQ(-1, sys_close_range(open_fds[0], open_fds[100], -1)) {
|
||||
if (errno == ENOSYS)
|
||||
XFAIL(return, "close_range() syscall not supported");
|
||||
SKIP(return, "close_range() syscall not supported");
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, sys_close_range(open_fds[0], open_fds[50], 0));
|
||||
@@ -108,7 +108,7 @@ TEST(close_range_unshare)
|
||||
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||
ASSERT_GE(fd, 0) {
|
||||
if (errno == ENOENT)
|
||||
XFAIL(return, "Skipping test since /dev/null does not exist");
|
||||
SKIP(return, "Skipping test since /dev/null does not exist");
|
||||
}
|
||||
|
||||
open_fds[i] = fd;
|
||||
@@ -197,7 +197,7 @@ TEST(close_range_unshare_capped)
|
||||
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||
ASSERT_GE(fd, 0) {
|
||||
if (errno == ENOENT)
|
||||
XFAIL(return, "Skipping test since /dev/null does not exist");
|
||||
SKIP(return, "Skipping test since /dev/null does not exist");
|
||||
}
|
||||
|
||||
open_fds[i] = fd;
|
||||
|
||||
@@ -74,7 +74,7 @@ static int __do_binderfs_test(struct __test_metadata *_metadata)
|
||||
ret = mount(NULL, binderfs_mntpt, "binder", 0, 0);
|
||||
EXPECT_EQ(ret, 0) {
|
||||
if (errno == ENODEV)
|
||||
XFAIL(goto out, "binderfs missing");
|
||||
SKIP(goto out, "binderfs missing");
|
||||
TH_LOG("%s - Failed to mount binderfs", strerror(errno));
|
||||
goto rmdir;
|
||||
}
|
||||
@@ -475,10 +475,10 @@ TEST(binderfs_stress)
|
||||
TEST(binderfs_test_privileged)
|
||||
{
|
||||
if (geteuid() != 0)
|
||||
XFAIL(return, "Tests are not run as root. Skipping privileged tests");
|
||||
SKIP(return, "Tests are not run as root. Skipping privileged tests");
|
||||
|
||||
if (__do_binderfs_test(_metadata))
|
||||
XFAIL(return, "The Android binderfs filesystem is not available");
|
||||
SKIP(return, "The Android binderfs filesystem is not available");
|
||||
}
|
||||
|
||||
TEST(binderfs_test_unprivileged)
|
||||
@@ -511,7 +511,7 @@ TEST(binderfs_test_unprivileged)
|
||||
ret = wait_for_pid(pid);
|
||||
if (ret) {
|
||||
if (ret == 2)
|
||||
XFAIL(return, "The Android binderfs filesystem is not available");
|
||||
SKIP(return, "The Android binderfs filesystem is not available");
|
||||
ASSERT_EQ(ret, 0) {
|
||||
TH_LOG("wait_for_pid() failed");
|
||||
}
|
||||
|
||||
@@ -3282,4 +3282,99 @@ TEST(epoll60)
|
||||
close(ctx.epfd);
|
||||
}
|
||||
|
||||
struct epoll61_ctx {
|
||||
int epfd;
|
||||
int evfd;
|
||||
};
|
||||
|
||||
static void *epoll61_write_eventfd(void *ctx_)
|
||||
{
|
||||
struct epoll61_ctx *ctx = ctx_;
|
||||
int64_t l = 1;
|
||||
|
||||
usleep(10950);
|
||||
write(ctx->evfd, &l, sizeof(l));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *epoll61_epoll_with_timeout(void *ctx_)
|
||||
{
|
||||
struct epoll61_ctx *ctx = ctx_;
|
||||
struct epoll_event events[1];
|
||||
int n;
|
||||
|
||||
n = epoll_wait(ctx->epfd, events, 1, 11);
|
||||
/*
|
||||
* If epoll returned the eventfd, write on the eventfd to wake up the
|
||||
* blocking poller.
|
||||
*/
|
||||
if (n == 1) {
|
||||
int64_t l = 1;
|
||||
|
||||
write(ctx->evfd, &l, sizeof(l));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *epoll61_blocking_epoll(void *ctx_)
|
||||
{
|
||||
struct epoll61_ctx *ctx = ctx_;
|
||||
struct epoll_event events[1];
|
||||
|
||||
epoll_wait(ctx->epfd, events, 1, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST(epoll61)
|
||||
{
|
||||
struct epoll61_ctx ctx;
|
||||
struct epoll_event ev;
|
||||
int i, r;
|
||||
|
||||
ctx.epfd = epoll_create1(0);
|
||||
ASSERT_GE(ctx.epfd, 0);
|
||||
ctx.evfd = eventfd(0, EFD_NONBLOCK);
|
||||
ASSERT_GE(ctx.evfd, 0);
|
||||
|
||||
ev.events = EPOLLIN | EPOLLET | EPOLLERR | EPOLLHUP;
|
||||
ev.data.ptr = NULL;
|
||||
r = epoll_ctl(ctx.epfd, EPOLL_CTL_ADD, ctx.evfd, &ev);
|
||||
ASSERT_EQ(r, 0);
|
||||
|
||||
/*
|
||||
* We are testing a race. Repeat the test case 1000 times to make it
|
||||
* more likely to fail in case of a bug.
|
||||
*/
|
||||
for (i = 0; i < 1000; i++) {
|
||||
pthread_t threads[3];
|
||||
int n;
|
||||
|
||||
/*
|
||||
* Start 3 threads:
|
||||
* Thread 1 sleeps for 10.9ms and writes to the evenfd.
|
||||
* Thread 2 calls epoll with a timeout of 11ms.
|
||||
* Thread 3 calls epoll with a timeout of -1.
|
||||
*
|
||||
* The eventfd write by Thread 1 should either wakeup Thread 2
|
||||
* or Thread 3. If it wakes up Thread 2, Thread 2 writes on the
|
||||
* eventfd to wake up Thread 3.
|
||||
*
|
||||
* If no events are missed, all three threads should eventually
|
||||
* be joinable.
|
||||
*/
|
||||
ASSERT_EQ(pthread_create(&threads[0], NULL,
|
||||
epoll61_write_eventfd, &ctx), 0);
|
||||
ASSERT_EQ(pthread_create(&threads[1], NULL,
|
||||
epoll61_epoll_with_timeout, &ctx), 0);
|
||||
ASSERT_EQ(pthread_create(&threads[2], NULL,
|
||||
epoll61_blocking_epoll, &ctx), 0);
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(threads); ++n)
|
||||
ASSERT_EQ(pthread_join(threads[n], NULL), 0);
|
||||
}
|
||||
|
||||
close(ctx.epfd);
|
||||
close(ctx.evfd);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
echo 0 > events/enable
|
||||
echo > dynamic_events
|
||||
|
||||
PLACE=kernel_clone
|
||||
PLACE=$FUNCTION_FORK
|
||||
|
||||
echo "p:myevent1 $PLACE" >> dynamic_events
|
||||
echo "r:myevent2 $PLACE" >> dynamic_events
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
echo 0 > events/enable
|
||||
echo > dynamic_events
|
||||
|
||||
PLACE=kernel_clone
|
||||
PLACE=$FUNCTION_FORK
|
||||
|
||||
setup_events() {
|
||||
echo "p:myevent1 $PLACE" >> dynamic_events
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
echo 0 > events/enable
|
||||
echo > dynamic_events
|
||||
|
||||
PLACE=kernel_clone
|
||||
PLACE=$FUNCTION_FORK
|
||||
|
||||
setup_events() {
|
||||
echo "p:myevent1 $PLACE" >> dynamic_events
|
||||
|
||||
@@ -39,7 +39,7 @@ do_test() {
|
||||
disable_tracing
|
||||
|
||||
echo do_execve* > set_ftrace_filter
|
||||
echo *do_fork >> set_ftrace_filter
|
||||
echo $FUNCTION_FORK >> set_ftrace_filter
|
||||
|
||||
echo $PID > set_ftrace_notrace_pid
|
||||
echo function > current_tracer
|
||||
|
||||
@@ -39,7 +39,7 @@ do_test() {
|
||||
disable_tracing
|
||||
|
||||
echo do_execve* > set_ftrace_filter
|
||||
echo *do_fork >> set_ftrace_filter
|
||||
echo $FUNCTION_FORK >> set_ftrace_filter
|
||||
|
||||
echo $PID > set_ftrace_pid
|
||||
echo function > current_tracer
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
# requires: set_ftrace_filter
|
||||
# flags: instance
|
||||
|
||||
echo kernel_clone:stacktrace >> set_ftrace_filter
|
||||
echo $FUNCTION_FORK:stacktrace >> set_ftrace_filter
|
||||
|
||||
grep -q "kernel_clone:stacktrace:unlimited" set_ftrace_filter
|
||||
grep -q "$FUNCTION_FORK:stacktrace:unlimited" set_ftrace_filter
|
||||
|
||||
(echo "forked"; sleep 1)
|
||||
|
||||
|
||||
@@ -133,6 +133,13 @@ yield() {
|
||||
ping $LOCALHOST -c 1 || sleep .001 || usleep 1 || sleep 1
|
||||
}
|
||||
|
||||
# The fork function in the kernel was renamed from "_do_fork" to
|
||||
# "kernel_fork". As older tests should still work with older kernels
|
||||
# as well as newer kernels, check which version of fork is used on this
|
||||
# kernel so that the tests can use the fork function for the running kernel.
|
||||
FUNCTION_FORK=`(if grep '\bkernel_clone\b' /proc/kallsyms > /dev/null; then
|
||||
echo kernel_clone; else echo '_do_fork'; fi)`
|
||||
|
||||
# Since probe event command may include backslash, explicitly use printf "%s"
|
||||
# to NOT interpret it.
|
||||
ftrace_errlog_check() { # err-prefix command-with-error-pos-by-^ command-file
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# description: Kprobe dynamic event - adding and removing
|
||||
# requires: kprobe_events
|
||||
|
||||
echo p:myevent kernel_clone > kprobe_events
|
||||
echo p:myevent $FUNCTION_FORK > kprobe_events
|
||||
grep myevent kprobe_events
|
||||
test -d events/kprobes/myevent
|
||||
echo > kprobe_events
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# description: Kprobe dynamic event - busy event check
|
||||
# requires: kprobe_events
|
||||
|
||||
echo p:myevent kernel_clone > kprobe_events
|
||||
echo p:myevent $FUNCTION_FORK > kprobe_events
|
||||
test -d events/kprobes/myevent
|
||||
echo 1 > events/kprobes/myevent/enable
|
||||
echo > kprobe_events && exit_fail # this must fail
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
# description: Kprobe dynamic event with arguments
|
||||
# requires: kprobe_events
|
||||
|
||||
echo 'p:testprobe kernel_clone $stack $stack0 +0($stack)' > kprobe_events
|
||||
echo "p:testprobe $FUNCTION_FORK \$stack \$stack0 +0(\$stack)" > kprobe_events
|
||||
grep testprobe kprobe_events | grep -q 'arg1=\$stack arg2=\$stack0 arg3=+0(\$stack)'
|
||||
test -d events/kprobes/testprobe
|
||||
|
||||
echo 1 > events/kprobes/testprobe/enable
|
||||
( echo "forked")
|
||||
grep testprobe trace | grep 'kernel_clone' | \
|
||||
grep testprobe trace | grep "$FUNCTION_FORK" | \
|
||||
grep -q 'arg1=0x[[:xdigit:]]* arg2=0x[[:xdigit:]]* arg3=0x[[:xdigit:]]*$'
|
||||
|
||||
echo 0 > events/kprobes/testprobe/enable
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
grep -A1 "fetcharg:" README | grep -q "\$comm" || exit_unsupported # this is too old
|
||||
|
||||
echo 'p:testprobe kernel_clone comm=$comm ' > kprobe_events
|
||||
echo "p:testprobe $FUNCTION_FORK comm=\$comm " > kprobe_events
|
||||
grep testprobe kprobe_events | grep -q 'comm=$comm'
|
||||
test -d events/kprobes/testprobe
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@ esac
|
||||
: "Test get argument (1)"
|
||||
echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):string" > kprobe_events
|
||||
echo 1 > events/kprobes/testprobe/enable
|
||||
echo "p:test kernel_clone" >> kprobe_events
|
||||
echo "p:test $FUNCTION_FORK" >> kprobe_events
|
||||
grep -qe "testprobe.* arg1=\"test\"" trace
|
||||
|
||||
echo 0 > events/kprobes/testprobe/enable
|
||||
: "Test get argument (2)"
|
||||
echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):string arg2=+0(${ARG1}):string" > kprobe_events
|
||||
echo 1 > events/kprobes/testprobe/enable
|
||||
echo "p:test kernel_clone" >> kprobe_events
|
||||
echo "p:test $FUNCTION_FORK" >> kprobe_events
|
||||
grep -qe "testprobe.* arg1=\"test\" arg2=\"test\"" trace
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ elif ! grep "$SYMBOL\$" /proc/kallsyms; then
|
||||
fi
|
||||
|
||||
: "Test get basic types symbol argument"
|
||||
echo "p:testprobe_u kernel_clone arg1=@linux_proc_banner:u64 arg2=@linux_proc_banner:u32 arg3=@linux_proc_banner:u16 arg4=@linux_proc_banner:u8" > kprobe_events
|
||||
echo "p:testprobe_s kernel_clone arg1=@linux_proc_banner:s64 arg2=@linux_proc_banner:s32 arg3=@linux_proc_banner:s16 arg4=@linux_proc_banner:s8" >> kprobe_events
|
||||
echo "p:testprobe_u $FUNCTION_FORK arg1=@linux_proc_banner:u64 arg2=@linux_proc_banner:u32 arg3=@linux_proc_banner:u16 arg4=@linux_proc_banner:u8" > kprobe_events
|
||||
echo "p:testprobe_s $FUNCTION_FORK arg1=@linux_proc_banner:s64 arg2=@linux_proc_banner:s32 arg3=@linux_proc_banner:s16 arg4=@linux_proc_banner:s8" >> kprobe_events
|
||||
if grep -q "x8/16/32/64" README; then
|
||||
echo "p:testprobe_x kernel_clone arg1=@linux_proc_banner:x64 arg2=@linux_proc_banner:x32 arg3=@linux_proc_banner:x16 arg4=@linux_proc_banner:x8" >> kprobe_events
|
||||
echo "p:testprobe_x $FUNCTION_FORK arg1=@linux_proc_banner:x64 arg2=@linux_proc_banner:x32 arg3=@linux_proc_banner:x16 arg4=@linux_proc_banner:x8" >> kprobe_events
|
||||
fi
|
||||
echo "p:testprobe_bf kernel_clone arg1=@linux_proc_banner:b8@4/32" >> kprobe_events
|
||||
echo "p:testprobe_bf $FUNCTION_FORK arg1=@linux_proc_banner:b8@4/32" >> kprobe_events
|
||||
echo 1 > events/kprobes/enable
|
||||
(echo "forked")
|
||||
echo 0 > events/kprobes/enable
|
||||
@@ -27,7 +27,7 @@ grep "testprobe_[usx]:.* arg1=.* arg2=.* arg3=.* arg4=.*" trace
|
||||
grep "testprobe_bf:.* arg1=.*" trace
|
||||
|
||||
: "Test get string symbol argument"
|
||||
echo "p:testprobe_str kernel_clone arg1=@linux_proc_banner:string" > kprobe_events
|
||||
echo "p:testprobe_str $FUNCTION_FORK arg1=@linux_proc_banner:string" > kprobe_events
|
||||
echo 1 > events/kprobes/enable
|
||||
(echo "forked")
|
||||
echo 0 > events/kprobes/enable
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# requires: kprobe_events "x8/16/32/64":README
|
||||
|
||||
gen_event() { # Bitsize
|
||||
echo "p:testprobe kernel_clone \$stack0:s$1 \$stack0:u$1 \$stack0:x$1 \$stack0:b4@4/$1"
|
||||
echo "p:testprobe $FUNCTION_FORK \$stack0:s$1 \$stack0:u$1 \$stack0:x$1 \$stack0:b4@4/$1"
|
||||
}
|
||||
|
||||
check_types() { # s-type u-type x-type bf-type width
|
||||
|
||||
@@ -9,12 +9,16 @@ grep -A10 "fetcharg:" README | grep -q '\[u\]<offset>' || exit_unsupported
|
||||
:;: "user-memory access syntax and ustring working on user memory";:
|
||||
echo 'p:myevent do_sys_open path=+0($arg2):ustring path2=+u0($arg2):string' \
|
||||
> kprobe_events
|
||||
echo 'p:myevent2 do_sys_openat2 path=+0($arg2):ustring path2=+u0($arg2):string' \
|
||||
>> kprobe_events
|
||||
|
||||
grep myevent kprobe_events | \
|
||||
grep -q 'path=+0($arg2):ustring path2=+u0($arg2):string'
|
||||
echo 1 > events/kprobes/myevent/enable
|
||||
echo 1 > events/kprobes/myevent2/enable
|
||||
echo > /dev/null
|
||||
echo 0 > events/kprobes/myevent/enable
|
||||
echo 0 > events/kprobes/myevent2/enable
|
||||
|
||||
grep myevent trace | grep -q 'path="/dev/null" path2="/dev/null"'
|
||||
|
||||
|
||||
@@ -5,29 +5,29 @@
|
||||
|
||||
# prepare
|
||||
echo nop > current_tracer
|
||||
echo kernel_clone > set_ftrace_filter
|
||||
echo 'p:testprobe kernel_clone' > kprobe_events
|
||||
echo $FUNCTION_FORK > set_ftrace_filter
|
||||
echo "p:testprobe $FUNCTION_FORK" > kprobe_events
|
||||
|
||||
# kprobe on / ftrace off
|
||||
echo 1 > events/kprobes/testprobe/enable
|
||||
echo > trace
|
||||
( echo "forked")
|
||||
grep testprobe trace
|
||||
! grep 'kernel_clone <-' trace
|
||||
! grep "$FUNCTION_FORK <-" trace
|
||||
|
||||
# kprobe on / ftrace on
|
||||
echo function > current_tracer
|
||||
echo > trace
|
||||
( echo "forked")
|
||||
grep testprobe trace
|
||||
grep 'kernel_clone <-' trace
|
||||
grep "$FUNCTION_FORK <-" trace
|
||||
|
||||
# kprobe off / ftrace on
|
||||
echo 0 > events/kprobes/testprobe/enable
|
||||
echo > trace
|
||||
( echo "forked")
|
||||
! grep testprobe trace
|
||||
grep 'kernel_clone <-' trace
|
||||
grep "$FUNCTION_FORK <-" trace
|
||||
|
||||
# kprobe on / ftrace on
|
||||
echo 1 > events/kprobes/testprobe/enable
|
||||
@@ -35,11 +35,11 @@ echo function > current_tracer
|
||||
echo > trace
|
||||
( echo "forked")
|
||||
grep testprobe trace
|
||||
grep 'kernel_clone <-' trace
|
||||
grep "$FUNCTION_FORK <-" trace
|
||||
|
||||
# kprobe on / ftrace off
|
||||
echo nop > current_tracer
|
||||
echo > trace
|
||||
( echo "forked")
|
||||
grep testprobe trace
|
||||
! grep 'kernel_clone <-' trace
|
||||
! grep "$FUNCTION_FORK <-" trace
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# requires: kprobe_events "Create/append/":README
|
||||
|
||||
# Choose 2 symbols for target
|
||||
SYM1=kernel_clone
|
||||
SYM1=$FUNCTION_FORK
|
||||
SYM2=do_exit
|
||||
EVENT_NAME=kprobes/testevent
|
||||
|
||||
|
||||
@@ -86,15 +86,15 @@ esac
|
||||
|
||||
# multiprobe errors
|
||||
if grep -q "Create/append/" README && grep -q "imm-value" README; then
|
||||
echo 'p:kprobes/testevent kernel_clone' > kprobe_events
|
||||
echo "p:kprobes/testevent $FUNCTION_FORK" > kprobe_events
|
||||
check_error '^r:kprobes/testevent do_exit' # DIFF_PROBE_TYPE
|
||||
|
||||
# Explicitly use printf "%s" to not interpret \1
|
||||
printf "%s" 'p:kprobes/testevent kernel_clone abcd=\1' > kprobe_events
|
||||
check_error 'p:kprobes/testevent kernel_clone ^bcd=\1' # DIFF_ARG_TYPE
|
||||
check_error 'p:kprobes/testevent kernel_clone ^abcd=\1:u8' # DIFF_ARG_TYPE
|
||||
check_error 'p:kprobes/testevent kernel_clone ^abcd=\"foo"' # DIFF_ARG_TYPE
|
||||
check_error '^p:kprobes/testevent kernel_clone abcd=\1' # SAME_PROBE
|
||||
printf "%s" "p:kprobes/testevent $FUNCTION_FORK abcd=\\1" > kprobe_events
|
||||
check_error "p:kprobes/testevent $FUNCTION_FORK ^bcd=\\1" # DIFF_ARG_TYPE
|
||||
check_error "p:kprobes/testevent $FUNCTION_FORK ^abcd=\\1:u8" # DIFF_ARG_TYPE
|
||||
check_error "p:kprobes/testevent $FUNCTION_FORK ^abcd=\\\"foo\"" # DIFF_ARG_TYPE
|
||||
check_error "^p:kprobes/testevent $FUNCTION_FORK abcd=\\1" # SAME_PROBE
|
||||
fi
|
||||
|
||||
# %return suffix errors
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
# requires: kprobe_events
|
||||
|
||||
# Add new kretprobe event
|
||||
echo 'r:testprobe2 kernel_clone $retval' > kprobe_events
|
||||
echo "r:testprobe2 $FUNCTION_FORK \$retval" > kprobe_events
|
||||
grep testprobe2 kprobe_events | grep -q 'arg1=\$retval'
|
||||
test -d events/kprobes/testprobe2
|
||||
|
||||
echo 1 > events/kprobes/testprobe2/enable
|
||||
( echo "forked")
|
||||
|
||||
cat trace | grep testprobe2 | grep -q '<- kernel_clone'
|
||||
cat trace | grep testprobe2 | grep -q "<- $FUNCTION_FORK"
|
||||
|
||||
echo 0 > events/kprobes/testprobe2/enable
|
||||
echo '-:testprobe2' >> kprobe_events
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# requires: kprobe_events
|
||||
|
||||
! grep -q 'myevent' kprobe_profile
|
||||
echo p:myevent kernel_clone > kprobe_events
|
||||
echo "p:myevent $FUNCTION_FORK" > kprobe_events
|
||||
grep -q 'myevent[[:space:]]*0[[:space:]]*0$' kprobe_profile
|
||||
echo 1 > events/kprobes/myevent/enable
|
||||
( echo "forked" )
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
snprintf(_metadata->results->reason, \
|
||||
sizeof(_metadata->results->reason), fmt, ##__VA_ARGS__); \
|
||||
if (TH_LOG_ENABLED) { \
|
||||
fprintf(TH_LOG_STREAM, "# SKIP %s\n", \
|
||||
fprintf(TH_LOG_STREAM, "# SKIP %s\n", \
|
||||
_metadata->results->reason); \
|
||||
} \
|
||||
_metadata->passed = 1; \
|
||||
@@ -432,7 +432,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* ASSERT_EQ(expected, seen)
|
||||
* ASSERT_EQ()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -443,7 +443,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, ==, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_NE(expected, seen)
|
||||
* ASSERT_NE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -454,7 +454,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, !=, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_LT(expected, seen)
|
||||
* ASSERT_LT()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -465,7 +465,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, <, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_LE(expected, seen)
|
||||
* ASSERT_LE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -476,7 +476,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, <=, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_GT(expected, seen)
|
||||
* ASSERT_GT()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -487,7 +487,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, >, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_GE(expected, seen)
|
||||
* ASSERT_GE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -498,7 +498,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, >=, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_NULL(seen)
|
||||
* ASSERT_NULL()
|
||||
*
|
||||
* @seen: measured value
|
||||
*
|
||||
@@ -508,7 +508,7 @@
|
||||
__EXPECT(NULL, "NULL", seen, #seen, ==, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_TRUE(seen)
|
||||
* ASSERT_TRUE()
|
||||
*
|
||||
* @seen: measured value
|
||||
*
|
||||
@@ -518,7 +518,7 @@
|
||||
__EXPECT(0, "0", seen, #seen, !=, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_FALSE(seen)
|
||||
* ASSERT_FALSE()
|
||||
*
|
||||
* @seen: measured value
|
||||
*
|
||||
@@ -528,7 +528,7 @@
|
||||
__EXPECT(0, "0", seen, #seen, ==, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_STREQ(expected, seen)
|
||||
* ASSERT_STREQ()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -539,7 +539,7 @@
|
||||
__EXPECT_STR(expected, seen, ==, 1)
|
||||
|
||||
/**
|
||||
* ASSERT_STRNE(expected, seen)
|
||||
* ASSERT_STRNE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -550,7 +550,7 @@
|
||||
__EXPECT_STR(expected, seen, !=, 1)
|
||||
|
||||
/**
|
||||
* EXPECT_EQ(expected, seen)
|
||||
* EXPECT_EQ()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -561,7 +561,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, ==, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_NE(expected, seen)
|
||||
* EXPECT_NE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -572,7 +572,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, !=, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_LT(expected, seen)
|
||||
* EXPECT_LT()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -583,7 +583,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, <, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_LE(expected, seen)
|
||||
* EXPECT_LE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -594,7 +594,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, <=, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_GT(expected, seen)
|
||||
* EXPECT_GT()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -605,7 +605,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, >, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_GE(expected, seen)
|
||||
* EXPECT_GE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -616,7 +616,7 @@
|
||||
__EXPECT(expected, #expected, seen, #seen, >=, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_NULL(seen)
|
||||
* EXPECT_NULL()
|
||||
*
|
||||
* @seen: measured value
|
||||
*
|
||||
@@ -626,7 +626,7 @@
|
||||
__EXPECT(NULL, "NULL", seen, #seen, ==, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_TRUE(seen)
|
||||
* EXPECT_TRUE()
|
||||
*
|
||||
* @seen: measured value
|
||||
*
|
||||
@@ -636,7 +636,7 @@
|
||||
__EXPECT(0, "0", seen, #seen, !=, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_FALSE(seen)
|
||||
* EXPECT_FALSE()
|
||||
*
|
||||
* @seen: measured value
|
||||
*
|
||||
@@ -646,7 +646,7 @@
|
||||
__EXPECT(0, "0", seen, #seen, ==, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_STREQ(expected, seen)
|
||||
* EXPECT_STREQ()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
@@ -657,7 +657,7 @@
|
||||
__EXPECT_STR(expected, seen, ==, 0)
|
||||
|
||||
/**
|
||||
* EXPECT_STRNE(expected, seen)
|
||||
* EXPECT_STRNE()
|
||||
*
|
||||
* @expected: expected value
|
||||
* @seen: measured value
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
/aarch64/get-reg-list
|
||||
/aarch64/get-reg-list-sve
|
||||
/s390x/memop
|
||||
/s390x/resets
|
||||
/s390x/sync_regs_test
|
||||
/x86_64/cr4_cpuid_sync_test
|
||||
/x86_64/debug_regs
|
||||
/x86_64/evmcs_test
|
||||
/x86_64/kvm_pv_test
|
||||
/x86_64/hyperv_cpuid
|
||||
/x86_64/mmio_warning_test
|
||||
/x86_64/platform_info_test
|
||||
@@ -24,6 +27,7 @@
|
||||
/clear_dirty_log_test
|
||||
/demand_paging_test
|
||||
/dirty_log_test
|
||||
/dirty_log_perf_test
|
||||
/kvm_create_max_vcpus
|
||||
/set_memory_region_test
|
||||
/steal_time
|
||||
|
||||
@@ -34,13 +34,14 @@ ifeq ($(ARCH),s390)
|
||||
endif
|
||||
|
||||
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c
|
||||
LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c
|
||||
LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
|
||||
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
|
||||
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c
|
||||
|
||||
TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/kvm_pv_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
|
||||
@@ -58,14 +59,15 @@ TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/debug_regs
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/user_msr_test
|
||||
TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
|
||||
TEST_GEN_PROGS_x86_64 += demand_paging_test
|
||||
TEST_GEN_PROGS_x86_64 += dirty_log_test
|
||||
TEST_GEN_PROGS_x86_64 += dirty_log_perf_test
|
||||
TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus
|
||||
TEST_GEN_PROGS_x86_64 += set_memory_region_test
|
||||
TEST_GEN_PROGS_x86_64 += steal_time
|
||||
|
||||
TEST_GEN_PROGS_aarch64 += clear_dirty_log_test
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list-sve
|
||||
TEST_GEN_PROGS_aarch64 += demand_paging_test
|
||||
TEST_GEN_PROGS_aarch64 += dirty_log_test
|
||||
TEST_GEN_PROGS_aarch64 += kvm_create_max_vcpus
|
||||
@@ -111,14 +113,21 @@ LDFLAGS += -pthread $(no-pie-option) $(pgste-option)
|
||||
include ../lib.mk
|
||||
|
||||
STATIC_LIBS := $(OUTPUT)/libkvm.a
|
||||
LIBKVM_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM))
|
||||
EXTRA_CLEAN += $(LIBKVM_OBJ) $(STATIC_LIBS) cscope.*
|
||||
LIBKVM_C := $(filter %.c,$(LIBKVM))
|
||||
LIBKVM_S := $(filter %.S,$(LIBKVM))
|
||||
LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))
|
||||
LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))
|
||||
EXTRA_CLEAN += $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(STATIC_LIBS) cscope.*
|
||||
|
||||
x := $(shell mkdir -p $(sort $(dir $(LIBKVM_OBJ))))
|
||||
$(LIBKVM_OBJ): $(OUTPUT)/%.o: %.c
|
||||
x := $(shell mkdir -p $(sort $(dir $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ))))
|
||||
$(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
|
||||
|
||||
$(OUTPUT)/libkvm.a: $(LIBKVM_OBJ)
|
||||
$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
|
||||
|
||||
LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ)
|
||||
$(OUTPUT)/libkvm.a: $(LIBKVM_OBJS)
|
||||
$(AR) crs $@ $^
|
||||
|
||||
x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define REG_LIST_SVE
|
||||
#include "get-reg-list.c"
|
||||
@@ -0,0 +1,841 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Check for KVM_GET_REG_LIST regressions.
|
||||
*
|
||||
* Copyright (C) 2020, Red Hat, Inc.
|
||||
*
|
||||
* When attempting to migrate from a host with an older kernel to a host
|
||||
* with a newer kernel we allow the newer kernel on the destination to
|
||||
* list new registers with get-reg-list. We assume they'll be unused, at
|
||||
* least until the guest reboots, and so they're relatively harmless.
|
||||
* However, if the destination host with the newer kernel is missing
|
||||
* registers which the source host with the older kernel has, then that's
|
||||
* a regression in get-reg-list. This test checks for that regression by
|
||||
* checking the current list against a blessed list. We should never have
|
||||
* missing registers, but if new ones appear then they can probably be
|
||||
* added to the blessed list. A completely new blessed list can be created
|
||||
* by running the test with the --list command line argument.
|
||||
*
|
||||
* Note, the blessed list should be created from the oldest possible
|
||||
* kernel. We can't go older than v4.15, though, because that's the first
|
||||
* release to expose the ID system registers in KVM_GET_REG_LIST, see
|
||||
* commit 93390c0a1b20 ("arm64: KVM: Hide unsupported AArch64 CPU features
|
||||
* from guests"). Also, one must use the --core-reg-fixup command line
|
||||
* option when running on an older kernel that doesn't include df205b5c6328
|
||||
* ("KVM: arm64: Filter out invalid core register IDs in KVM_GET_REG_LIST")
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "kvm_util.h"
|
||||
#include "test_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
#ifdef REG_LIST_SVE
|
||||
#define reg_list_sve() (true)
|
||||
#else
|
||||
#define reg_list_sve() (false)
|
||||
#endif
|
||||
|
||||
#define REG_MASK (KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_COPROC_MASK)
|
||||
|
||||
#define for_each_reg(i) \
|
||||
for ((i) = 0; (i) < reg_list->n; ++(i))
|
||||
|
||||
#define for_each_missing_reg(i) \
|
||||
for ((i) = 0; (i) < blessed_n; ++(i)) \
|
||||
if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i]))
|
||||
|
||||
#define for_each_new_reg(i) \
|
||||
for ((i) = 0; (i) < reg_list->n; ++(i)) \
|
||||
if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
|
||||
|
||||
|
||||
static struct kvm_reg_list *reg_list;
|
||||
|
||||
static __u64 base_regs[], vregs[], sve_regs[], rejects_set[];
|
||||
static __u64 base_regs_n, vregs_n, sve_regs_n, rejects_set_n;
|
||||
static __u64 *blessed_reg, blessed_n;
|
||||
|
||||
static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_regs; ++i)
|
||||
if (reg == regs[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *str_with_index(const char *template, __u64 index)
|
||||
{
|
||||
char *str, *p;
|
||||
int n;
|
||||
|
||||
str = strdup(template);
|
||||
p = strstr(str, "##");
|
||||
n = sprintf(p, "%lld", index);
|
||||
strcat(p + n, strstr(template, "##") + 2);
|
||||
|
||||
return (const char *)str;
|
||||
}
|
||||
|
||||
#define CORE_REGS_XX_NR_WORDS 2
|
||||
#define CORE_SPSR_XX_NR_WORDS 2
|
||||
#define CORE_FPREGS_XX_NR_WORDS 4
|
||||
|
||||
static const char *core_id_to_str(__u64 id)
|
||||
{
|
||||
__u64 core_off = id & ~REG_MASK, idx;
|
||||
|
||||
/*
|
||||
* core_off is the offset into struct kvm_regs
|
||||
*/
|
||||
switch (core_off) {
|
||||
case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
|
||||
KVM_REG_ARM_CORE_REG(regs.regs[30]):
|
||||
idx = (core_off - KVM_REG_ARM_CORE_REG(regs.regs[0])) / CORE_REGS_XX_NR_WORDS;
|
||||
TEST_ASSERT(idx < 31, "Unexpected regs.regs index: %lld", idx);
|
||||
return str_with_index("KVM_REG_ARM_CORE_REG(regs.regs[##])", idx);
|
||||
case KVM_REG_ARM_CORE_REG(regs.sp):
|
||||
return "KVM_REG_ARM_CORE_REG(regs.sp)";
|
||||
case KVM_REG_ARM_CORE_REG(regs.pc):
|
||||
return "KVM_REG_ARM_CORE_REG(regs.pc)";
|
||||
case KVM_REG_ARM_CORE_REG(regs.pstate):
|
||||
return "KVM_REG_ARM_CORE_REG(regs.pstate)";
|
||||
case KVM_REG_ARM_CORE_REG(sp_el1):
|
||||
return "KVM_REG_ARM_CORE_REG(sp_el1)";
|
||||
case KVM_REG_ARM_CORE_REG(elr_el1):
|
||||
return "KVM_REG_ARM_CORE_REG(elr_el1)";
|
||||
case KVM_REG_ARM_CORE_REG(spsr[0]) ...
|
||||
KVM_REG_ARM_CORE_REG(spsr[KVM_NR_SPSR - 1]):
|
||||
idx = (core_off - KVM_REG_ARM_CORE_REG(spsr[0])) / CORE_SPSR_XX_NR_WORDS;
|
||||
TEST_ASSERT(idx < KVM_NR_SPSR, "Unexpected spsr index: %lld", idx);
|
||||
return str_with_index("KVM_REG_ARM_CORE_REG(spsr[##])", idx);
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ...
|
||||
KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]):
|
||||
idx = (core_off - KVM_REG_ARM_CORE_REG(fp_regs.vregs[0])) / CORE_FPREGS_XX_NR_WORDS;
|
||||
TEST_ASSERT(idx < 32, "Unexpected fp_regs.vregs index: %lld", idx);
|
||||
return str_with_index("KVM_REG_ARM_CORE_REG(fp_regs.vregs[##])", idx);
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.fpsr):
|
||||
return "KVM_REG_ARM_CORE_REG(fp_regs.fpsr)";
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.fpcr):
|
||||
return "KVM_REG_ARM_CORE_REG(fp_regs.fpcr)";
|
||||
}
|
||||
|
||||
TEST_FAIL("Unknown core reg id: 0x%llx", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *sve_id_to_str(__u64 id)
|
||||
{
|
||||
__u64 sve_off, n, i;
|
||||
|
||||
if (id == KVM_REG_ARM64_SVE_VLS)
|
||||
return "KVM_REG_ARM64_SVE_VLS";
|
||||
|
||||
sve_off = id & ~(REG_MASK | ((1ULL << 5) - 1));
|
||||
i = id & (KVM_ARM64_SVE_MAX_SLICES - 1);
|
||||
|
||||
TEST_ASSERT(i == 0, "Currently we don't expect slice > 0, reg id 0x%llx", id);
|
||||
|
||||
switch (sve_off) {
|
||||
case KVM_REG_ARM64_SVE_ZREG_BASE ...
|
||||
KVM_REG_ARM64_SVE_ZREG_BASE + (1ULL << 5) * KVM_ARM64_SVE_NUM_ZREGS - 1:
|
||||
n = (id >> 5) & (KVM_ARM64_SVE_NUM_ZREGS - 1);
|
||||
TEST_ASSERT(id == KVM_REG_ARM64_SVE_ZREG(n, 0),
|
||||
"Unexpected bits set in SVE ZREG id: 0x%llx", id);
|
||||
return str_with_index("KVM_REG_ARM64_SVE_ZREG(##, 0)", n);
|
||||
case KVM_REG_ARM64_SVE_PREG_BASE ...
|
||||
KVM_REG_ARM64_SVE_PREG_BASE + (1ULL << 5) * KVM_ARM64_SVE_NUM_PREGS - 1:
|
||||
n = (id >> 5) & (KVM_ARM64_SVE_NUM_PREGS - 1);
|
||||
TEST_ASSERT(id == KVM_REG_ARM64_SVE_PREG(n, 0),
|
||||
"Unexpected bits set in SVE PREG id: 0x%llx", id);
|
||||
return str_with_index("KVM_REG_ARM64_SVE_PREG(##, 0)", n);
|
||||
case KVM_REG_ARM64_SVE_FFR_BASE:
|
||||
TEST_ASSERT(id == KVM_REG_ARM64_SVE_FFR(0),
|
||||
"Unexpected bits set in SVE FFR id: 0x%llx", id);
|
||||
return "KVM_REG_ARM64_SVE_FFR(0)";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void print_reg(__u64 id)
|
||||
{
|
||||
unsigned op0, op1, crn, crm, op2;
|
||||
const char *reg_size = NULL;
|
||||
|
||||
TEST_ASSERT((id & KVM_REG_ARCH_MASK) == KVM_REG_ARM64,
|
||||
"KVM_REG_ARM64 missing in reg id: 0x%llx", id);
|
||||
|
||||
switch (id & KVM_REG_SIZE_MASK) {
|
||||
case KVM_REG_SIZE_U8:
|
||||
reg_size = "KVM_REG_SIZE_U8";
|
||||
break;
|
||||
case KVM_REG_SIZE_U16:
|
||||
reg_size = "KVM_REG_SIZE_U16";
|
||||
break;
|
||||
case KVM_REG_SIZE_U32:
|
||||
reg_size = "KVM_REG_SIZE_U32";
|
||||
break;
|
||||
case KVM_REG_SIZE_U64:
|
||||
reg_size = "KVM_REG_SIZE_U64";
|
||||
break;
|
||||
case KVM_REG_SIZE_U128:
|
||||
reg_size = "KVM_REG_SIZE_U128";
|
||||
break;
|
||||
case KVM_REG_SIZE_U256:
|
||||
reg_size = "KVM_REG_SIZE_U256";
|
||||
break;
|
||||
case KVM_REG_SIZE_U512:
|
||||
reg_size = "KVM_REG_SIZE_U512";
|
||||
break;
|
||||
case KVM_REG_SIZE_U1024:
|
||||
reg_size = "KVM_REG_SIZE_U1024";
|
||||
break;
|
||||
case KVM_REG_SIZE_U2048:
|
||||
reg_size = "KVM_REG_SIZE_U2048";
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unexpected reg size: 0x%llx in reg id: 0x%llx",
|
||||
(id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT, id);
|
||||
}
|
||||
|
||||
switch (id & KVM_REG_ARM_COPROC_MASK) {
|
||||
case KVM_REG_ARM_CORE:
|
||||
printf("\tKVM_REG_ARM64 | %s | KVM_REG_ARM_CORE | %s,\n", reg_size, core_id_to_str(id));
|
||||
break;
|
||||
case KVM_REG_ARM_DEMUX:
|
||||
TEST_ASSERT(!(id & ~(REG_MASK | KVM_REG_ARM_DEMUX_ID_MASK | KVM_REG_ARM_DEMUX_VAL_MASK)),
|
||||
"Unexpected bits set in DEMUX reg id: 0x%llx", id);
|
||||
printf("\tKVM_REG_ARM64 | %s | KVM_REG_ARM_DEMUX | KVM_REG_ARM_DEMUX_ID_CCSIDR | %lld,\n",
|
||||
reg_size, id & KVM_REG_ARM_DEMUX_VAL_MASK);
|
||||
break;
|
||||
case KVM_REG_ARM64_SYSREG:
|
||||
op0 = (id & KVM_REG_ARM64_SYSREG_OP0_MASK) >> KVM_REG_ARM64_SYSREG_OP0_SHIFT;
|
||||
op1 = (id & KVM_REG_ARM64_SYSREG_OP1_MASK) >> KVM_REG_ARM64_SYSREG_OP1_SHIFT;
|
||||
crn = (id & KVM_REG_ARM64_SYSREG_CRN_MASK) >> KVM_REG_ARM64_SYSREG_CRN_SHIFT;
|
||||
crm = (id & KVM_REG_ARM64_SYSREG_CRM_MASK) >> KVM_REG_ARM64_SYSREG_CRM_SHIFT;
|
||||
op2 = (id & KVM_REG_ARM64_SYSREG_OP2_MASK) >> KVM_REG_ARM64_SYSREG_OP2_SHIFT;
|
||||
TEST_ASSERT(id == ARM64_SYS_REG(op0, op1, crn, crm, op2),
|
||||
"Unexpected bits set in SYSREG reg id: 0x%llx", id);
|
||||
printf("\tARM64_SYS_REG(%d, %d, %d, %d, %d),\n", op0, op1, crn, crm, op2);
|
||||
break;
|
||||
case KVM_REG_ARM_FW:
|
||||
TEST_ASSERT(id == KVM_REG_ARM_FW_REG(id & 0xffff),
|
||||
"Unexpected bits set in FW reg id: 0x%llx", id);
|
||||
printf("\tKVM_REG_ARM_FW_REG(%lld),\n", id & 0xffff);
|
||||
break;
|
||||
case KVM_REG_ARM64_SVE:
|
||||
if (reg_list_sve())
|
||||
printf("\t%s,\n", sve_id_to_str(id));
|
||||
else
|
||||
TEST_FAIL("KVM_REG_ARM64_SVE is an unexpected coproc type in reg id: 0x%llx", id);
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unexpected coproc type: 0x%llx in reg id: 0x%llx",
|
||||
(id & KVM_REG_ARM_COPROC_MASK) >> KVM_REG_ARM_COPROC_SHIFT, id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Older kernels listed each 32-bit word of CORE registers separately.
|
||||
* For 64 and 128-bit registers we need to ignore the extra words. We
|
||||
* also need to fixup the sizes, because the older kernels stated all
|
||||
* registers were 64-bit, even when they weren't.
|
||||
*/
|
||||
static void core_reg_fixup(void)
|
||||
{
|
||||
struct kvm_reg_list *tmp;
|
||||
__u64 id, core_off;
|
||||
int i;
|
||||
|
||||
tmp = calloc(1, sizeof(*tmp) + reg_list->n * sizeof(__u64));
|
||||
|
||||
for (i = 0; i < reg_list->n; ++i) {
|
||||
id = reg_list->reg[i];
|
||||
|
||||
if ((id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM_CORE) {
|
||||
tmp->reg[tmp->n++] = id;
|
||||
continue;
|
||||
}
|
||||
|
||||
core_off = id & ~REG_MASK;
|
||||
|
||||
switch (core_off) {
|
||||
case 0x52: case 0xd2: case 0xd6:
|
||||
/*
|
||||
* These offsets are pointing at padding.
|
||||
* We need to ignore them too.
|
||||
*/
|
||||
continue;
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ...
|
||||
KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]):
|
||||
if (core_off & 3)
|
||||
continue;
|
||||
id &= ~KVM_REG_SIZE_MASK;
|
||||
id |= KVM_REG_SIZE_U128;
|
||||
tmp->reg[tmp->n++] = id;
|
||||
continue;
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.fpsr):
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.fpcr):
|
||||
id &= ~KVM_REG_SIZE_MASK;
|
||||
id |= KVM_REG_SIZE_U32;
|
||||
tmp->reg[tmp->n++] = id;
|
||||
continue;
|
||||
default:
|
||||
if (core_off & 1)
|
||||
continue;
|
||||
tmp->reg[tmp->n++] = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(reg_list);
|
||||
reg_list = tmp;
|
||||
}
|
||||
|
||||
static void prepare_vcpu_init(struct kvm_vcpu_init *init)
|
||||
{
|
||||
if (reg_list_sve())
|
||||
init->features[0] |= 1 << KVM_ARM_VCPU_SVE;
|
||||
}
|
||||
|
||||
static void finalize_vcpu(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
{
|
||||
int feature;
|
||||
|
||||
if (reg_list_sve()) {
|
||||
feature = KVM_ARM_VCPU_SVE;
|
||||
vcpu_ioctl(vm, vcpuid, KVM_ARM_VCPU_FINALIZE, &feature);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_supported(void)
|
||||
{
|
||||
if (reg_list_sve() && !kvm_check_cap(KVM_CAP_ARM_SVE)) {
|
||||
fprintf(stderr, "SVE not available, skipping tests\n");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
struct kvm_vcpu_init init = { .target = -1, };
|
||||
int new_regs = 0, missing_regs = 0, i;
|
||||
int failed_get = 0, failed_set = 0, failed_reject = 0;
|
||||
bool print_list = false, fixup_core_regs = false;
|
||||
struct kvm_vm *vm;
|
||||
__u64 *vec_regs;
|
||||
|
||||
check_supported();
|
||||
|
||||
for (i = 1; i < ac; ++i) {
|
||||
if (strcmp(av[i], "--core-reg-fixup") == 0)
|
||||
fixup_core_regs = true;
|
||||
else if (strcmp(av[i], "--list") == 0)
|
||||
print_list = true;
|
||||
else
|
||||
fprintf(stderr, "Ignoring unknown option: %s\n", av[i]);
|
||||
}
|
||||
|
||||
vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR);
|
||||
prepare_vcpu_init(&init);
|
||||
aarch64_vcpu_add_default(vm, 0, &init, NULL);
|
||||
finalize_vcpu(vm, 0);
|
||||
|
||||
reg_list = vcpu_get_reg_list(vm, 0);
|
||||
|
||||
if (fixup_core_regs)
|
||||
core_reg_fixup();
|
||||
|
||||
if (print_list) {
|
||||
putchar('\n');
|
||||
for_each_reg(i)
|
||||
print_reg(reg_list->reg[i]);
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only test that we can get the register and then write back the
|
||||
* same value. Some registers may allow other values to be written
|
||||
* back, but others only allow some bits to be changed, and at least
|
||||
* for ID registers set will fail if the value does not exactly match
|
||||
* what was returned by get. If registers that allow other values to
|
||||
* be written need to have the other values tested, then we should
|
||||
* create a new set of tests for those in a new independent test
|
||||
* executable.
|
||||
*/
|
||||
for_each_reg(i) {
|
||||
uint8_t addr[2048 / 8];
|
||||
struct kvm_one_reg reg = {
|
||||
.id = reg_list->reg[i],
|
||||
.addr = (__u64)&addr,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = _vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
puts("Failed to get ");
|
||||
print_reg(reg.id);
|
||||
putchar('\n');
|
||||
++failed_get;
|
||||
}
|
||||
|
||||
/* rejects_set registers are rejected after KVM_ARM_VCPU_FINALIZE */
|
||||
if (find_reg(rejects_set, rejects_set_n, reg.id)) {
|
||||
ret = _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, ®);
|
||||
if (ret != -1 || errno != EPERM) {
|
||||
printf("Failed to reject (ret=%d, errno=%d) ", ret, errno);
|
||||
print_reg(reg.id);
|
||||
putchar('\n');
|
||||
++failed_reject;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
puts("Failed to set ");
|
||||
print_reg(reg.id);
|
||||
putchar('\n');
|
||||
++failed_set;
|
||||
}
|
||||
}
|
||||
|
||||
if (reg_list_sve()) {
|
||||
blessed_n = base_regs_n + sve_regs_n;
|
||||
vec_regs = sve_regs;
|
||||
} else {
|
||||
blessed_n = base_regs_n + vregs_n;
|
||||
vec_regs = vregs;
|
||||
}
|
||||
|
||||
blessed_reg = calloc(blessed_n, sizeof(__u64));
|
||||
for (i = 0; i < base_regs_n; ++i)
|
||||
blessed_reg[i] = base_regs[i];
|
||||
for (i = 0; i < blessed_n - base_regs_n; ++i)
|
||||
blessed_reg[base_regs_n + i] = vec_regs[i];
|
||||
|
||||
for_each_new_reg(i)
|
||||
++new_regs;
|
||||
|
||||
for_each_missing_reg(i)
|
||||
++missing_regs;
|
||||
|
||||
if (new_regs || missing_regs) {
|
||||
printf("Number blessed registers: %5lld\n", blessed_n);
|
||||
printf("Number registers: %5lld\n", reg_list->n);
|
||||
}
|
||||
|
||||
if (new_regs) {
|
||||
printf("\nThere are %d new registers.\n"
|
||||
"Consider adding them to the blessed reg "
|
||||
"list with the following lines:\n\n", new_regs);
|
||||
for_each_new_reg(i)
|
||||
print_reg(reg_list->reg[i]);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
if (missing_regs) {
|
||||
printf("\nThere are %d missing registers.\n"
|
||||
"The following lines are missing registers:\n\n", missing_regs);
|
||||
for_each_missing_reg(i)
|
||||
print_reg(blessed_reg[i]);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,
|
||||
"There are %d missing registers; "
|
||||
"%d registers failed get; %d registers failed set; %d registers failed reject",
|
||||
missing_regs, failed_get, failed_set, failed_reject);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current blessed list was primed with the output of kernel version
|
||||
* v4.15 with --core-reg-fixup and then later updated with new registers.
|
||||
*/
|
||||
static __u64 base_regs[] = {
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[0]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[1]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[2]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[3]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[4]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[5]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[6]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[7]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[8]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[9]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[10]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[11]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[12]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[13]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[14]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[15]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[16]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[17]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[18]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[19]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[20]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[21]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[22]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[23]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[24]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[25]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[26]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[27]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[28]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[29]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[30]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.sp),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.pc),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.pstate),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(sp_el1),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(elr_el1),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(spsr[0]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(spsr[1]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(spsr[2]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(spsr[3]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(spsr[4]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.fpsr),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.fpcr),
|
||||
KVM_REG_ARM_FW_REG(0),
|
||||
KVM_REG_ARM_FW_REG(1),
|
||||
KVM_REG_ARM_FW_REG(2),
|
||||
ARM64_SYS_REG(3, 3, 14, 3, 1), /* CNTV_CTL_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 14, 3, 2), /* CNTV_CVAL_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 14, 0, 2),
|
||||
ARM64_SYS_REG(3, 0, 0, 0, 0), /* MIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 0, 6), /* REVIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 1, 0, 0, 1), /* CLIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 1, 0, 0, 7), /* AIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 3, 0, 0, 1), /* CTR_EL0 */
|
||||
ARM64_SYS_REG(2, 0, 0, 0, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 0, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 0, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 0, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 1, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 1, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 1, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 1, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 2, 0), /* MDCCINT_EL1 */
|
||||
ARM64_SYS_REG(2, 0, 0, 2, 2), /* MDSCR_EL1 */
|
||||
ARM64_SYS_REG(2, 0, 0, 2, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 2, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 2, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 2, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 3, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 3, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 3, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 3, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 4, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 4, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 4, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 4, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 5, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 5, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 5, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 5, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 6, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 6, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 6, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 6, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 7, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 7, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 7, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 7, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 8, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 8, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 8, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 8, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 9, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 9, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 9, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 9, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 10, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 10, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 10, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 10, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 11, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 11, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 11, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 11, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 12, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 12, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 12, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 12, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 13, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 13, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 13, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 13, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 14, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 14, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 14, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 14, 7),
|
||||
ARM64_SYS_REG(2, 0, 0, 15, 4),
|
||||
ARM64_SYS_REG(2, 0, 0, 15, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 15, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 15, 7),
|
||||
ARM64_SYS_REG(2, 4, 0, 7, 0), /* DBGVCR32_EL2 */
|
||||
ARM64_SYS_REG(3, 0, 0, 0, 5), /* MPIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 0), /* ID_PFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 1), /* ID_PFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 2), /* ID_DFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 3), /* ID_AFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 4), /* ID_MMFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 5), /* ID_MMFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 6), /* ID_MMFR2_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 7), /* ID_MMFR3_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 0), /* ID_ISAR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 1), /* ID_ISAR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 2), /* ID_ISAR2_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 3), /* ID_ISAR3_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 4), /* ID_ISAR4_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 5), /* ID_ISAR5_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 6), /* ID_MMFR4_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 2, 7), /* ID_ISAR6_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 0), /* MVFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 1), /* MVFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 2), /* MVFR2_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 3),
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 4), /* ID_PFR2_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 5), /* ID_DFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 6), /* ID_MMFR5_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 3, 7),
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 0), /* ID_AA64PFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 1), /* ID_AA64PFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 2),
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 3),
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 4), /* ID_AA64ZFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 5),
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 6),
|
||||
ARM64_SYS_REG(3, 0, 0, 4, 7),
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 0), /* ID_AA64DFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 1), /* ID_AA64DFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 2),
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 3),
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 4), /* ID_AA64AFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 5), /* ID_AA64AFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 6),
|
||||
ARM64_SYS_REG(3, 0, 0, 5, 7),
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 0), /* ID_AA64ISAR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 1), /* ID_AA64ISAR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 2),
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 3),
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 4),
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 5),
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 6),
|
||||
ARM64_SYS_REG(3, 0, 0, 6, 7),
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 0), /* ID_AA64MMFR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 1), /* ID_AA64MMFR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 2), /* ID_AA64MMFR2_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 3),
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 4),
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 5),
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 6),
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 7),
|
||||
ARM64_SYS_REG(3, 0, 1, 0, 0), /* SCTLR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 1, 0, 1), /* ACTLR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 1, 0, 2), /* CPACR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 2, 0, 0), /* TTBR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 2, 0, 1), /* TTBR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 2, 0, 2), /* TCR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 5, 1, 0), /* AFSR0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 5, 1, 1), /* AFSR1_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 5, 2, 0), /* ESR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 6, 0, 0), /* FAR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 7, 4, 0), /* PAR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 9, 14, 1), /* PMINTENSET_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 9, 14, 2), /* PMINTENCLR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 10, 2, 0), /* MAIR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 10, 3, 0), /* AMAIR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 12, 0, 0), /* VBAR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 12, 1, 1), /* DISR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 13, 0, 1), /* CONTEXTIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 13, 0, 4), /* TPIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 14, 1, 0), /* CNTKCTL_EL1 */
|
||||
ARM64_SYS_REG(3, 2, 0, 0, 0), /* CSSELR_EL1 */
|
||||
ARM64_SYS_REG(3, 3, 9, 12, 0), /* PMCR_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 12, 1), /* PMCNTENSET_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 12, 2), /* PMCNTENCLR_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 12, 3), /* PMOVSCLR_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 12, 4), /* PMSWINC_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 12, 5), /* PMSELR_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 13, 0), /* PMCCNTR_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 14, 0), /* PMUSERENR_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 9, 14, 3), /* PMOVSSET_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 13, 0, 2), /* TPIDR_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 13, 0, 3), /* TPIDRRO_EL0 */
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 8, 7),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 9, 7),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 10, 7),
|
||||
ARM64_SYS_REG(3, 3, 14, 11, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 11, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 11, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 11, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 11, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 11, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 11, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 12, 7),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 13, 7),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 14, 7),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 0),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 1),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 2),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 3),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 4),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 5),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 6),
|
||||
ARM64_SYS_REG(3, 3, 14, 15, 7), /* PMCCFILTR_EL0 */
|
||||
ARM64_SYS_REG(3, 4, 3, 0, 0), /* DACR32_EL2 */
|
||||
ARM64_SYS_REG(3, 4, 5, 0, 1), /* IFSR32_EL2 */
|
||||
ARM64_SYS_REG(3, 4, 5, 3, 0), /* FPEXC32_EL2 */
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX | KVM_REG_ARM_DEMUX_ID_CCSIDR | 0,
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX | KVM_REG_ARM_DEMUX_ID_CCSIDR | 1,
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX | KVM_REG_ARM_DEMUX_ID_CCSIDR | 2,
|
||||
};
|
||||
static __u64 base_regs_n = ARRAY_SIZE(base_regs);
|
||||
|
||||
static __u64 vregs[] = {
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[1]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[2]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[3]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[4]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[5]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[6]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[7]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[8]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[9]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[10]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[11]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[12]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[13]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[14]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[15]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[16]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[17]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[18]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[19]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[20]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[21]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[22]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[23]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[24]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[25]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[26]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[27]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[28]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[29]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[30]),
|
||||
KVM_REG_ARM64 | KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]),
|
||||
};
|
||||
static __u64 vregs_n = ARRAY_SIZE(vregs);
|
||||
|
||||
static __u64 sve_regs[] = {
|
||||
KVM_REG_ARM64_SVE_VLS,
|
||||
KVM_REG_ARM64_SVE_ZREG(0, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(1, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(2, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(3, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(4, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(5, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(6, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(7, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(8, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(9, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(10, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(11, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(12, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(13, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(14, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(15, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(16, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(17, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(18, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(19, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(20, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(21, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(22, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(23, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(24, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(25, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(26, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(27, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(28, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(29, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(30, 0),
|
||||
KVM_REG_ARM64_SVE_ZREG(31, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(0, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(1, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(2, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(3, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(4, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(5, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(6, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(7, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(8, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(9, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(10, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(11, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(12, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(13, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(14, 0),
|
||||
KVM_REG_ARM64_SVE_PREG(15, 0),
|
||||
KVM_REG_ARM64_SVE_FFR(0),
|
||||
ARM64_SYS_REG(3, 0, 1, 2, 0), /* ZCR_EL1 */
|
||||
};
|
||||
static __u64 sve_regs_n = ARRAY_SIZE(sve_regs);
|
||||
|
||||
static __u64 rejects_set[] = {
|
||||
#ifdef REG_LIST_SVE
|
||||
KVM_REG_ARM64_SVE_VLS,
|
||||
#endif
|
||||
};
|
||||
static __u64 rejects_set_n = ARRAY_SIZE(rejects_set);
|
||||
@@ -1,6 +0,0 @@
|
||||
#define USE_CLEAR_DIRTY_LOG
|
||||
#define KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE (1 << 0)
|
||||
#define KVM_DIRTY_LOG_INITIALLY_SET (1 << 1)
|
||||
#define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
|
||||
KVM_DIRTY_LOG_INITIALLY_SET)
|
||||
#include "dirty_log_test.c"
|
||||
@@ -21,20 +21,12 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/userfaultfd.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "perf_test_util.h"
|
||||
#include "processor.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#ifdef __NR_userfaultfd
|
||||
|
||||
/* The memory slot index demand page */
|
||||
#define TEST_MEM_SLOT_INDEX 1
|
||||
|
||||
/* Default guest test virtual memory offset */
|
||||
#define DEFAULT_GUEST_TEST_MEM 0xc0000000
|
||||
|
||||
#define DEFAULT_GUEST_TEST_MEM_SIZE (1 << 30) /* 1G */
|
||||
|
||||
#ifdef PRINT_PER_PAGE_UPDATES
|
||||
#define PER_PAGE_DEBUG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
@@ -47,77 +39,17 @@
|
||||
#define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define MAX_VCPUS 512
|
||||
|
||||
/*
|
||||
* Guest/Host shared variables. Ensure addr_gva2hva() and/or
|
||||
* sync_global_to/from_guest() are used when accessing from
|
||||
* the host. READ/WRITE_ONCE() should also be used with anything
|
||||
* that may change.
|
||||
*/
|
||||
static uint64_t host_page_size;
|
||||
static uint64_t guest_page_size;
|
||||
|
||||
static char *guest_data_prototype;
|
||||
|
||||
/*
|
||||
* Guest physical memory offset of the testing memory slot.
|
||||
* This will be set to the topmost valid physical address minus
|
||||
* the test memory size.
|
||||
*/
|
||||
static uint64_t guest_test_phys_mem;
|
||||
|
||||
/*
|
||||
* Guest virtual memory offset of the testing memory slot.
|
||||
* Must not conflict with identity mapped test code.
|
||||
*/
|
||||
static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
|
||||
|
||||
struct vcpu_args {
|
||||
uint64_t gva;
|
||||
uint64_t pages;
|
||||
|
||||
/* Only used by the host userspace part of the vCPU thread */
|
||||
int vcpu_id;
|
||||
struct kvm_vm *vm;
|
||||
};
|
||||
|
||||
static struct vcpu_args vcpu_args[MAX_VCPUS];
|
||||
|
||||
/*
|
||||
* Continuously write to the first 8 bytes of each page in the demand paging
|
||||
* memory region.
|
||||
*/
|
||||
static void guest_code(uint32_t vcpu_id)
|
||||
{
|
||||
uint64_t gva;
|
||||
uint64_t pages;
|
||||
int i;
|
||||
|
||||
/* Make sure vCPU args data structure is not corrupt. */
|
||||
GUEST_ASSERT(vcpu_args[vcpu_id].vcpu_id == vcpu_id);
|
||||
|
||||
gva = vcpu_args[vcpu_id].gva;
|
||||
pages = vcpu_args[vcpu_id].pages;
|
||||
|
||||
for (i = 0; i < pages; i++) {
|
||||
uint64_t addr = gva + (i * guest_page_size);
|
||||
|
||||
addr &= ~(host_page_size - 1);
|
||||
*(uint64_t *)addr = 0x0123456789ABCDEF;
|
||||
}
|
||||
|
||||
GUEST_SYNC(1);
|
||||
}
|
||||
|
||||
static void *vcpu_worker(void *data)
|
||||
{
|
||||
int ret;
|
||||
struct vcpu_args *args = (struct vcpu_args *)data;
|
||||
struct kvm_vm *vm = args->vm;
|
||||
int vcpu_id = args->vcpu_id;
|
||||
struct vcpu_args *vcpu_args = (struct vcpu_args *)data;
|
||||
int vcpu_id = vcpu_args->vcpu_id;
|
||||
struct kvm_vm *vm = perf_test_args.vm;
|
||||
struct kvm_run *run;
|
||||
struct timespec start, end, ts_diff;
|
||||
struct timespec start;
|
||||
struct timespec ts_diff;
|
||||
|
||||
vcpu_args_set(vm, vcpu_id, 1, vcpu_id);
|
||||
run = vcpu_state(vm, vcpu_id);
|
||||
@@ -133,52 +65,18 @@ static void *vcpu_worker(void *data)
|
||||
exit_reason_str(run->exit_reason));
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
ts_diff = timespec_sub(end, start);
|
||||
ts_diff = timespec_diff_now(start);
|
||||
PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_id,
|
||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define PAGE_SHIFT_4K 12
|
||||
#define PTES_PER_4K_PT 512
|
||||
|
||||
static struct kvm_vm *create_vm(enum vm_guest_mode mode, int vcpus,
|
||||
uint64_t vcpu_memory_bytes)
|
||||
{
|
||||
struct kvm_vm *vm;
|
||||
uint64_t pages = DEFAULT_GUEST_PHY_PAGES;
|
||||
|
||||
/* Account for a few pages per-vCPU for stacks */
|
||||
pages += DEFAULT_STACK_PGS * vcpus;
|
||||
|
||||
/*
|
||||
* Reserve twice the ammount of memory needed to map the test region and
|
||||
* the page table / stacks region, at 4k, for page tables. Do the
|
||||
* calculation with 4K page size: the smallest of all archs. (e.g., 64K
|
||||
* page size guest will need even less memory for page tables).
|
||||
*/
|
||||
pages += (2 * pages) / PTES_PER_4K_PT;
|
||||
pages += ((2 * vcpus * vcpu_memory_bytes) >> PAGE_SHIFT_4K) /
|
||||
PTES_PER_4K_PT;
|
||||
pages = vm_adjust_num_guest_pages(mode, pages);
|
||||
|
||||
pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
|
||||
|
||||
vm = _vm_create(mode, pages, O_RDWR);
|
||||
kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
|
||||
#ifdef __x86_64__
|
||||
vm_create_irqchip(vm);
|
||||
#endif
|
||||
return vm;
|
||||
}
|
||||
|
||||
static int handle_uffd_page_request(int uffd, uint64_t addr)
|
||||
{
|
||||
pid_t tid;
|
||||
struct timespec start;
|
||||
struct timespec end;
|
||||
struct timespec ts_diff;
|
||||
struct uffdio_copy copy;
|
||||
int r;
|
||||
|
||||
@@ -186,7 +84,7 @@ static int handle_uffd_page_request(int uffd, uint64_t addr)
|
||||
|
||||
copy.src = (uint64_t)guest_data_prototype;
|
||||
copy.dst = addr;
|
||||
copy.len = host_page_size;
|
||||
copy.len = perf_test_args.host_page_size;
|
||||
copy.mode = 0;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
@@ -198,12 +96,12 @@ static int handle_uffd_page_request(int uffd, uint64_t addr)
|
||||
return r;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
ts_diff = timespec_diff_now(start);
|
||||
|
||||
PER_PAGE_DEBUG("UFFDIO_COPY %d \t%ld ns\n", tid,
|
||||
timespec_to_ns(timespec_sub(end, start)));
|
||||
timespec_to_ns(ts_diff));
|
||||
PER_PAGE_DEBUG("Paged in %ld bytes at 0x%lx from thread %d\n",
|
||||
host_page_size, addr, tid);
|
||||
perf_test_args.host_page_size, addr, tid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -223,7 +121,8 @@ static void *uffd_handler_thread_fn(void *arg)
|
||||
int pipefd = uffd_args->pipefd;
|
||||
useconds_t delay = uffd_args->delay;
|
||||
int64_t pages = 0;
|
||||
struct timespec start, end, ts_diff;
|
||||
struct timespec start;
|
||||
struct timespec ts_diff;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
while (!quit_uffd_thread) {
|
||||
@@ -292,8 +191,7 @@ static void *uffd_handler_thread_fn(void *arg)
|
||||
pages++;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
ts_diff = timespec_sub(end, start);
|
||||
ts_diff = timespec_diff_now(start);
|
||||
PER_VCPU_DEBUG("userfaulted %ld pages over %ld.%.9lds. (%f/sec)\n",
|
||||
pages, ts_diff.tv_sec, ts_diff.tv_nsec,
|
||||
pages / ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
|
||||
@@ -351,99 +249,54 @@ static int setup_demand_paging(struct kvm_vm *vm,
|
||||
}
|
||||
|
||||
static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
||||
useconds_t uffd_delay, int vcpus,
|
||||
uint64_t vcpu_memory_bytes)
|
||||
useconds_t uffd_delay)
|
||||
{
|
||||
pthread_t *vcpu_threads;
|
||||
pthread_t *uffd_handler_threads = NULL;
|
||||
struct uffd_handler_args *uffd_args = NULL;
|
||||
struct timespec start, end, ts_diff;
|
||||
struct timespec start;
|
||||
struct timespec ts_diff;
|
||||
int *pipefds = NULL;
|
||||
struct kvm_vm *vm;
|
||||
uint64_t guest_num_pages;
|
||||
int vcpu_id;
|
||||
int r;
|
||||
|
||||
vm = create_vm(mode, vcpus, vcpu_memory_bytes);
|
||||
vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size);
|
||||
|
||||
guest_page_size = vm_get_page_size(vm);
|
||||
perf_test_args.wr_fract = 1;
|
||||
|
||||
TEST_ASSERT(vcpu_memory_bytes % guest_page_size == 0,
|
||||
"Guest memory size is not guest page size aligned.");
|
||||
|
||||
guest_num_pages = (vcpus * vcpu_memory_bytes) / guest_page_size;
|
||||
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
|
||||
|
||||
/*
|
||||
* If there should be more memory in the guest test region than there
|
||||
* can be pages in the guest, it will definitely cause problems.
|
||||
*/
|
||||
TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm),
|
||||
"Requested more guest memory than address space allows.\n"
|
||||
" guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n",
|
||||
guest_num_pages, vm_get_max_gfn(vm), vcpus,
|
||||
vcpu_memory_bytes);
|
||||
|
||||
host_page_size = getpagesize();
|
||||
TEST_ASSERT(vcpu_memory_bytes % host_page_size == 0,
|
||||
"Guest memory size is not host page size aligned.");
|
||||
|
||||
guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) *
|
||||
guest_page_size;
|
||||
guest_test_phys_mem &= ~(host_page_size - 1);
|
||||
|
||||
#ifdef __s390x__
|
||||
/* Align to 1M (segment size) */
|
||||
guest_test_phys_mem &= ~((1 << 20) - 1);
|
||||
#endif
|
||||
|
||||
pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem);
|
||||
|
||||
/* Add an extra memory slot for testing demand paging */
|
||||
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
|
||||
guest_test_phys_mem,
|
||||
TEST_MEM_SLOT_INDEX,
|
||||
guest_num_pages, 0);
|
||||
|
||||
/* Do mapping for the demand paging memory slot */
|
||||
virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0);
|
||||
|
||||
ucall_init(vm, NULL);
|
||||
|
||||
guest_data_prototype = malloc(host_page_size);
|
||||
guest_data_prototype = malloc(perf_test_args.host_page_size);
|
||||
TEST_ASSERT(guest_data_prototype,
|
||||
"Failed to allocate buffer for guest data pattern");
|
||||
memset(guest_data_prototype, 0xAB, host_page_size);
|
||||
memset(guest_data_prototype, 0xAB, perf_test_args.host_page_size);
|
||||
|
||||
vcpu_threads = malloc(vcpus * sizeof(*vcpu_threads));
|
||||
vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
|
||||
TEST_ASSERT(vcpu_threads, "Memory allocation failed");
|
||||
|
||||
add_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
|
||||
|
||||
if (use_uffd) {
|
||||
uffd_handler_threads =
|
||||
malloc(vcpus * sizeof(*uffd_handler_threads));
|
||||
malloc(nr_vcpus * sizeof(*uffd_handler_threads));
|
||||
TEST_ASSERT(uffd_handler_threads, "Memory allocation failed");
|
||||
|
||||
uffd_args = malloc(vcpus * sizeof(*uffd_args));
|
||||
uffd_args = malloc(nr_vcpus * sizeof(*uffd_args));
|
||||
TEST_ASSERT(uffd_args, "Memory allocation failed");
|
||||
|
||||
pipefds = malloc(sizeof(int) * vcpus * 2);
|
||||
pipefds = malloc(sizeof(int) * nr_vcpus * 2);
|
||||
TEST_ASSERT(pipefds, "Unable to allocate memory for pipefd");
|
||||
}
|
||||
|
||||
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
|
||||
vm_paddr_t vcpu_gpa;
|
||||
void *vcpu_hva;
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
|
||||
vm_paddr_t vcpu_gpa;
|
||||
void *vcpu_hva;
|
||||
|
||||
vm_vcpu_add_default(vm, vcpu_id, guest_code);
|
||||
vcpu_gpa = guest_test_phys_mem + (vcpu_id * guest_percpu_mem_size);
|
||||
PER_VCPU_DEBUG("Added VCPU %d with test mem gpa [%lx, %lx)\n",
|
||||
vcpu_id, vcpu_gpa, vcpu_gpa + guest_percpu_mem_size);
|
||||
|
||||
vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes);
|
||||
PER_VCPU_DEBUG("Added VCPU %d with test mem gpa [%lx, %lx)\n",
|
||||
vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes);
|
||||
/* Cache the HVA pointer of the region */
|
||||
vcpu_hva = addr_gpa2hva(vm, vcpu_gpa);
|
||||
|
||||
/* Cache the HVA pointer of the region */
|
||||
vcpu_hva = addr_gpa2hva(vm, vcpu_gpa);
|
||||
|
||||
if (use_uffd) {
|
||||
/*
|
||||
* Set up user fault fd to handle demand paging
|
||||
* requests.
|
||||
@@ -456,53 +309,41 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
||||
&uffd_handler_threads[vcpu_id],
|
||||
pipefds[vcpu_id * 2],
|
||||
uffd_delay, &uffd_args[vcpu_id],
|
||||
vcpu_hva, vcpu_memory_bytes);
|
||||
vcpu_hva, guest_percpu_mem_size);
|
||||
if (r < 0)
|
||||
exit(-r);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
vcpu_set_cpuid(vm, vcpu_id, kvm_get_supported_cpuid());
|
||||
#endif
|
||||
|
||||
vcpu_args[vcpu_id].vm = vm;
|
||||
vcpu_args[vcpu_id].vcpu_id = vcpu_id;
|
||||
vcpu_args[vcpu_id].gva = guest_test_virt_mem +
|
||||
(vcpu_id * vcpu_memory_bytes);
|
||||
vcpu_args[vcpu_id].pages = vcpu_memory_bytes / guest_page_size;
|
||||
}
|
||||
|
||||
/* Export the shared variables to the guest */
|
||||
sync_global_to_guest(vm, host_page_size);
|
||||
sync_global_to_guest(vm, guest_page_size);
|
||||
sync_global_to_guest(vm, vcpu_args);
|
||||
sync_global_to_guest(vm, perf_test_args);
|
||||
|
||||
pr_info("Finished creating vCPUs and starting uffd threads\n");
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
|
||||
pthread_create(&vcpu_threads[vcpu_id], NULL, vcpu_worker,
|
||||
&vcpu_args[vcpu_id]);
|
||||
&perf_test_args.vcpu_args[vcpu_id]);
|
||||
}
|
||||
|
||||
pr_info("Started all vCPUs\n");
|
||||
|
||||
/* Wait for the vcpu threads to quit */
|
||||
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
|
||||
pthread_join(vcpu_threads[vcpu_id], NULL);
|
||||
PER_VCPU_DEBUG("Joined thread for vCPU %d\n", vcpu_id);
|
||||
}
|
||||
|
||||
pr_info("All vCPU threads joined\n");
|
||||
ts_diff = timespec_diff_now(start);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
pr_info("All vCPU threads joined\n");
|
||||
|
||||
if (use_uffd) {
|
||||
char c;
|
||||
|
||||
/* Tell the user fault fd handler threads to quit */
|
||||
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
|
||||
r = write(pipefds[vcpu_id * 2 + 1], &c, 1);
|
||||
TEST_ASSERT(r == 1, "Unable to write to pipefd");
|
||||
|
||||
@@ -510,11 +351,11 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
||||
}
|
||||
}
|
||||
|
||||
ts_diff = timespec_sub(end, start);
|
||||
pr_info("Total guest execution time: %ld.%.9lds\n",
|
||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
pr_info("Overall demand paging rate: %f pgs/sec\n",
|
||||
guest_num_pages / ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
|
||||
perf_test_args.vcpu_args[0].pages * nr_vcpus /
|
||||
((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
|
||||
|
||||
ucall_uninit(vm);
|
||||
kvm_vm_free(vm);
|
||||
@@ -568,9 +409,8 @@ static void help(char *name)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
|
||||
bool mode_selected = false;
|
||||
uint64_t vcpu_memory_bytes = DEFAULT_GUEST_TEST_MEM_SIZE;
|
||||
int vcpus = 1;
|
||||
unsigned int mode;
|
||||
int opt, i;
|
||||
bool use_uffd = false;
|
||||
@@ -619,15 +459,12 @@ int main(int argc, char *argv[])
|
||||
"A negative UFFD delay is not supported.");
|
||||
break;
|
||||
case 'b':
|
||||
vcpu_memory_bytes = parse_size(optarg);
|
||||
guest_percpu_mem_size = parse_size(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
vcpus = atoi(optarg);
|
||||
TEST_ASSERT(vcpus > 0,
|
||||
"Must have a positive number of vCPUs");
|
||||
TEST_ASSERT(vcpus <= MAX_VCPUS,
|
||||
"This test does not currently support\n"
|
||||
"more than %d vCPUs.", MAX_VCPUS);
|
||||
nr_vcpus = atoi(optarg);
|
||||
TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
|
||||
"Invalid number of vcpus, must be between 1 and %d", max_vcpus);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
@@ -642,7 +479,7 @@ int main(int argc, char *argv[])
|
||||
TEST_ASSERT(guest_modes[i].supported,
|
||||
"Guest mode ID %d (%s) not supported.",
|
||||
i, vm_guest_mode_string(i));
|
||||
run_test(i, use_uffd, uffd_delay, vcpus, vcpu_memory_bytes);
|
||||
run_test(i, use_uffd, uffd_delay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* KVM dirty page logging performance test
|
||||
*
|
||||
* Based on dirty_log_test.c
|
||||
*
|
||||
* Copyright (C) 2018, Red Hat, Inc.
|
||||
* Copyright (C) 2020, Google, Inc.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* for program_invocation_name */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "perf_test_util.h"
|
||||
#include "processor.h"
|
||||
#include "test_util.h"
|
||||
|
||||
/* How many host loops to run by default (one KVM_GET_DIRTY_LOG for each loop)*/
|
||||
#define TEST_HOST_LOOP_N 2UL
|
||||
|
||||
/* Host variables */
|
||||
static bool host_quit;
|
||||
static uint64_t iteration;
|
||||
static uint64_t vcpu_last_completed_iteration[MAX_VCPUS];
|
||||
|
||||
static void *vcpu_worker(void *data)
|
||||
{
|
||||
int ret;
|
||||
struct kvm_vm *vm = perf_test_args.vm;
|
||||
uint64_t pages_count = 0;
|
||||
struct kvm_run *run;
|
||||
struct timespec start;
|
||||
struct timespec ts_diff;
|
||||
struct timespec total = (struct timespec){0};
|
||||
struct timespec avg;
|
||||
struct vcpu_args *vcpu_args = (struct vcpu_args *)data;
|
||||
int vcpu_id = vcpu_args->vcpu_id;
|
||||
|
||||
vcpu_args_set(vm, vcpu_id, 1, vcpu_id);
|
||||
run = vcpu_state(vm, vcpu_id);
|
||||
|
||||
while (!READ_ONCE(host_quit)) {
|
||||
uint64_t current_iteration = READ_ONCE(iteration);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
ret = _vcpu_run(vm, vcpu_id);
|
||||
ts_diff = timespec_diff_now(start);
|
||||
|
||||
TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret);
|
||||
TEST_ASSERT(get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC,
|
||||
"Invalid guest sync status: exit_reason=%s\n",
|
||||
exit_reason_str(run->exit_reason));
|
||||
|
||||
pr_debug("Got sync event from vCPU %d\n", vcpu_id);
|
||||
vcpu_last_completed_iteration[vcpu_id] = current_iteration;
|
||||
pr_debug("vCPU %d updated last completed iteration to %lu\n",
|
||||
vcpu_id, vcpu_last_completed_iteration[vcpu_id]);
|
||||
|
||||
if (current_iteration) {
|
||||
pages_count += vcpu_args->pages;
|
||||
total = timespec_add(total, ts_diff);
|
||||
pr_debug("vCPU %d iteration %lu dirty memory time: %ld.%.9lds\n",
|
||||
vcpu_id, current_iteration, ts_diff.tv_sec,
|
||||
ts_diff.tv_nsec);
|
||||
} else {
|
||||
pr_debug("vCPU %d iteration %lu populate memory time: %ld.%.9lds\n",
|
||||
vcpu_id, current_iteration, ts_diff.tv_sec,
|
||||
ts_diff.tv_nsec);
|
||||
}
|
||||
|
||||
while (current_iteration == READ_ONCE(iteration) &&
|
||||
!READ_ONCE(host_quit)) {}
|
||||
}
|
||||
|
||||
avg = timespec_div(total, vcpu_last_completed_iteration[vcpu_id]);
|
||||
pr_debug("\nvCPU %d dirtied 0x%lx pages over %lu iterations in %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
|
||||
vcpu_id, pages_count, vcpu_last_completed_iteration[vcpu_id],
|
||||
total.tv_sec, total.tv_nsec, avg.tv_sec, avg.tv_nsec);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
static u64 dirty_log_manual_caps;
|
||||
#endif
|
||||
|
||||
static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
||||
uint64_t phys_offset, int wr_fract)
|
||||
{
|
||||
pthread_t *vcpu_threads;
|
||||
struct kvm_vm *vm;
|
||||
unsigned long *bmap;
|
||||
uint64_t guest_num_pages;
|
||||
uint64_t host_num_pages;
|
||||
int vcpu_id;
|
||||
struct timespec start;
|
||||
struct timespec ts_diff;
|
||||
struct timespec get_dirty_log_total = (struct timespec){0};
|
||||
struct timespec vcpu_dirty_total = (struct timespec){0};
|
||||
struct timespec avg;
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
struct kvm_enable_cap cap = {};
|
||||
struct timespec clear_dirty_log_total = (struct timespec){0};
|
||||
#endif
|
||||
|
||||
vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size);
|
||||
|
||||
perf_test_args.wr_fract = wr_fract;
|
||||
|
||||
guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm_get_page_shift(vm);
|
||||
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
|
||||
host_num_pages = vm_num_host_pages(mode, guest_num_pages);
|
||||
bmap = bitmap_alloc(host_num_pages);
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2;
|
||||
cap.args[0] = dirty_log_manual_caps;
|
||||
vm_enable_cap(vm, &cap);
|
||||
#endif
|
||||
|
||||
vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
|
||||
TEST_ASSERT(vcpu_threads, "Memory allocation failed");
|
||||
|
||||
add_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
|
||||
|
||||
sync_global_to_guest(vm, perf_test_args);
|
||||
|
||||
/* Start the iterations */
|
||||
iteration = 0;
|
||||
host_quit = false;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
|
||||
pthread_create(&vcpu_threads[vcpu_id], NULL, vcpu_worker,
|
||||
&perf_test_args.vcpu_args[vcpu_id]);
|
||||
}
|
||||
|
||||
/* Allow the vCPU to populate memory */
|
||||
pr_debug("Starting iteration %lu - Populating\n", iteration);
|
||||
while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != iteration)
|
||||
pr_debug("Waiting for vcpu_last_completed_iteration == %lu\n",
|
||||
iteration);
|
||||
|
||||
ts_diff = timespec_diff_now(start);
|
||||
pr_info("Populate memory time: %ld.%.9lds\n",
|
||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
|
||||
/* Enable dirty logging */
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX,
|
||||
KVM_MEM_LOG_DIRTY_PAGES);
|
||||
ts_diff = timespec_diff_now(start);
|
||||
pr_info("Enabling dirty logging time: %ld.%.9lds\n\n",
|
||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
|
||||
while (iteration < iterations) {
|
||||
/*
|
||||
* Incrementing the iteration number will start the vCPUs
|
||||
* dirtying memory again.
|
||||
*/
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
iteration++;
|
||||
|
||||
pr_debug("Starting iteration %lu\n", iteration);
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
|
||||
while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != iteration)
|
||||
pr_debug("Waiting for vCPU %d vcpu_last_completed_iteration == %lu\n",
|
||||
vcpu_id, iteration);
|
||||
}
|
||||
|
||||
ts_diff = timespec_diff_now(start);
|
||||
vcpu_dirty_total = timespec_add(vcpu_dirty_total, ts_diff);
|
||||
pr_info("Iteration %lu dirty memory time: %ld.%.9lds\n",
|
||||
iteration, ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap);
|
||||
|
||||
ts_diff = timespec_diff_now(start);
|
||||
get_dirty_log_total = timespec_add(get_dirty_log_total,
|
||||
ts_diff);
|
||||
pr_info("Iteration %lu get dirty log time: %ld.%.9lds\n",
|
||||
iteration, ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0,
|
||||
host_num_pages);
|
||||
|
||||
ts_diff = timespec_diff_now(start);
|
||||
clear_dirty_log_total = timespec_add(clear_dirty_log_total,
|
||||
ts_diff);
|
||||
pr_info("Iteration %lu clear dirty log time: %ld.%.9lds\n",
|
||||
iteration, ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Tell the vcpu thread to quit */
|
||||
host_quit = true;
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++)
|
||||
pthread_join(vcpu_threads[vcpu_id], NULL);
|
||||
|
||||
/* Disable dirty logging */
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX, 0);
|
||||
ts_diff = timespec_diff_now(start);
|
||||
pr_info("Disabling dirty logging time: %ld.%.9lds\n",
|
||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
|
||||
avg = timespec_div(get_dirty_log_total, iterations);
|
||||
pr_info("Get dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
|
||||
iterations, get_dirty_log_total.tv_sec,
|
||||
get_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
avg = timespec_div(clear_dirty_log_total, iterations);
|
||||
pr_info("Clear dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
|
||||
iterations, clear_dirty_log_total.tv_sec,
|
||||
clear_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
|
||||
#endif
|
||||
|
||||
free(bmap);
|
||||
free(vcpu_threads);
|
||||
ucall_uninit(vm);
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
struct guest_mode {
|
||||
bool supported;
|
||||
bool enabled;
|
||||
};
|
||||
static struct guest_mode guest_modes[NUM_VM_MODES];
|
||||
|
||||
#define guest_mode_init(mode, supported, enabled) ({ \
|
||||
guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
|
||||
})
|
||||
|
||||
static void help(char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
puts("");
|
||||
printf("usage: %s [-h] [-i iterations] [-p offset] "
|
||||
"[-m mode] [-b vcpu bytes] [-v vcpus]\n", name);
|
||||
puts("");
|
||||
printf(" -i: specify iteration counts (default: %"PRIu64")\n",
|
||||
TEST_HOST_LOOP_N);
|
||||
printf(" -p: specify guest physical test memory offset\n"
|
||||
" Warning: a low offset can conflict with the loaded test code.\n");
|
||||
printf(" -m: specify the guest mode ID to test "
|
||||
"(default: test all supported modes)\n"
|
||||
" This option may be used multiple times.\n"
|
||||
" Guest mode IDs:\n");
|
||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
||||
printf(" %d: %s%s\n", i, vm_guest_mode_string(i),
|
||||
guest_modes[i].supported ? " (supported)" : "");
|
||||
}
|
||||
printf(" -b: specify the size of the memory region which should be\n"
|
||||
" dirtied by each vCPU. e.g. 10M or 3G.\n"
|
||||
" (default: 1G)\n");
|
||||
printf(" -f: specify the fraction of pages which should be written to\n"
|
||||
" as opposed to simply read, in the form\n"
|
||||
" 1/<fraction of pages to write>.\n"
|
||||
" (default: 1 i.e. all pages are written to.)\n");
|
||||
printf(" -v: specify the number of vCPUs to run.\n");
|
||||
puts("");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned long iterations = TEST_HOST_LOOP_N;
|
||||
bool mode_selected = false;
|
||||
uint64_t phys_offset = 0;
|
||||
unsigned int mode;
|
||||
int opt, i;
|
||||
int wr_fract = 1;
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
dirty_log_manual_caps =
|
||||
kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
if (!dirty_log_manual_caps) {
|
||||
print_skip("KVM_CLEAR_DIRTY_LOG not available");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
||||
KVM_DIRTY_LOG_INITIALLY_SET);
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
guest_mode_init(VM_MODE_PXXV48_4K, true, true);
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
||||
guest_mode_init(VM_MODE_P40V48_64K, true, true);
|
||||
|
||||
{
|
||||
unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
|
||||
|
||||
if (limit >= 52)
|
||||
guest_mode_init(VM_MODE_P52V48_64K, true, true);
|
||||
if (limit >= 48) {
|
||||
guest_mode_init(VM_MODE_P48V48_4K, true, true);
|
||||
guest_mode_init(VM_MODE_P48V48_64K, true, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef __s390x__
|
||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
||||
#endif
|
||||
|
||||
while ((opt = getopt(argc, argv, "hi:p:m:b:f:v:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
iterations = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'p':
|
||||
phys_offset = strtoull(optarg, NULL, 0);
|
||||
break;
|
||||
case 'm':
|
||||
if (!mode_selected) {
|
||||
for (i = 0; i < NUM_VM_MODES; ++i)
|
||||
guest_modes[i].enabled = false;
|
||||
mode_selected = true;
|
||||
}
|
||||
mode = strtoul(optarg, NULL, 10);
|
||||
TEST_ASSERT(mode < NUM_VM_MODES,
|
||||
"Guest mode ID %d too big", mode);
|
||||
guest_modes[mode].enabled = true;
|
||||
break;
|
||||
case 'b':
|
||||
guest_percpu_mem_size = parse_size(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
wr_fract = atoi(optarg);
|
||||
TEST_ASSERT(wr_fract >= 1,
|
||||
"Write fraction cannot be less than one");
|
||||
break;
|
||||
case 'v':
|
||||
nr_vcpus = atoi(optarg);
|
||||
TEST_ASSERT(nr_vcpus > 0,
|
||||
"Must have a positive number of vCPUs");
|
||||
TEST_ASSERT(nr_vcpus <= MAX_VCPUS,
|
||||
"This test does not currently support\n"
|
||||
"more than %d vCPUs.", MAX_VCPUS);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
help(argv[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT(iterations >= 2, "The test should have at least two iterations");
|
||||
|
||||
pr_info("Test iterations: %"PRIu64"\n", iterations);
|
||||
|
||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
||||
if (!guest_modes[i].enabled)
|
||||
continue;
|
||||
TEST_ASSERT(guest_modes[i].supported,
|
||||
"Guest mode ID %d (%s) not supported.",
|
||||
i, vm_guest_mode_string(i));
|
||||
run_test(i, iterations, phys_offset, wr_fract);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -128,6 +128,78 @@ static uint64_t host_dirty_count;
|
||||
static uint64_t host_clear_count;
|
||||
static uint64_t host_track_next_count;
|
||||
|
||||
enum log_mode_t {
|
||||
/* Only use KVM_GET_DIRTY_LOG for logging */
|
||||
LOG_MODE_DIRTY_LOG = 0,
|
||||
|
||||
/* Use both KVM_[GET|CLEAR]_DIRTY_LOG for logging */
|
||||
LOG_MODE_CLEAR_LOG = 1,
|
||||
|
||||
LOG_MODE_NUM,
|
||||
|
||||
/* Run all supported modes */
|
||||
LOG_MODE_ALL = LOG_MODE_NUM,
|
||||
};
|
||||
|
||||
/* Mode of logging to test. Default is to run all supported modes */
|
||||
static enum log_mode_t host_log_mode_option = LOG_MODE_ALL;
|
||||
/* Logging mode for current run */
|
||||
static enum log_mode_t host_log_mode;
|
||||
|
||||
static bool clear_log_supported(void)
|
||||
{
|
||||
return kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
}
|
||||
|
||||
static void clear_log_create_vm_done(struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_enable_cap cap = {};
|
||||
u64 manual_caps;
|
||||
|
||||
manual_caps = kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
TEST_ASSERT(manual_caps, "MANUAL_CAPS is zero!");
|
||||
manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
||||
KVM_DIRTY_LOG_INITIALLY_SET);
|
||||
cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2;
|
||||
cap.args[0] = manual_caps;
|
||||
vm_enable_cap(vm, &cap);
|
||||
}
|
||||
|
||||
static void dirty_log_collect_dirty_pages(struct kvm_vm *vm, int slot,
|
||||
void *bitmap, uint32_t num_pages)
|
||||
{
|
||||
kvm_vm_get_dirty_log(vm, slot, bitmap);
|
||||
}
|
||||
|
||||
static void clear_log_collect_dirty_pages(struct kvm_vm *vm, int slot,
|
||||
void *bitmap, uint32_t num_pages)
|
||||
{
|
||||
kvm_vm_get_dirty_log(vm, slot, bitmap);
|
||||
kvm_vm_clear_dirty_log(vm, slot, bitmap, 0, num_pages);
|
||||
}
|
||||
|
||||
struct log_mode {
|
||||
const char *name;
|
||||
/* Return true if this mode is supported, otherwise false */
|
||||
bool (*supported)(void);
|
||||
/* Hook when the vm creation is done (before vcpu creation) */
|
||||
void (*create_vm_done)(struct kvm_vm *vm);
|
||||
/* Hook to collect the dirty pages into the bitmap provided */
|
||||
void (*collect_dirty_pages) (struct kvm_vm *vm, int slot,
|
||||
void *bitmap, uint32_t num_pages);
|
||||
} log_modes[LOG_MODE_NUM] = {
|
||||
{
|
||||
.name = "dirty-log",
|
||||
.collect_dirty_pages = dirty_log_collect_dirty_pages,
|
||||
},
|
||||
{
|
||||
.name = "clear-log",
|
||||
.supported = clear_log_supported,
|
||||
.create_vm_done = clear_log_create_vm_done,
|
||||
.collect_dirty_pages = clear_log_collect_dirty_pages,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* We use this bitmap to track some pages that should have its dirty
|
||||
* bit set in the _next_ iteration. For example, if we detected the
|
||||
@@ -137,6 +209,44 @@ static uint64_t host_track_next_count;
|
||||
*/
|
||||
static unsigned long *host_bmap_track;
|
||||
|
||||
static void log_modes_dump(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("all");
|
||||
for (i = 0; i < LOG_MODE_NUM; i++)
|
||||
printf(", %s", log_modes[i].name);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static bool log_mode_supported(void)
|
||||
{
|
||||
struct log_mode *mode = &log_modes[host_log_mode];
|
||||
|
||||
if (mode->supported)
|
||||
return mode->supported();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void log_mode_create_vm_done(struct kvm_vm *vm)
|
||||
{
|
||||
struct log_mode *mode = &log_modes[host_log_mode];
|
||||
|
||||
if (mode->create_vm_done)
|
||||
mode->create_vm_done(vm);
|
||||
}
|
||||
|
||||
static void log_mode_collect_dirty_pages(struct kvm_vm *vm, int slot,
|
||||
void *bitmap, uint32_t num_pages)
|
||||
{
|
||||
struct log_mode *mode = &log_modes[host_log_mode];
|
||||
|
||||
TEST_ASSERT(mode->collect_dirty_pages != NULL,
|
||||
"collect_dirty_pages() is required for any log mode!");
|
||||
mode->collect_dirty_pages(vm, slot, bitmap, num_pages);
|
||||
}
|
||||
|
||||
static void generate_random_array(uint64_t *guest_array, uint64_t size)
|
||||
{
|
||||
uint64_t i;
|
||||
@@ -195,7 +305,7 @@ static void vm_dirty_log_verify(enum vm_guest_mode mode, unsigned long *bmap)
|
||||
page);
|
||||
}
|
||||
|
||||
if (test_bit_le(page, bmap)) {
|
||||
if (test_and_clear_bit_le(page, bmap)) {
|
||||
host_dirty_count++;
|
||||
/*
|
||||
* If the bit is set, the value written onto
|
||||
@@ -252,11 +362,12 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid,
|
||||
|
||||
pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
|
||||
|
||||
vm = _vm_create(mode, DEFAULT_GUEST_PHY_PAGES + extra_pg_pages, O_RDWR);
|
||||
vm = vm_create(mode, DEFAULT_GUEST_PHY_PAGES + extra_pg_pages, O_RDWR);
|
||||
kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
|
||||
#ifdef __x86_64__
|
||||
vm_create_irqchip(vm);
|
||||
#endif
|
||||
log_mode_create_vm_done(vm);
|
||||
vm_vcpu_add_default(vm, vcpuid, guest_code);
|
||||
return vm;
|
||||
}
|
||||
@@ -264,10 +375,6 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid,
|
||||
#define DIRTY_MEM_BITS 30 /* 1G */
|
||||
#define PAGE_SHIFT_4K 12
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
static u64 dirty_log_manual_caps;
|
||||
#endif
|
||||
|
||||
static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
||||
unsigned long interval, uint64_t phys_offset)
|
||||
{
|
||||
@@ -275,6 +382,12 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
||||
struct kvm_vm *vm;
|
||||
unsigned long *bmap;
|
||||
|
||||
if (!log_mode_supported()) {
|
||||
print_skip("Log mode '%s' not supported",
|
||||
log_modes[host_log_mode].name);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We reserve page table for 2 times of extra dirty mem which
|
||||
* will definitely cover the original (1G+) test range. Here
|
||||
@@ -317,14 +430,6 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
||||
bmap = bitmap_alloc(host_num_pages);
|
||||
host_bmap_track = bitmap_alloc(host_num_pages);
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
struct kvm_enable_cap cap = {};
|
||||
|
||||
cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2;
|
||||
cap.args[0] = dirty_log_manual_caps;
|
||||
vm_enable_cap(vm, &cap);
|
||||
#endif
|
||||
|
||||
/* Add an extra memory slot for testing dirty logging */
|
||||
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
|
||||
guest_test_phys_mem,
|
||||
@@ -362,11 +467,8 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
||||
while (iteration < iterations) {
|
||||
/* Give the vcpu thread some time to dirty some pages */
|
||||
usleep(interval * 1000);
|
||||
kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap);
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0,
|
||||
host_num_pages);
|
||||
#endif
|
||||
log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX,
|
||||
bmap, host_num_pages);
|
||||
vm_dirty_log_verify(mode, bmap);
|
||||
iteration++;
|
||||
sync_global_to_guest(vm, iteration);
|
||||
@@ -410,6 +512,9 @@ static void help(char *name)
|
||||
TEST_HOST_LOOP_INTERVAL);
|
||||
printf(" -p: specify guest physical test memory offset\n"
|
||||
" Warning: a low offset can conflict with the loaded test code.\n");
|
||||
printf(" -M: specify the host logging mode "
|
||||
"(default: run all log modes). Supported modes: \n\t");
|
||||
log_modes_dump();
|
||||
printf(" -m: specify the guest mode ID to test "
|
||||
"(default: test all supported modes)\n"
|
||||
" This option may be used multiple times.\n"
|
||||
@@ -429,18 +534,7 @@ int main(int argc, char *argv[])
|
||||
bool mode_selected = false;
|
||||
uint64_t phys_offset = 0;
|
||||
unsigned int mode;
|
||||
int opt, i;
|
||||
|
||||
#ifdef USE_CLEAR_DIRTY_LOG
|
||||
dirty_log_manual_caps =
|
||||
kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
if (!dirty_log_manual_caps) {
|
||||
print_skip("KVM_CLEAR_DIRTY_LOG not available");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
||||
KVM_DIRTY_LOG_INITIALLY_SET);
|
||||
#endif
|
||||
int opt, i, j;
|
||||
|
||||
#ifdef __x86_64__
|
||||
guest_mode_init(VM_MODE_PXXV48_4K, true, true);
|
||||
@@ -464,7 +558,7 @@ int main(int argc, char *argv[])
|
||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
||||
#endif
|
||||
|
||||
while ((opt = getopt(argc, argv, "hi:I:p:m:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hi:I:p:m:M:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
iterations = strtol(optarg, NULL, 10);
|
||||
@@ -486,6 +580,26 @@ int main(int argc, char *argv[])
|
||||
"Guest mode ID %d too big", mode);
|
||||
guest_modes[mode].enabled = true;
|
||||
break;
|
||||
case 'M':
|
||||
if (!strcmp(optarg, "all")) {
|
||||
host_log_mode_option = LOG_MODE_ALL;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < LOG_MODE_NUM; i++) {
|
||||
if (!strcmp(optarg, log_modes[i].name)) {
|
||||
pr_info("Setting log mode to: '%s'\n",
|
||||
optarg);
|
||||
host_log_mode_option = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == LOG_MODE_NUM) {
|
||||
printf("Log mode '%s' invalid. Please choose "
|
||||
"from: ", optarg);
|
||||
log_modes_dump();
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
help(argv[0]);
|
||||
@@ -507,7 +621,18 @@ int main(int argc, char *argv[])
|
||||
TEST_ASSERT(guest_modes[i].supported,
|
||||
"Guest mode ID %d (%s) not supported.",
|
||||
i, vm_guest_mode_string(i));
|
||||
run_test(i, iterations, interval, phys_offset);
|
||||
if (host_log_mode_option == LOG_MODE_ALL) {
|
||||
/* Run each log mode */
|
||||
for (j = 0; j < LOG_MODE_NUM; j++) {
|
||||
pr_info("Testing Log Mode '%s'\n",
|
||||
log_modes[j].name);
|
||||
host_log_mode = j;
|
||||
run_test(i, iterations, interval, phys_offset);
|
||||
}
|
||||
} else {
|
||||
host_log_mode = host_log_mode_option;
|
||||
run_test(i, iterations, interval, phys_offset);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -63,9 +63,11 @@ enum vm_mem_backing_src_type {
|
||||
|
||||
int kvm_check_cap(long cap);
|
||||
int vm_enable_cap(struct kvm_vm *vm, struct kvm_enable_cap *cap);
|
||||
int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
struct kvm_enable_cap *cap);
|
||||
void vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size);
|
||||
|
||||
struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm);
|
||||
struct kvm_vm *_vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm);
|
||||
void kvm_vm_free(struct kvm_vm *vmp);
|
||||
void kvm_vm_restart(struct kvm_vm *vmp, int perm);
|
||||
void kvm_vm_release(struct kvm_vm *vmp);
|
||||
@@ -149,6 +151,7 @@ void vcpu_set_guest_debug(struct kvm_vm *vm, uint32_t vcpuid,
|
||||
struct kvm_guest_debug *debug);
|
||||
void vcpu_set_mp_state(struct kvm_vm *vm, uint32_t vcpuid,
|
||||
struct kvm_mp_state *mp_state);
|
||||
struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vm *vm, uint32_t vcpuid);
|
||||
void vcpu_regs_get(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs);
|
||||
void vcpu_regs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs);
|
||||
|
||||
@@ -294,6 +297,8 @@ int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd);
|
||||
memcpy(&(g), _p, sizeof(g)); \
|
||||
})
|
||||
|
||||
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid);
|
||||
|
||||
/* Common ucalls */
|
||||
enum {
|
||||
UCALL_NONE,
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* tools/testing/selftests/kvm/include/perf_test_util.h
|
||||
*
|
||||
* Copyright (C) 2020, Google LLC.
|
||||
*/
|
||||
|
||||
#ifndef SELFTEST_KVM_PERF_TEST_UTIL_H
|
||||
#define SELFTEST_KVM_PERF_TEST_UTIL_H
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
#define MAX_VCPUS 512
|
||||
|
||||
#define PAGE_SHIFT_4K 12
|
||||
#define PTES_PER_4K_PT 512
|
||||
|
||||
#define TEST_MEM_SLOT_INDEX 1
|
||||
|
||||
/* Default guest test virtual memory offset */
|
||||
#define DEFAULT_GUEST_TEST_MEM 0xc0000000
|
||||
|
||||
#define DEFAULT_PER_VCPU_MEM_SIZE (1 << 30) /* 1G */
|
||||
|
||||
/*
|
||||
* Guest physical memory offset of the testing memory slot.
|
||||
* This will be set to the topmost valid physical address minus
|
||||
* the test memory size.
|
||||
*/
|
||||
static uint64_t guest_test_phys_mem;
|
||||
|
||||
/*
|
||||
* Guest virtual memory offset of the testing memory slot.
|
||||
* Must not conflict with identity mapped test code.
|
||||
*/
|
||||
static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
|
||||
static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
|
||||
|
||||
/* Number of VCPUs for the test */
|
||||
static int nr_vcpus = 1;
|
||||
|
||||
struct vcpu_args {
|
||||
uint64_t gva;
|
||||
uint64_t pages;
|
||||
|
||||
/* Only used by the host userspace part of the vCPU thread */
|
||||
int vcpu_id;
|
||||
};
|
||||
|
||||
struct perf_test_args {
|
||||
struct kvm_vm *vm;
|
||||
uint64_t host_page_size;
|
||||
uint64_t guest_page_size;
|
||||
int wr_fract;
|
||||
|
||||
struct vcpu_args vcpu_args[MAX_VCPUS];
|
||||
};
|
||||
|
||||
static struct perf_test_args perf_test_args;
|
||||
|
||||
/*
|
||||
* Continuously write to the first 8 bytes of each page in the
|
||||
* specified region.
|
||||
*/
|
||||
static void guest_code(uint32_t vcpu_id)
|
||||
{
|
||||
struct vcpu_args *vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
|
||||
uint64_t gva;
|
||||
uint64_t pages;
|
||||
int i;
|
||||
|
||||
/* Make sure vCPU args data structure is not corrupt. */
|
||||
GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id);
|
||||
|
||||
gva = vcpu_args->gva;
|
||||
pages = vcpu_args->pages;
|
||||
|
||||
while (true) {
|
||||
for (i = 0; i < pages; i++) {
|
||||
uint64_t addr = gva + (i * perf_test_args.guest_page_size);
|
||||
|
||||
if (i % perf_test_args.wr_fract == 0)
|
||||
*(uint64_t *)addr = 0x0123456789ABCDEF;
|
||||
else
|
||||
READ_ONCE(*(uint64_t *)addr);
|
||||
}
|
||||
|
||||
GUEST_SYNC(1);
|
||||
}
|
||||
}
|
||||
|
||||
static struct kvm_vm *create_vm(enum vm_guest_mode mode, int vcpus,
|
||||
uint64_t vcpu_memory_bytes)
|
||||
{
|
||||
struct kvm_vm *vm;
|
||||
uint64_t pages = DEFAULT_GUEST_PHY_PAGES;
|
||||
uint64_t guest_num_pages;
|
||||
|
||||
/* Account for a few pages per-vCPU for stacks */
|
||||
pages += DEFAULT_STACK_PGS * vcpus;
|
||||
|
||||
/*
|
||||
* Reserve twice the ammount of memory needed to map the test region and
|
||||
* the page table / stacks region, at 4k, for page tables. Do the
|
||||
* calculation with 4K page size: the smallest of all archs. (e.g., 64K
|
||||
* page size guest will need even less memory for page tables).
|
||||
*/
|
||||
pages += (2 * pages) / PTES_PER_4K_PT;
|
||||
pages += ((2 * vcpus * vcpu_memory_bytes) >> PAGE_SHIFT_4K) /
|
||||
PTES_PER_4K_PT;
|
||||
pages = vm_adjust_num_guest_pages(mode, pages);
|
||||
|
||||
pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
|
||||
|
||||
vm = vm_create(mode, pages, O_RDWR);
|
||||
kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
|
||||
#ifdef __x86_64__
|
||||
vm_create_irqchip(vm);
|
||||
#endif
|
||||
|
||||
perf_test_args.vm = vm;
|
||||
perf_test_args.guest_page_size = vm_get_page_size(vm);
|
||||
perf_test_args.host_page_size = getpagesize();
|
||||
|
||||
TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0,
|
||||
"Guest memory size is not guest page size aligned.");
|
||||
|
||||
guest_num_pages = (vcpus * vcpu_memory_bytes) /
|
||||
perf_test_args.guest_page_size;
|
||||
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
|
||||
|
||||
/*
|
||||
* If there should be more memory in the guest test region than there
|
||||
* can be pages in the guest, it will definitely cause problems.
|
||||
*/
|
||||
TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm),
|
||||
"Requested more guest memory than address space allows.\n"
|
||||
" guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n",
|
||||
guest_num_pages, vm_get_max_gfn(vm), vcpus,
|
||||
vcpu_memory_bytes);
|
||||
|
||||
TEST_ASSERT(vcpu_memory_bytes % perf_test_args.host_page_size == 0,
|
||||
"Guest memory size is not host page size aligned.");
|
||||
|
||||
guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) *
|
||||
perf_test_args.guest_page_size;
|
||||
guest_test_phys_mem &= ~(perf_test_args.host_page_size - 1);
|
||||
|
||||
#ifdef __s390x__
|
||||
/* Align to 1M (segment size) */
|
||||
guest_test_phys_mem &= ~((1 << 20) - 1);
|
||||
#endif
|
||||
|
||||
pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem);
|
||||
|
||||
/* Add an extra memory slot for testing */
|
||||
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
|
||||
guest_test_phys_mem,
|
||||
TEST_MEM_SLOT_INDEX,
|
||||
guest_num_pages, 0);
|
||||
|
||||
/* Do mapping for the demand paging memory slot */
|
||||
virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0);
|
||||
|
||||
ucall_init(vm, NULL);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
static void add_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes)
|
||||
{
|
||||
vm_paddr_t vcpu_gpa;
|
||||
struct vcpu_args *vcpu_args;
|
||||
int vcpu_id;
|
||||
|
||||
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
|
||||
vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
|
||||
|
||||
vm_vcpu_add_default(vm, vcpu_id, guest_code);
|
||||
|
||||
#ifdef __x86_64__
|
||||
vcpu_set_cpuid(vm, vcpu_id, kvm_get_supported_cpuid());
|
||||
#endif
|
||||
|
||||
vcpu_args->vcpu_id = vcpu_id;
|
||||
vcpu_args->gva = guest_test_virt_mem +
|
||||
(vcpu_id * vcpu_memory_bytes);
|
||||
vcpu_args->pages = vcpu_memory_bytes /
|
||||
perf_test_args.guest_page_size;
|
||||
|
||||
vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes);
|
||||
pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n",
|
||||
vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */
|
||||
@@ -64,5 +64,7 @@ int64_t timespec_to_ns(struct timespec ts);
|
||||
struct timespec timespec_add_ns(struct timespec ts, int64_t ns);
|
||||
struct timespec timespec_add(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_sub(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_diff_now(struct timespec start);
|
||||
struct timespec timespec_div(struct timespec ts, int divisor);
|
||||
|
||||
#endif /* SELFTEST_KVM_TEST_UTIL_H */
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#define X86_CR4_SMAP (1ul << 21)
|
||||
#define X86_CR4_PKE (1ul << 22)
|
||||
|
||||
#define UNEXPECTED_VECTOR_PORT 0xfff0u
|
||||
|
||||
/* General Registers in 64-Bit Mode */
|
||||
struct gpr64_regs {
|
||||
u64 rax;
|
||||
@@ -59,7 +61,7 @@ struct gpr64_regs {
|
||||
struct desc64 {
|
||||
uint16_t limit0;
|
||||
uint16_t base0;
|
||||
unsigned base1:8, s:1, type:4, dpl:2, p:1;
|
||||
unsigned base1:8, type:4, s:1, dpl:2, p:1;
|
||||
unsigned limit1:4, avl:1, l:1, db:1, g:1, base2:8;
|
||||
uint32_t base3;
|
||||
uint32_t zero1;
|
||||
@@ -239,6 +241,11 @@ static inline struct desc_ptr get_idt(void)
|
||||
return idt;
|
||||
}
|
||||
|
||||
static inline void outl(uint16_t port, uint32_t value)
|
||||
{
|
||||
__asm__ __volatile__("outl %%eax, %%dx" : : "d"(port), "a"(value));
|
||||
}
|
||||
|
||||
#define SET_XMM(__var, __xmm) \
|
||||
asm volatile("movq %0, %%"#__xmm : : "r"(__var) : #__xmm)
|
||||
|
||||
@@ -338,6 +345,35 @@ uint32_t kvm_get_cpuid_max_basic(void);
|
||||
uint32_t kvm_get_cpuid_max_extended(void);
|
||||
void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits);
|
||||
|
||||
struct ex_regs {
|
||||
uint64_t rax, rcx, rdx, rbx;
|
||||
uint64_t rbp, rsi, rdi;
|
||||
uint64_t r8, r9, r10, r11;
|
||||
uint64_t r12, r13, r14, r15;
|
||||
uint64_t vector;
|
||||
uint64_t error_code;
|
||||
uint64_t rip;
|
||||
uint64_t cs;
|
||||
uint64_t rflags;
|
||||
};
|
||||
|
||||
void vm_init_descriptor_tables(struct kvm_vm *vm);
|
||||
void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid);
|
||||
void vm_handle_exception(struct kvm_vm *vm, int vector,
|
||||
void (*handler)(struct ex_regs *));
|
||||
|
||||
/*
|
||||
* set_cpuid() - overwrites a matching cpuid entry with the provided value.
|
||||
* matches based on ent->function && ent->index. returns true
|
||||
* if a match was found and successfully overwritten.
|
||||
* @cpuid: the kvm cpuid list to modify.
|
||||
* @ent: cpuid entry to insert
|
||||
*/
|
||||
bool set_cpuid(struct kvm_cpuid2 *cpuid, struct kvm_cpuid_entry2 *ent);
|
||||
|
||||
uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3);
|
||||
|
||||
/*
|
||||
* Basic CPU control in CR0
|
||||
*/
|
||||
|
||||
@@ -350,3 +350,7 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...)
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -94,6 +94,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
|
||||
struct kvm_run *run = vcpu_state(vm, vcpu_id);
|
||||
struct ucall ucall = {};
|
||||
|
||||
if (uc)
|
||||
memset(uc, 0, sizeof(*uc));
|
||||
|
||||
if (run->exit_reason == KVM_EXIT_MMIO &&
|
||||
run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
|
||||
vm_vaddr_t gva;
|
||||
|
||||
@@ -86,6 +86,34 @@ int vm_enable_cap(struct kvm_vm *vm, struct kvm_enable_cap *cap)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VCPU Enable Capability
|
||||
*
|
||||
* Input Args:
|
||||
* vm - Virtual Machine
|
||||
* vcpu_id - VCPU
|
||||
* cap - Capability
|
||||
*
|
||||
* Output Args: None
|
||||
*
|
||||
* Return: On success, 0. On failure a TEST_ASSERT failure is produced.
|
||||
*
|
||||
* Enables a capability (KVM_CAP_*) on the VCPU.
|
||||
*/
|
||||
int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
struct kvm_enable_cap *cap)
|
||||
{
|
||||
struct vcpu *vcpu = vcpu_find(vm, vcpu_id);
|
||||
int r;
|
||||
|
||||
TEST_ASSERT(vcpu, "cannot find vcpu %d", vcpu_id);
|
||||
|
||||
r = ioctl(vcpu->fd, KVM_ENABLE_CAP, cap);
|
||||
TEST_ASSERT(!r, "KVM_ENABLE_CAP vCPU ioctl failed,\n"
|
||||
" rc: %i, errno: %i", r, errno);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void vm_open(struct kvm_vm *vm, int perm)
|
||||
{
|
||||
vm->kvm_fd = open(KVM_DEV_PATH, perm);
|
||||
@@ -152,7 +180,7 @@ _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params)
|
||||
* descriptor to control the created VM is created with the permissions
|
||||
* given by perm (e.g. O_RDWR).
|
||||
*/
|
||||
struct kvm_vm *_vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
|
||||
struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
|
||||
{
|
||||
struct kvm_vm *vm;
|
||||
|
||||
@@ -243,11 +271,6 @@ struct kvm_vm *_vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
|
||||
return vm;
|
||||
}
|
||||
|
||||
struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
|
||||
{
|
||||
return _vm_create(mode, phy_pages, perm);
|
||||
}
|
||||
|
||||
/*
|
||||
* VM Restart
|
||||
*
|
||||
@@ -1204,6 +1227,9 @@ int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
do {
|
||||
rc = ioctl(vcpu->fd, KVM_RUN, NULL);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
|
||||
assert_on_unhandled_exception(vm, vcpuid);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1260,6 +1286,35 @@ void vcpu_set_mp_state(struct kvm_vm *vm, uint32_t vcpuid,
|
||||
"rc: %i errno: %i", ret, errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* VM VCPU Get Reg List
|
||||
*
|
||||
* Input Args:
|
||||
* vm - Virtual Machine
|
||||
* vcpuid - VCPU ID
|
||||
*
|
||||
* Output Args:
|
||||
* None
|
||||
*
|
||||
* Return:
|
||||
* A pointer to an allocated struct kvm_reg_list
|
||||
*
|
||||
* Get the list of guest registers which are supported for
|
||||
* KVM_GET_ONE_REG/KVM_SET_ONE_REG calls
|
||||
*/
|
||||
struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
{
|
||||
struct kvm_reg_list reg_list_n = { .n = 0 }, *reg_list;
|
||||
int ret;
|
||||
|
||||
ret = _vcpu_ioctl(vm, vcpuid, KVM_GET_REG_LIST, ®_list_n);
|
||||
TEST_ASSERT(ret == -1 && errno == E2BIG, "KVM_GET_REG_LIST n=0");
|
||||
reg_list = calloc(1, sizeof(*reg_list) + reg_list_n.n * sizeof(__u64));
|
||||
reg_list->n = reg_list_n.n;
|
||||
vcpu_ioctl(vm, vcpuid, KVM_GET_REG_LIST, reg_list);
|
||||
return reg_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* VM VCPU Regs Get
|
||||
*
|
||||
|
||||
@@ -50,6 +50,8 @@ struct kvm_vm {
|
||||
vm_paddr_t pgd;
|
||||
vm_vaddr_t gdt;
|
||||
vm_vaddr_t tss;
|
||||
vm_vaddr_t idt;
|
||||
vm_vaddr_t handlers;
|
||||
};
|
||||
|
||||
struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid);
|
||||
|
||||
@@ -241,3 +241,7 @@ void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent)
|
||||
fprintf(stream, "%*spstate: psw: 0x%.16llx:0x%.16llx\n",
|
||||
indent, "", vcpu->state->psw_mask, vcpu->state->psw_addr);
|
||||
}
|
||||
|
||||
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
|
||||
struct kvm_run *run = vcpu_state(vm, vcpu_id);
|
||||
struct ucall ucall = {};
|
||||
|
||||
if (uc)
|
||||
memset(uc, 0, sizeof(*uc));
|
||||
|
||||
if (run->exit_reason == KVM_EXIT_S390_SIEIC &&
|
||||
run->s390_sieic.icptcode == 4 &&
|
||||
(run->s390_sieic.ipa >> 8) == 0x83 && /* 0x83 means DIAGNOSE */
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
*
|
||||
* Copyright (C) 2020, Google LLC.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
/*
|
||||
@@ -81,6 +84,21 @@ struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
|
||||
return timespec_add_ns((struct timespec){0}, ns1 - ns2);
|
||||
}
|
||||
|
||||
struct timespec timespec_diff_now(struct timespec start)
|
||||
{
|
||||
struct timespec end;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
return timespec_sub(end, start);
|
||||
}
|
||||
|
||||
struct timespec timespec_div(struct timespec ts, int divisor)
|
||||
{
|
||||
int64_t ns = timespec_to_ns(ts) / divisor;
|
||||
|
||||
return timespec_add_ns((struct timespec){0}, ns);
|
||||
}
|
||||
|
||||
void print_skip(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
handle_exception:
|
||||
push %r15
|
||||
push %r14
|
||||
push %r13
|
||||
push %r12
|
||||
push %r11
|
||||
push %r10
|
||||
push %r9
|
||||
push %r8
|
||||
|
||||
push %rdi
|
||||
push %rsi
|
||||
push %rbp
|
||||
push %rbx
|
||||
push %rdx
|
||||
push %rcx
|
||||
push %rax
|
||||
mov %rsp, %rdi
|
||||
|
||||
call route_exception
|
||||
|
||||
pop %rax
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
pop %rbx
|
||||
pop %rbp
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
pop %r8
|
||||
pop %r9
|
||||
pop %r10
|
||||
pop %r11
|
||||
pop %r12
|
||||
pop %r13
|
||||
pop %r14
|
||||
pop %r15
|
||||
|
||||
/* Discard vector and error code. */
|
||||
add $16, %rsp
|
||||
iretq
|
||||
|
||||
/*
|
||||
* Build the handle_exception wrappers which push the vector/error code on the
|
||||
* stack and an array of pointers to those wrappers.
|
||||
*/
|
||||
.pushsection .rodata
|
||||
.globl idt_handlers
|
||||
idt_handlers:
|
||||
.popsection
|
||||
|
||||
.macro HANDLERS has_error from to
|
||||
vector = \from
|
||||
.rept \to - \from + 1
|
||||
.align 8
|
||||
|
||||
/* Fetch current address and append it to idt_handlers. */
|
||||
current_handler = .
|
||||
.pushsection .rodata
|
||||
.quad current_handler
|
||||
.popsection
|
||||
|
||||
.if ! \has_error
|
||||
pushq $0
|
||||
.endif
|
||||
pushq $vector
|
||||
jmp handle_exception
|
||||
vector = vector + 1
|
||||
.endr
|
||||
.endm
|
||||
|
||||
.global idt_handler_code
|
||||
idt_handler_code:
|
||||
HANDLERS has_error=0 from=0 to=7
|
||||
HANDLERS has_error=1 from=8 to=8
|
||||
HANDLERS has_error=0 from=9 to=9
|
||||
HANDLERS has_error=1 from=10 to=14
|
||||
HANDLERS has_error=0 from=15 to=16
|
||||
HANDLERS has_error=1 from=17 to=17
|
||||
HANDLERS has_error=0 from=18 to=255
|
||||
|
||||
.section .note.GNU-stack, "", %progbits
|
||||
@@ -12,9 +12,18 @@
|
||||
#include "../kvm_util_internal.h"
|
||||
#include "processor.h"
|
||||
|
||||
#ifndef NUM_INTERRUPTS
|
||||
#define NUM_INTERRUPTS 256
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CODE_SELECTOR 0x8
|
||||
#define DEFAULT_DATA_SELECTOR 0x10
|
||||
|
||||
/* Minimum physical address used for virtual translation tables. */
|
||||
#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
|
||||
|
||||
vm_vaddr_t exception_handlers;
|
||||
|
||||
/* Virtual translation table structure declarations */
|
||||
struct pageMapL4Entry {
|
||||
uint64_t present:1;
|
||||
@@ -392,11 +401,12 @@ static void kvm_seg_fill_gdt_64bit(struct kvm_vm *vm, struct kvm_segment *segp)
|
||||
desc->limit0 = segp->limit & 0xFFFF;
|
||||
desc->base0 = segp->base & 0xFFFF;
|
||||
desc->base1 = segp->base >> 16;
|
||||
desc->s = segp->s;
|
||||
desc->type = segp->type;
|
||||
desc->s = segp->s;
|
||||
desc->dpl = segp->dpl;
|
||||
desc->p = segp->present;
|
||||
desc->limit1 = segp->limit >> 16;
|
||||
desc->avl = segp->avl;
|
||||
desc->l = segp->l;
|
||||
desc->db = segp->db;
|
||||
desc->g = segp->g;
|
||||
@@ -556,9 +566,9 @@ static void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_m
|
||||
sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX);
|
||||
|
||||
kvm_seg_set_unusable(&sregs.ldt);
|
||||
kvm_seg_set_kernel_code_64bit(vm, 0x8, &sregs.cs);
|
||||
kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.ds);
|
||||
kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.es);
|
||||
kvm_seg_set_kernel_code_64bit(vm, DEFAULT_CODE_SELECTOR, &sregs.cs);
|
||||
kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.ds);
|
||||
kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.es);
|
||||
kvm_setup_tss_64bit(vm, &sregs.tr, 0x18, gdt_memslot, pgd_memslot);
|
||||
break;
|
||||
|
||||
@@ -1118,3 +1128,131 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)
|
||||
*va_bits = (entry->eax >> 8) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
struct idt_entry {
|
||||
uint16_t offset0;
|
||||
uint16_t selector;
|
||||
uint16_t ist : 3;
|
||||
uint16_t : 5;
|
||||
uint16_t type : 4;
|
||||
uint16_t : 1;
|
||||
uint16_t dpl : 2;
|
||||
uint16_t p : 1;
|
||||
uint16_t offset1;
|
||||
uint32_t offset2; uint32_t reserved;
|
||||
};
|
||||
|
||||
static void set_idt_entry(struct kvm_vm *vm, int vector, unsigned long addr,
|
||||
int dpl, unsigned short selector)
|
||||
{
|
||||
struct idt_entry *base =
|
||||
(struct idt_entry *)addr_gva2hva(vm, vm->idt);
|
||||
struct idt_entry *e = &base[vector];
|
||||
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->offset0 = addr;
|
||||
e->selector = selector;
|
||||
e->ist = 0;
|
||||
e->type = 14;
|
||||
e->dpl = dpl;
|
||||
e->p = 1;
|
||||
e->offset1 = addr >> 16;
|
||||
e->offset2 = addr >> 32;
|
||||
}
|
||||
|
||||
void kvm_exit_unexpected_vector(uint32_t value)
|
||||
{
|
||||
outl(UNEXPECTED_VECTOR_PORT, value);
|
||||
}
|
||||
|
||||
void route_exception(struct ex_regs *regs)
|
||||
{
|
||||
typedef void(*handler)(struct ex_regs *);
|
||||
handler *handlers = (handler *)exception_handlers;
|
||||
|
||||
if (handlers && handlers[regs->vector]) {
|
||||
handlers[regs->vector](regs);
|
||||
return;
|
||||
}
|
||||
|
||||
kvm_exit_unexpected_vector(regs->vector);
|
||||
}
|
||||
|
||||
void vm_init_descriptor_tables(struct kvm_vm *vm)
|
||||
{
|
||||
extern void *idt_handlers;
|
||||
int i;
|
||||
|
||||
vm->idt = vm_vaddr_alloc(vm, getpagesize(), 0x2000, 0, 0);
|
||||
vm->handlers = vm_vaddr_alloc(vm, 256 * sizeof(void *), 0x2000, 0, 0);
|
||||
/* Handlers have the same address in both address spaces.*/
|
||||
for (i = 0; i < NUM_INTERRUPTS; i++)
|
||||
set_idt_entry(vm, i, (unsigned long)(&idt_handlers)[i], 0,
|
||||
DEFAULT_CODE_SELECTOR);
|
||||
}
|
||||
|
||||
void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
{
|
||||
struct kvm_sregs sregs;
|
||||
|
||||
vcpu_sregs_get(vm, vcpuid, &sregs);
|
||||
sregs.idt.base = vm->idt;
|
||||
sregs.idt.limit = NUM_INTERRUPTS * sizeof(struct idt_entry) - 1;
|
||||
sregs.gdt.base = vm->gdt;
|
||||
sregs.gdt.limit = getpagesize() - 1;
|
||||
kvm_seg_set_kernel_data_64bit(NULL, DEFAULT_DATA_SELECTOR, &sregs.gs);
|
||||
vcpu_sregs_set(vm, vcpuid, &sregs);
|
||||
*(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers;
|
||||
}
|
||||
|
||||
void vm_handle_exception(struct kvm_vm *vm, int vector,
|
||||
void (*handler)(struct ex_regs *))
|
||||
{
|
||||
vm_vaddr_t *handlers = (vm_vaddr_t *)addr_gva2hva(vm, vm->handlers);
|
||||
|
||||
handlers[vector] = (vm_vaddr_t)handler;
|
||||
}
|
||||
|
||||
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
{
|
||||
if (vcpu_state(vm, vcpuid)->exit_reason == KVM_EXIT_IO
|
||||
&& vcpu_state(vm, vcpuid)->io.port == UNEXPECTED_VECTOR_PORT
|
||||
&& vcpu_state(vm, vcpuid)->io.size == 4) {
|
||||
/* Grab pointer to io data */
|
||||
uint32_t *data = (void *)vcpu_state(vm, vcpuid)
|
||||
+ vcpu_state(vm, vcpuid)->io.data_offset;
|
||||
|
||||
TEST_ASSERT(false,
|
||||
"Unexpected vectored event in guest (vector:0x%x)",
|
||||
*data);
|
||||
}
|
||||
}
|
||||
|
||||
bool set_cpuid(struct kvm_cpuid2 *cpuid,
|
||||
struct kvm_cpuid_entry2 *ent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cpuid->nent; i++) {
|
||||
struct kvm_cpuid_entry2 *cur = &cpuid->entries[i];
|
||||
|
||||
if (cur->function != ent->function || cur->index != ent->index)
|
||||
continue;
|
||||
|
||||
memcpy(cur, ent, sizeof(struct kvm_cpuid_entry2));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t a3)
|
||||
{
|
||||
uint64_t r;
|
||||
|
||||
asm volatile("vmcall"
|
||||
: "=a"(r)
|
||||
: "b"(a0), "c"(a1), "d"(a2), "S"(a3));
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
|
||||
struct kvm_run *run = vcpu_state(vm, vcpu_id);
|
||||
struct ucall ucall = {};
|
||||
|
||||
if (uc)
|
||||
memset(uc, 0, sizeof(*uc));
|
||||
|
||||
if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
|
||||
struct kvm_regs regs;
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020, Google LLC.
|
||||
*
|
||||
* Tests for KVM paravirtual feature disablement
|
||||
*/
|
||||
#include <asm/kvm_para.h>
|
||||
#include <linux/kvm_para.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
extern unsigned char rdmsr_start;
|
||||
extern unsigned char rdmsr_end;
|
||||
|
||||
static u64 do_rdmsr(u32 idx)
|
||||
{
|
||||
u32 lo, hi;
|
||||
|
||||
asm volatile("rdmsr_start: rdmsr;"
|
||||
"rdmsr_end:"
|
||||
: "=a"(lo), "=c"(hi)
|
||||
: "c"(idx));
|
||||
|
||||
return (((u64) hi) << 32) | lo;
|
||||
}
|
||||
|
||||
extern unsigned char wrmsr_start;
|
||||
extern unsigned char wrmsr_end;
|
||||
|
||||
static void do_wrmsr(u32 idx, u64 val)
|
||||
{
|
||||
u32 lo, hi;
|
||||
|
||||
lo = val;
|
||||
hi = val >> 32;
|
||||
|
||||
asm volatile("wrmsr_start: wrmsr;"
|
||||
"wrmsr_end:"
|
||||
: : "a"(lo), "c"(idx), "d"(hi));
|
||||
}
|
||||
|
||||
static int nr_gp;
|
||||
|
||||
static void guest_gp_handler(struct ex_regs *regs)
|
||||
{
|
||||
unsigned char *rip = (unsigned char *)regs->rip;
|
||||
bool r, w;
|
||||
|
||||
r = rip == &rdmsr_start;
|
||||
w = rip == &wrmsr_start;
|
||||
GUEST_ASSERT(r || w);
|
||||
|
||||
nr_gp++;
|
||||
|
||||
if (r)
|
||||
regs->rip = (uint64_t)&rdmsr_end;
|
||||
else
|
||||
regs->rip = (uint64_t)&wrmsr_end;
|
||||
}
|
||||
|
||||
struct msr_data {
|
||||
uint32_t idx;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#define TEST_MSR(msr) { .idx = msr, .name = #msr }
|
||||
#define UCALL_PR_MSR 0xdeadbeef
|
||||
#define PR_MSR(msr) ucall(UCALL_PR_MSR, 1, msr)
|
||||
|
||||
/*
|
||||
* KVM paravirtual msrs to test. Expect a #GP if any of these msrs are read or
|
||||
* written, as the KVM_CPUID_FEATURES leaf is cleared.
|
||||
*/
|
||||
static struct msr_data msrs_to_test[] = {
|
||||
TEST_MSR(MSR_KVM_SYSTEM_TIME),
|
||||
TEST_MSR(MSR_KVM_SYSTEM_TIME_NEW),
|
||||
TEST_MSR(MSR_KVM_WALL_CLOCK),
|
||||
TEST_MSR(MSR_KVM_WALL_CLOCK_NEW),
|
||||
TEST_MSR(MSR_KVM_ASYNC_PF_EN),
|
||||
TEST_MSR(MSR_KVM_STEAL_TIME),
|
||||
TEST_MSR(MSR_KVM_PV_EOI_EN),
|
||||
TEST_MSR(MSR_KVM_POLL_CONTROL),
|
||||
TEST_MSR(MSR_KVM_ASYNC_PF_INT),
|
||||
TEST_MSR(MSR_KVM_ASYNC_PF_ACK),
|
||||
};
|
||||
|
||||
static void test_msr(struct msr_data *msr)
|
||||
{
|
||||
PR_MSR(msr);
|
||||
do_rdmsr(msr->idx);
|
||||
GUEST_ASSERT(READ_ONCE(nr_gp) == 1);
|
||||
|
||||
nr_gp = 0;
|
||||
do_wrmsr(msr->idx, 0);
|
||||
GUEST_ASSERT(READ_ONCE(nr_gp) == 1);
|
||||
nr_gp = 0;
|
||||
}
|
||||
|
||||
struct hcall_data {
|
||||
uint64_t nr;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#define TEST_HCALL(hc) { .nr = hc, .name = #hc }
|
||||
#define UCALL_PR_HCALL 0xdeadc0de
|
||||
#define PR_HCALL(hc) ucall(UCALL_PR_HCALL, 1, hc)
|
||||
|
||||
/*
|
||||
* KVM hypercalls to test. Expect -KVM_ENOSYS when called, as the corresponding
|
||||
* features have been cleared in KVM_CPUID_FEATURES.
|
||||
*/
|
||||
static struct hcall_data hcalls_to_test[] = {
|
||||
TEST_HCALL(KVM_HC_KICK_CPU),
|
||||
TEST_HCALL(KVM_HC_SEND_IPI),
|
||||
TEST_HCALL(KVM_HC_SCHED_YIELD),
|
||||
};
|
||||
|
||||
static void test_hcall(struct hcall_data *hc)
|
||||
{
|
||||
uint64_t r;
|
||||
|
||||
PR_HCALL(hc);
|
||||
r = kvm_hypercall(hc->nr, 0, 0, 0, 0);
|
||||
GUEST_ASSERT(r == -KVM_ENOSYS);
|
||||
}
|
||||
|
||||
static void guest_main(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(msrs_to_test); i++) {
|
||||
test_msr(&msrs_to_test[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hcalls_to_test); i++) {
|
||||
test_hcall(&hcalls_to_test[i]);
|
||||
}
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
static void clear_kvm_cpuid_features(struct kvm_cpuid2 *cpuid)
|
||||
{
|
||||
struct kvm_cpuid_entry2 ent = {0};
|
||||
|
||||
ent.function = KVM_CPUID_FEATURES;
|
||||
TEST_ASSERT(set_cpuid(cpuid, &ent),
|
||||
"failed to clear KVM_CPUID_FEATURES leaf");
|
||||
}
|
||||
|
||||
static void pr_msr(struct ucall *uc)
|
||||
{
|
||||
struct msr_data *msr = (struct msr_data *)uc->args[0];
|
||||
|
||||
pr_info("testing msr: %s (%#x)\n", msr->name, msr->idx);
|
||||
}
|
||||
|
||||
static void pr_hcall(struct ucall *uc)
|
||||
{
|
||||
struct hcall_data *hc = (struct hcall_data *)uc->args[0];
|
||||
|
||||
pr_info("testing hcall: %s (%lu)\n", hc->name, hc->nr);
|
||||
}
|
||||
|
||||
static void handle_abort(struct ucall *uc)
|
||||
{
|
||||
TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0],
|
||||
__FILE__, uc->args[1]);
|
||||
}
|
||||
|
||||
#define VCPU_ID 0
|
||||
|
||||
static void enter_guest(struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_run *run;
|
||||
struct ucall uc;
|
||||
int r;
|
||||
|
||||
run = vcpu_state(vm, VCPU_ID);
|
||||
|
||||
while (true) {
|
||||
r = _vcpu_run(vm, VCPU_ID);
|
||||
TEST_ASSERT(!r, "vcpu_run failed: %d\n", r);
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
|
||||
"unexpected exit reason: %u (%s)",
|
||||
run->exit_reason, exit_reason_str(run->exit_reason));
|
||||
|
||||
switch (get_ucall(vm, VCPU_ID, &uc)) {
|
||||
case UCALL_PR_MSR:
|
||||
pr_msr(&uc);
|
||||
break;
|
||||
case UCALL_PR_HCALL:
|
||||
pr_hcall(&uc);
|
||||
break;
|
||||
case UCALL_ABORT:
|
||||
handle_abort(&uc);
|
||||
return;
|
||||
case UCALL_DONE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct kvm_enable_cap cap = {0};
|
||||
struct kvm_cpuid2 *best;
|
||||
struct kvm_vm *vm;
|
||||
|
||||
if (!kvm_check_cap(KVM_CAP_ENFORCE_PV_FEATURE_CPUID)) {
|
||||
pr_info("will skip kvm paravirt restriction tests.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vm = vm_create_default(VCPU_ID, 0, guest_main);
|
||||
|
||||
cap.cap = KVM_CAP_ENFORCE_PV_FEATURE_CPUID;
|
||||
cap.args[0] = 1;
|
||||
vcpu_enable_cap(vm, VCPU_ID, &cap);
|
||||
|
||||
best = kvm_get_supported_cpuid();
|
||||
clear_kvm_cpuid_features(best);
|
||||
vcpu_set_cpuid(vm, VCPU_ID, best);
|
||||
|
||||
vm_init_descriptor_tables(vm);
|
||||
vcpu_init_descriptor_tables(vm, VCPU_ID);
|
||||
vm_handle_exception(vm, GP_VECTOR, guest_gp_handler);
|
||||
|
||||
enter_guest(vm);
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
@@ -136,7 +136,7 @@ endif
|
||||
ifeq ($(OVERRIDE_TARGETS),)
|
||||
LOCAL_HDRS := $(selfdir)/kselftest_harness.h $(selfdir)/kselftest.h
|
||||
$(OUTPUT)/%:%.c $(LOCAL_HDRS)
|
||||
$(LINK.c) $^ $(LDLIBS) -o $@
|
||||
$(LINK.c) $(filter-out $(LOCAL_HDRS),$^) $(LDLIBS) -o $@
|
||||
|
||||
$(OUTPUT)/%.o:%.S
|
||||
$(COMPILE.S) $^ -o $@
|
||||
|
||||
@@ -4,3 +4,4 @@ CONFIG_USER_NS=y
|
||||
CONFIG_PID_NS=y
|
||||
CONFIG_NET_NS=y
|
||||
CONFIG_CGROUPS=y
|
||||
CONFIG_CHECKPOINT_RESTORE=y
|
||||
|
||||
@@ -204,7 +204,10 @@ TEST_F(child, fetch_fd)
|
||||
fd = sys_pidfd_getfd(self->pidfd, self->remote_fd, 0);
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
EXPECT_EQ(0, sys_kcmp(getpid(), self->pid, KCMP_FILE, fd, self->remote_fd));
|
||||
ret = sys_kcmp(getpid(), self->pid, KCMP_FILE, fd, self->remote_fd);
|
||||
if (ret < 0 && errno == ENOSYS)
|
||||
SKIP(return, "kcmp() syscall not supported");
|
||||
EXPECT_EQ(ret, 0);
|
||||
|
||||
ret = fcntl(fd, F_GETFD);
|
||||
ASSERT_GE(ret, 0);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/kcmp.h>
|
||||
|
||||
#include "pidfd.h"
|
||||
#include "../clone3/clone3_selftests.h"
|
||||
|
||||
@@ -330,7 +330,7 @@ static int test_pidfd_send_signal_recycled_pid_fail(void)
|
||||
ksft_exit_fail_msg("%s test: Failed to recycle pid %d\n",
|
||||
test_name, PID_RECYCLE);
|
||||
case PIDFD_SKIP:
|
||||
ksft_print_msg("%s test: Skipping test\n", test_name);
|
||||
ksft_test_result_skip("%s test: Skipping test\n", test_name);
|
||||
ret = 0;
|
||||
break;
|
||||
case PIDFD_XFAIL:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
rfi_flush
|
||||
entry_flush
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
/* Test that /proc/loadavg correctly reports last pid in pid namespace. */
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
// Test that values in /proc/uptime increment monotonically
|
||||
// while shifting across CPUs.
|
||||
#define _GNU_SOURCE
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -1758,10 +1758,10 @@ TEST_F(TRACE_poke, getpid_runs_normally)
|
||||
* and the code is stored as a positive value. \
|
||||
*/ \
|
||||
if (_result < 0) { \
|
||||
SYSCALL_RET(_regs) = -result; \
|
||||
SYSCALL_RET(_regs) = -_result; \
|
||||
(_regs).ccr |= 0x10000000; \
|
||||
} else { \
|
||||
SYSCALL_RET(_regs) = result; \
|
||||
SYSCALL_RET(_regs) = _result; \
|
||||
(_regs).ccr &= ~0x10000000; \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -1804,8 +1804,8 @@ TEST_F(TRACE_poke, getpid_runs_normally)
|
||||
#define SYSCALL_RET(_regs) (_regs).a[(_regs).windowbase * 4 + 2]
|
||||
#elif defined(__sh__)
|
||||
# define ARCH_REGS struct pt_regs
|
||||
# define SYSCALL_NUM(_regs) (_regs).gpr[3]
|
||||
# define SYSCALL_RET(_regs) (_regs).gpr[0]
|
||||
# define SYSCALL_NUM(_regs) (_regs).regs[3]
|
||||
# define SYSCALL_RET(_regs) (_regs).regs[0]
|
||||
#else
|
||||
# error "Do not know how to find your architecture's registers and syscalls"
|
||||
#endif
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DEV2 protocol ip pref 1 ingress flower dst_mac e4:11:22:11:4a:51 action drop",
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC filter show terse dev $DEV2 ingress",
|
||||
"verifyCmd": "$TC -br filter show dev $DEV2 ingress",
|
||||
"matchPattern": "filter protocol ip pref 1 flower.*handle",
|
||||
"matchCount": "1",
|
||||
"teardown": [
|
||||
@@ -119,7 +119,7 @@
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DEV2 protocol ip pref 1 ingress flower dst_mac e4:11:22:11:4a:51 action drop",
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC filter show terse dev $DEV2 ingress",
|
||||
"verifyCmd": "$TC -br filter show dev $DEV2 ingress",
|
||||
"matchPattern": " dst_mac e4:11:22:11:4a:51",
|
||||
"matchCount": "0",
|
||||
"teardown": [
|
||||
|
||||
@@ -316,6 +316,14 @@ pp sleep 3
|
||||
n2 ping -W 1 -c 1 192.168.241.1
|
||||
n1 wg set wg0 peer "$pub2" persistent-keepalive 0
|
||||
|
||||
# Test that sk_bound_dev_if works
|
||||
n1 ping -I wg0 -c 1 -W 1 192.168.241.2
|
||||
# What about when the mark changes and the packet must be rerouted?
|
||||
n1 iptables -t mangle -I OUTPUT -j MARK --set-xmark 1
|
||||
n1 ping -c 1 -W 1 192.168.241.2 # First the boring case
|
||||
n1 ping -I wg0 -c 1 -W 1 192.168.241.2 # Then the sk_bound_dev_if case
|
||||
n1 iptables -t mangle -D OUTPUT -j MARK --set-xmark 1
|
||||
|
||||
# Test that onion routing works, even when it loops
|
||||
n1 wg set wg0 peer "$pub3" allowed-ips 192.168.242.2/32 endpoint 192.168.241.2:5
|
||||
ip1 addr add 192.168.242.1/24 dev wg0
|
||||
|
||||
@@ -18,10 +18,12 @@ CONFIG_NF_NAT=y
|
||||
CONFIG_NETFILTER_XTABLES=y
|
||||
CONFIG_NETFILTER_XT_NAT=y
|
||||
CONFIG_NETFILTER_XT_MATCH_LENGTH=y
|
||||
CONFIG_NETFILTER_XT_MARK=y
|
||||
CONFIG_NF_CONNTRACK_IPV4=y
|
||||
CONFIG_NF_NAT_IPV4=y
|
||||
CONFIG_IP_NF_IPTABLES=y
|
||||
CONFIG_IP_NF_FILTER=y
|
||||
CONFIG_IP_NF_MANGLE=y
|
||||
CONFIG_IP_NF_NAT=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
CONFIG_IP_MULTIPLE_TABLES=y
|
||||
|
||||
Reference in New Issue
Block a user