-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Showing
4 changed files
with
202 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |