tests/tcg/aarch64: Add MTE gdbstub tests
Add tests to exercise the MTE stubs. The tests will only run if a version of GDB that supports MTE is available in the test environment. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> [AJB: re-base and checkpatch fixes] Message-Id: <20240628050850.536447-12-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240705084047.857176-41-alex.bennee@linaro.org>
This commit is contained in:
parent
f81198cefa
commit
340ca46b68
4
configure
vendored
4
configure
vendored
@ -1673,6 +1673,10 @@ for target in $target_list; do
|
||||
echo "GDB=$gdb_bin" >> $config_target_mak
|
||||
fi
|
||||
|
||||
if test "${arch}" = "aarch64" && version_ge ${gdb_version##* } 15.0; then
|
||||
echo "GDB_HAS_MTE=y" >> $config_target_mak
|
||||
fi
|
||||
|
||||
echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> Makefile.prereqs
|
||||
tcg_tests_targets="$tcg_tests_targets $target"
|
||||
fi
|
||||
|
@ -64,7 +64,7 @@ AARCH64_TESTS += bti-2
|
||||
|
||||
# MTE Tests
|
||||
ifneq ($(CROSS_CC_HAS_ARMV8_MTE),)
|
||||
AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7
|
||||
AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 mte-8
|
||||
mte-%: CFLAGS += $(CROSS_CC_HAS_ARMV8_MTE)
|
||||
endif
|
||||
|
||||
@ -131,6 +131,18 @@ run-gdbstub-sve-ioctls: sve-ioctls
|
||||
basic gdbstub SVE ZLEN support)
|
||||
|
||||
EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls
|
||||
|
||||
ifeq ($(GDB_HAS_MTE),y)
|
||||
run-gdbstub-mte: mte-8
|
||||
$(call run-test, $@, $(GDB_SCRIPT) \
|
||||
--gdb $(GDB) \
|
||||
--qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
|
||||
--bin $< --test $(AARCH64_SRC)/gdbstub/test-mte.py, \
|
||||
gdbstub MTE support)
|
||||
|
||||
EXTRA_RUNS += run-gdbstub-mte
|
||||
endif
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
|
86
tests/tcg/aarch64/gdbstub/test-mte.py
Normal file
86
tests/tcg/aarch64/gdbstub/test-mte.py
Normal file
@ -0,0 +1,86 @@
|
||||
from __future__ import print_function
|
||||
#
|
||||
# Test GDB memory-tag commands that exercise the stubs for the qIsAddressTagged,
|
||||
# qMemTag, and QMemTag packets. Logical tag-only commands rely on local
|
||||
# operations, hence don't exercise any stub.
|
||||
#
|
||||
# The test consists in breaking just after a atag() call (which sets the
|
||||
# allocation tag -- see mte-8.c for details) and setting/getting tags in
|
||||
# different memory locations and ranges starting at the address of the array
|
||||
# 'a'.
|
||||
#
|
||||
# This is launched via tests/guest-debug/run-test.py
|
||||
#
|
||||
|
||||
|
||||
import gdb
|
||||
import re
|
||||
from test_gdbstub import main, report
|
||||
|
||||
|
||||
PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \(0x[0-9a-f]+\)."
|
||||
PATTERN_1 = ".*(0x[0-9a-f]+)"
|
||||
|
||||
|
||||
def run_test():
|
||||
gdb.execute("break 95", False, True)
|
||||
gdb.execute("continue", False, True)
|
||||
try:
|
||||
# Test if we can check correctly that the allocation tag for
|
||||
# array 'a' matches the logical tag after atag() is called.
|
||||
co = gdb.execute("memory-tag check a", False, True)
|
||||
tags_match = re.findall(PATTERN_0, co, re.MULTILINE)
|
||||
if tags_match:
|
||||
report(True, f"{tags_match[0]}")
|
||||
else:
|
||||
report(False, "Logical and allocation tags don't match!")
|
||||
|
||||
# Test allocation tag 'set and print' commands. Commands on logical
|
||||
# tags rely on local operation and so don't exercise any stub.
|
||||
|
||||
# Set the allocation tag for the first granule (16 bytes) of
|
||||
# address starting at 'a' address to a known value, i.e. 0x04.
|
||||
gdb.execute("memory-tag set-allocation-tag a 1 04", False, True)
|
||||
|
||||
# Then set the allocation tag for the second granule to a known
|
||||
# value, i.e. 0x06. This tests that contiguous tag granules are
|
||||
# set correct and don't run over each other.
|
||||
gdb.execute("memory-tag set-allocation-tag a+16 1 06", False, True)
|
||||
|
||||
# Read the known values back and check if they remain the same.
|
||||
|
||||
co = gdb.execute("memory-tag print-allocation-tag a", False, True)
|
||||
first_tag = re.match(PATTERN_1, co)[1]
|
||||
|
||||
co = gdb.execute("memory-tag print-allocation-tag a+16", False, True)
|
||||
second_tag = re.match(PATTERN_1, co)[1]
|
||||
|
||||
if first_tag == "0x4" and second_tag == "0x6":
|
||||
report(True, "Allocation tags are correctly set/printed.")
|
||||
else:
|
||||
report(False, "Can't set/print allocation tags!")
|
||||
|
||||
# Now test fill pattern by setting a whole page with a pattern.
|
||||
gdb.execute("memory-tag set-allocation-tag a 4096 0a0b", False, True)
|
||||
|
||||
# And read back the tags of the last two granules in page so
|
||||
# we also test if the pattern is set correctly up to the end of
|
||||
# the page.
|
||||
co = gdb.execute("memory-tag print-allocation-tag a+4096-32", False, True)
|
||||
tag = re.match(PATTERN_1, co)[1]
|
||||
|
||||
co = gdb.execute("memory-tag print-allocation-tag a+4096-16", False, True)
|
||||
last_tag = re.match(PATTERN_1, co)[1]
|
||||
|
||||
if tag == "0xa" and last_tag == "0xb":
|
||||
report(True, "Fill pattern is ok.")
|
||||
else:
|
||||
report(False, "Fill pattern failed!")
|
||||
|
||||
except gdb.error:
|
||||
# This usually happens because a GDB version that does not
|
||||
# support memory tagging was used to run the test.
|
||||
report(False, "'memory-tag' command failed!")
|
||||
|
||||
|
||||
main(run_test, expected_arch="aarch64")
|
99
tests/tcg/aarch64/mte-8.c
Normal file
99
tests/tcg/aarch64/mte-8.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* To be compiled with -march=armv8.5-a+memtag
|
||||
*
|
||||
* This test is adapted from a Linux test. Please see:
|
||||
*
|
||||
* https://www.kernel.org/doc/html/next/arch/arm64/memory-tagging-extension.html#example-of-correct-usage
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <string.h>
|
||||
/*
|
||||
* From arch/arm64/include/uapi/asm/hwcap.h
|
||||
*/
|
||||
#define HWCAP2_MTE (1 << 18)
|
||||
|
||||
/*
|
||||
* From arch/arm64/include/uapi/asm/mman.h
|
||||
*/
|
||||
#define PROT_MTE 0x20
|
||||
|
||||
/*
|
||||
* Insert a random logical tag into the given pointer.
|
||||
*/
|
||||
#define insert_random_tag(ptr) ({ \
|
||||
uint64_t __val; \
|
||||
asm("irg %0, %1" : "=r" (__val) : "r" (ptr)); \
|
||||
__val; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Set the allocation tag on the destination address.
|
||||
*/
|
||||
#define set_tag(tagged_addr) do { \
|
||||
asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \
|
||||
} while (0)
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned char *a;
|
||||
unsigned long page_sz = sysconf(_SC_PAGESIZE);
|
||||
unsigned long hwcap2 = getauxval(AT_HWCAP2);
|
||||
|
||||
/* check if MTE is present */
|
||||
if (!(hwcap2 & HWCAP2_MTE)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the tagged address ABI, synchronous or asynchronous MTE
|
||||
* tag check faults (based on per-CPU preference) and allow all
|
||||
* non-zero tags in the randomly generated set.
|
||||
*/
|
||||
if (prctl(PR_SET_TAGGED_ADDR_CTRL,
|
||||
PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC |
|
||||
(0xfffe << PR_MTE_TAG_SHIFT),
|
||||
0, 0, 0)) {
|
||||
perror("prctl() failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
a = mmap(0, page_sz, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (a == MAP_FAILED) {
|
||||
perror("mmap() failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("a[] address is %p\n", a);
|
||||
|
||||
/*
|
||||
* Enable MTE on the above anonymous mmap. The flag could be passed
|
||||
* directly to mmap() and skip this step.
|
||||
*/
|
||||
if (mprotect(a, page_sz, PROT_READ | PROT_WRITE | PROT_MTE)) {
|
||||
perror("mprotect() failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* access with the default tag (0) */
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
|
||||
printf("a[0] = %hhu a[1] = %hhu\n", a[0], a[1]);
|
||||
|
||||
/* set the logical and allocation tags */
|
||||
a = (unsigned char *)insert_random_tag(a);
|
||||
set_tag(a);
|
||||
|
||||
printf("%p\n", a);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user