Gustavo Romero <gustavo.rom...@linaro.org> writes: > Add tests to exercise the MTE stubs. > > Signed-off-by: Gustavo Romero <gustavo.rom...@linaro.org> > --- > tests/tcg/aarch64/Makefile.target | 11 ++- > tests/tcg/aarch64/gdbstub/test-mte.py | 86 ++++++++++++++++++++++ > tests/tcg/aarch64/mte-8.c | 102 ++++++++++++++++++++++++++ > 3 files changed, 197 insertions(+), 2 deletions(-) > create mode 100644 tests/tcg/aarch64/gdbstub/test-mte.py > create mode 100644 tests/tcg/aarch64/mte-8.c > > diff --git a/tests/tcg/aarch64/Makefile.target > b/tests/tcg/aarch64/Makefile.target > index 70d728ae9a..d2e3f251eb 100644 > --- a/tests/tcg/aarch64/Makefile.target > +++ b/tests/tcg/aarch64/Makefile.target > @@ -62,7 +62,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 += -march=armv8.5-a+memtag > endif > > @@ -127,7 +127,14 @@ run-gdbstub-sve-ioctls: sve-ioctls > --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve-ioctl.py, \ > basic gdbstub SVE ZLEN support) > > -EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls > +run-gdbstub-mte: mte-8 > + $(call run-test, $@, $(GDB_SCRIPT) \ > + --gdb $(GDB) \ > + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ > + --bin "$< -s" --test $(AARCH64_SRC)/gdbstub/test-mte.py, \ > + gdbstub MTE support) > + > +EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls run-gdbstub-mte > endif > endif > > diff --git a/tests/tcg/aarch64/gdbstub/test-mte.py > b/tests/tcg/aarch64/gdbstub/test-mte.py > new file mode 100644 > index 0000000000..ec49eb8d2b > --- /dev/null > +++ b/tests/tcg/aarch64/gdbstub/test-mte.py > @@ -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 99", 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") > diff --git a/tests/tcg/aarch64/mte-8.c b/tests/tcg/aarch64/mte-8.c > new file mode 100644 > index 0000000000..367768e6b6 > --- /dev/null > +++ b/tests/tcg/aarch64/mte-8.c > @@ -0,0 +1,102 @@ > +/* > + * 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); > + > + if (!(argc == 2 && strcmp(argv[1], "-s") == 0)) { > + return 0; > + }
Whats this trying to do? I would expect the test case to be able to run normally without being debugged by gdb, so why do we need a particular command line to shortcut it here? > + > + /* 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; > +} -- Alex Bennée Virtualisation Tech Lead @ Linaro