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

Reply via email to