Add tests to exercise the MTE stubs.
Signed-off-by: Gustavo Romero <gustavo.romero@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;
+ }
+
+ /* 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;
+}
--
2.34.1
Gustavo Romero <gustavo.romero@linaro.org> writes:
> Add tests to exercise the MTE stubs.
>
> Signed-off-by: Gustavo Romero <gustavo.romero@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
Hi Alex!
On 6/14/24 8:42 AM, Alex Bennée wrote:
> Gustavo Romero <gustavo.romero@linaro.org> writes:
>
>> Add tests to exercise the MTE stubs.
>>
>> Signed-off-by: Gustavo Romero <gustavo.romero@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?
Good catch. This is a leftover. The first versions of the test
would cause a sigsegv, but I simplified it on this final version,
so it runs normally now.
Removed in v3. Thanks
>> +
>> + /* 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;
>> +}
>
© 2016 - 2025 Red Hat, Inc.