aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilosz Wasilewski <milosz.wasilewski@linaro.org>2014-06-23 17:48:10 +0100
committerMilosz Wasilewski <milosz.wasilewski@linaro.org>2014-06-23 17:48:10 +0100
commita748ee530e2e7f72e123294313309f15c2f9026e (patch)
treeb09efe6a832da8c4f7ef0b8424ac205d5d4062aa
initial commitHEADmaster
-rw-r--r--Android.mk42
-rw-r--r--LICENCE30
-rw-r--r--Makefile51
-rw-r--r--README.md41
-rw-r--r--main.c115
-rwxr-xr-xrun-tests.sh72
-rw-r--r--test.c1243
-rw-r--r--test.h39
8 files changed, 1633 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..4db69a9
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,42 @@
+# Copyright 2014, ARM Limited
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of ARM Limited nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Make file for building under Android
+# No special options included.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := pointer_tagging_tests
+LOCAL_SRC_FILES := main.c test.c
+LOCAL_C_INCLUDES := test.h
+LOCAL_CFLAGS := -Wall -g -std=gnu99
+LOCAL_LDFLAGS :=
+LOCAL_SHARED_LIBRARIES :=
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
diff --git a/LICENCE b/LICENCE
new file mode 100644
index 0000000..49538db
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,30 @@
+LICENCE
+=======
+
+The software in this repository is covered by the following licence.
+
+// Copyright 2014, ARM Limited
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of ARM Limited nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..29f61d6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+# Copyright 2014, ARM Limited
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of ARM Limited nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CC = gcc
+CCFLAGS = -Wall -g -std=gnu99
+LDFLAGS = -static -lrt -lpthread
+
+SOURCES = $(wildcard *.c)
+HEADERS = $(wildcard *.h)
+EXEC = pointer_tagging_tests
+
+all: $(SOURCES) $(HEADERS)
+ $(CC) $(SOURCES) -o $(EXEC) $(CCFLAGS) $(DEFINE) $(LDFLAGS)
+
+notag:
+ make DEFINE=-DDISABLE_TAGS
+
+noasm:
+ make DEFINE=-DDISABLE_ASM
+
+noasm.notag: notag.noasm
+notag.noasm:
+ make DEFINE="-DDISABLE_TAGS -DDISABLE_ASM"
+
+clean:
+ rm -f *.o $(EXEC)
+
+.PHONY: clean notag
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..08823c5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+# Pointer tagging tests
+
+This test suite is designed to verify that the AArch64 tagged-addressing feature
+is properly supported. Tagged addressing is guaranteed to be available in the
+ARMv8 architecture, but it requires some kernel support.
+
+## Build
+
+To build the test suite on an AArch64 platform:
+
+ $ make
+
+To cross-compile, set `CC` to your cross-compiler's `gcc` exeuctable. For
+example:
+
+ $ make CC=aarch64-linux-gnu-gcc
+
+## Running
+
+To run the whole test suite, use the provided `run-tests.sh` script.
+
+ $ ./run-tests.sh # Run all tests.
+ $ ./run-tests.sh <pattern> # Run all tests matching pattern (using `grep`).
+
+Tests can be run by using `pointer_tagging_tests` directly, but this will only
+allow one test to be run at a time. Use `--help` for usage information.
+
+## Advanced Builds
+
+The test suite will build and run on other architectures (such as amd64) if you
+disable tagging and assembly:
+
+ $ make notag.noasm
+
+This disables several tests that require assembly, and also prevents other tests
+from using tagged addresses. This option is intended to aid debugging, in case a
+test is misbehaving. The options may also be specified alone:
+
+ $ make notag
+ $ make noasm
+
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..71850cf
--- /dev/null
+++ b/main.c
@@ -0,0 +1,115 @@
+// Copyright 2014, ARM Limited
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of ARM Limited nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+static void usage(char const * prog_name) {
+ printf("Usage: %s [options]\n", prog_name);
+ printf(" --help, -h\n");
+ printf(" Print this help text.\n");
+ printf(" --list, -l\n");
+ printf(" List available tests.\n");
+ printf(" --test=<test>\n");
+ printf(" -t <test>\n");
+ printf(" Run the specified test. Only one test may be specified\n");
+}
+
+int main(int argc, char** argv) {
+ // Keep stdio unbuffered so that assertion output is in the right order in
+ // relation to stdout.
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ char * test = NULL;
+ int test_count = 0;
+ int opt_help = 0;
+ int opt_list = 0;
+
+ char const * shortopts = "hlt:";
+ struct option longopts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"list", no_argument, NULL, 'l'},
+ {"test", required_argument, NULL, 't'},
+ {NULL, 0, NULL, 0}
+ };
+
+ test_init();
+
+ int index;
+ while (1) {
+ int c = getopt_long(argc, argv, shortopts, longopts, &index);
+
+ if (c == -1) {
+ // The end of the options.
+ break;
+ } else if (c == 0) {
+ // A longopt: Make it look like a shortopt so we can handle them together.
+ c = longopts[index].val;
+ }
+
+ // Treat everything else as a shortopt.
+ if (c == 'h') {
+ opt_help = 1;
+ } else if (c == 'l') {
+ opt_list = 1;
+ } else if (c == 't') { // --test, -t
+ test_count++;
+ test = optarg;
+ }
+ }
+
+ if (opt_help) {
+ usage(argv[0]);
+ return EXIT_SUCCESS;
+ }
+
+ if (opt_list) {
+ test_print_list();
+ return EXIT_SUCCESS;
+ }
+
+ if (test_count == 0) {
+ printf("Error: No test specified.\n");
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ } else if (test_count > 1) {
+ printf("Error: Multiple tests specified.\n");
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (test_run_by_name(test) == -1) {
+ printf("Test %s does not exist.\n", test);
+ return EXIT_FAILURE;
+ }
+
+ printf("Test '%s' PASSED.\n", test);
+ return EXIT_SUCCESS;
+}
diff --git a/run-tests.sh b/run-tests.sh
new file mode 100755
index 0000000..bbdc73a
--- /dev/null
+++ b/run-tests.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+# Copyright 2014, ARM Limited
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of ARM Limited nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+BIN=./pointer_tagging_tests
+
+if [ ! -x $BIN ]; then
+ echo "Couldn't find $BIN. Please run 'make' first."
+ echo "See README.md for details."
+ exit
+fi
+
+if [ -t 1 ]; then
+ CLR_PASS="\033[1;32m"
+ CLR_FAIL="\033[1;31m"
+ CLR_RESET="\033[m"
+else
+ CLR_PASS=""
+ CLR_FAIL=""
+ CLR_RESET=""
+fi
+
+# Filter the test list.
+if [ $# == 0 ]; then
+ TEST_LIST=`$BIN --list`
+else
+ TEST_LIST=`$BIN --list | grep "$1"`
+fi
+TEST_COUNT=`echo "$TEST_LIST" | wc -l`
+
+if [ -z "$TEST_LIST" ]; then
+ echo "No matching tests."
+ exit
+fi
+
+echo "==== Running $TEST_COUNT tests. ===="
+for TEST in $TEST_LIST
+do
+ OUT=`$BIN --test $TEST 2>&1`
+ if [ $? == 0 ]; then
+ echo -e "Test '$TEST' ${CLR_PASS}PASSED${CLR_RESET}."
+ else
+ echo -e "Test '$TEST' ${CLR_FAIL}FAILED${CLR_RESET}."
+ if [ -n "$OUT" ]; then
+ echo -e "Output:\n$OUT\n"
+ fi
+ fi
+done
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..65a18f0
--- /dev/null
+++ b/test.c
@@ -0,0 +1,1243 @@
+// Copyright 2014, ARM Limited
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of ARM Limited nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "test.h"
+
+
+#define CHECK(condition) assert(condition)
+
+
+// -----------------------------------------------------------------------------
+// Test utilities.
+// -----------------------------------------------------------------------------
+
+// Helpers for manipulating and checking tagged addresses.
+
+#ifndef DISABLE_TAGS
+static int const kTagShift = 56;
+static int const kTagCount = 256;
+static uint64_t const kTagMask = UINT64_C(0xff00000000000000);
+#else
+// With tags disabled, just pretend that there is only one tag (with value 0).
+static int const kTagShift = 0;
+static int const kTagCount = 1;
+static uint64_t const kTagMask = UINT64_C(0x0000000000000000);
+#endif
+
+
+// Return 'pointer' tagged with the specified tag.
+static void * get_tagged_address(void * pointer, uint8_t tag) {
+ CHECK(tag < kTagCount);
+ uint64_t mask = (uint64_t)tag << kTagShift;
+ uint64_t address = (uint64_t)pointer & ~kTagMask;
+ return (void*)(address | mask);
+}
+
+
+// Return the tag of 'pointer'.
+static uint8_t get_tag(void * pointer) {
+ return ((uint64_t)pointer & kTagMask) >> kTagShift;
+}
+
+
+// Check that 'pointer' has the specified tag.
+static void check_tag(void * pointer, uint8_t tag) {
+ CHECK(tag < kTagCount);
+ CHECK(get_tag(pointer) == tag);
+}
+
+
+// Return 'pointer' with a tag of 0.
+// Some build configurations don't use this function; the 'unused' attribute
+// silences a corresponding GCC warning.
+static __attribute__((unused)) void * get_clear_address(void * pointer) {
+ return get_tagged_address(pointer, 0);
+}
+
+
+// Signal-handling helpers.
+volatile int signal_counter;
+
+static void setup_signal(int sig_num,
+ void (*handler)(int, siginfo_t*, void*),
+ timer_t* timer_id) {
+ int ret;
+ struct sigevent event;
+ struct sigaction act;
+ struct itimerspec timer_specs;
+
+ memset(&event, 0, sizeof(struct sigevent));
+ memset(&act, 0, sizeof(struct sigaction));
+ memset(&timer_specs, 0, sizeof(struct itimerspec));
+
+ // Call signal_handler when receiving a signal.
+ act.sa_sigaction = handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+
+ ret = sigaction(sig_num, &act, NULL);
+ CHECK(ret == 0);
+
+ event.sigev_notify = SIGEV_SIGNAL;
+ event.sigev_signo = sig_num;
+
+ // Fire the signal every 10ms.
+ timer_specs.it_value.tv_sec = 1;
+ timer_specs.it_interval.tv_nsec = 10000000;
+
+ ret = timer_create(CLOCK_REALTIME, &event, timer_id);
+ CHECK(ret == 0);
+
+ timer_settime(*timer_id, 0, &timer_specs, NULL);
+ CHECK(ret == 0);
+}
+
+
+static void teardown_signal(timer_t* timer_id) {
+ int ret = timer_delete(*timer_id);
+ CHECK(ret == 0);
+}
+
+
+// -----------------------------------------------------------------------------
+// Tests.
+// -----------------------------------------------------------------------------
+
+
+// Read from a value using tagged addresses.
+static void test_read(void) {
+ uint64_t val = 0xbadbeef;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged = get_tagged_address(&val, tag);
+ val = tag;
+ CHECK(*tagged == tag);
+ check_tag(tagged, tag);
+ }
+}
+
+
+// Write to a value using tagged addresses.
+static void test_write(void) {
+ uint64_t val = 0xbadbeef;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged = get_tagged_address(&val, tag);
+ *tagged = tag;
+ CHECK(val == tag);
+ check_tag(tagged, tag);
+ }
+}
+
+
+// Read from sequential addresses in a heap buffer with varying tags.
+static void test_sequential_read_heap(void) {
+ uint8_t * data = calloc(kTagCount, sizeof(uint8_t));
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint8_t * tagged = get_tagged_address(data, tag);
+ data[tag] = tag;
+ CHECK(tagged[tag] == tag);
+ check_tag(tagged, tag);
+ }
+ free(data);
+}
+
+
+// Write to sequential addresses in a heap buffer with varying tags.
+static void test_sequential_write_heap(void) {
+ uint8_t * data = calloc(kTagCount, sizeof(uint8_t));
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint8_t * tagged = get_tagged_address(data, tag);
+ tagged[tag] = tag;
+ CHECK(data[tag] == tag);
+ check_tag(tagged, tag);
+ }
+ free(data);
+}
+
+
+// Read from sequential addresses in a stack buffer with varying tags.
+static void test_sequential_read_stack(void) {
+ uint8_t data[kTagCount];
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint8_t * tagged = get_tagged_address(data, tag);
+ data[tag] = tag;
+ CHECK(tagged[tag] == tag);
+ check_tag(tagged, tag);
+ }
+}
+
+
+// Write to sequential addresses in a stack buffer with varying tags.
+static void test_sequential_write_stack(void) {
+ uint8_t data[kTagCount];
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint8_t * tagged = get_tagged_address(data, tag);
+ tagged[tag] = tag;
+ CHECK(data[tag] == tag);
+ check_tag(tagged, tag);
+ }
+}
+
+
+// Access a large heap buffer (spanning multiple pages) using tagged addresses.
+static void test_large_heap(void) {
+ int count = 10000;
+ uint64_t * data = calloc(count, sizeof(uint64_t));
+
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ tagged_address[i] = i;
+ CHECK(data[i] == i);
+ check_tag(tagged_address, tag);
+ }
+ }
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ CHECK(tagged_address[i] == i);
+ check_tag(tagged_address, tag);
+ }
+ }
+ free(data);
+}
+
+
+// Access a large stack buffer (spanning multiple pages) using tagged addresses.
+static void test_large_stack(void) {
+ uint64_t data[10000];
+ int count = sizeof(data)/sizeof(data[0]);
+
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ tagged_address[i] = i;
+ CHECK(data[i] == i);
+ check_tag(tagged_address, tag);
+ }
+ }
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ CHECK(tagged_address[i] == i);
+ check_tag(tagged_address, tag);
+ }
+ }
+}
+
+
+// Test that accesses work with tagged addresses with concurrent signal
+// handlers. This checks that the process state is properly preserved when a
+// signal is received.
+static void handler_runtests(int sig_num, siginfo_t * siginfo, void * ucontext) {
+ test_read();
+ test_write();
+ signal_counter++;
+}
+
+static void test_signal(void) {
+ int watchdog = 1000000;
+ timer_t timer_id;
+ setup_signal(SIGINT, handler_runtests, &timer_id);
+ // Stop once a hundred signals have been handled.
+ while (signal_counter <= 100) {
+ usleep(1);
+ test_read();
+ test_write();
+ CHECK(--watchdog > 0);
+ }
+ teardown_signal(&timer_id);
+}
+
+
+// Test that signal handlers can themselves access tagged addresses.
+static uint64_t global_value;
+
+static void handler_tag_pointer(int sig_num, siginfo_t* siginfo, void* ucontext) {
+ static int tag = 0;
+ uint64_t * tagged = get_tagged_address(&global_value, tag);
+ *tagged = tag;
+ if (++tag >= kTagCount) tag = 0;
+ signal_counter++;
+}
+
+static void test_signal_handler(void) {
+ int watchdog = 1000000;
+ timer_t timer_id;
+ global_value = 0xbadbeef;
+ setup_signal(SIGTERM, handler_tag_pointer, &timer_id);
+ // Wait for the signal handler to execute at least kTagCount times.
+ while (signal_counter < kTagCount) {
+ usleep(1);
+ CHECK(--watchdog > 0);
+ }
+ teardown_signal(&timer_id);
+ CHECK(global_value < kTagCount);
+}
+
+
+// Test that memory allocated explicitly with MAP_PRIVATE can be accessed using
+// tagged addresses.
+static void test_mmap_private_data(void) {
+ int const count = 100;
+ uint64_t * data = (uint64_t*) mmap(NULL,
+ count * sizeof(uint64_t),
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE,
+ -1,
+ 0);
+ CHECK(data != MAP_FAILED);
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ tagged_address[i] = i;
+ CHECK(data[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ CHECK(tagged_address[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+
+ CHECK(munmap(data, count * sizeof(uint64_t)) == 0);
+}
+
+
+// Test that memory allocated explicitly with MAP_SHARED can be accessed using
+// tagged addresses.
+static void test_mmap_shared_data(void) {
+ int const count = 100;
+ uint64_t * data = (uint64_t*) mmap(NULL,
+ count * sizeof(uint64_t),
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED,
+ -1,
+ 0);
+ CHECK(data != MAP_FAILED);
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ tagged_address[i] = i;
+ CHECK(data[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ CHECK(tagged_address[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+
+ CHECK(munmap(data, count * sizeof(uint64_t)) == 0);
+}
+
+
+// Test that a large memory buffer (spanning multiple pages) allocated
+// explicitly with MAP_PRIVATE can be accessed using tagged addresses.
+static void test_large_mmap_private_data(void) {
+ int const count = 10000;
+ uint64_t * data = (uint64_t*) mmap(NULL,
+ count * sizeof(uint64_t),
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE,
+ -1,
+ 0);
+ CHECK(data != MAP_FAILED);
+
+ // The resulting address should never be tagged.
+ check_tag(data, 0);
+
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ tagged_address[i] = i;
+ CHECK(data[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ CHECK(tagged_address[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+
+ CHECK(munmap(data, count * sizeof(uint64_t)) == 0);
+}
+
+
+// Test that a large memory buffer (spanning multiple pages) allocated
+// explicitly with MAP_SHARED can be accessed using tagged addresses.
+static void test_large_mmap_shared_data(void) {
+ int const count = 10000;
+ uint64_t * data = (uint64_t*) mmap(NULL,
+ count * sizeof(uint64_t),
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED,
+ -1,
+ 0);
+ CHECK(data != MAP_FAILED);
+
+ // The resulting address should never be tagged.
+ check_tag(data, 0);
+
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ tagged_address[i] = i;
+ CHECK(data[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+ for (int tag = 0; tag < kTagCount; tag++) {
+ uint64_t * tagged_address = get_tagged_address(data, tag);
+ for (int i = 0; i < count; i++) {
+ CHECK(tagged_address[i] == i);
+ }
+ check_tag(tagged_address, tag);
+ }
+
+ CHECK(munmap(data, count * sizeof(uint64_t)) == 0);
+}
+
+
+// Test that memory allocated explicitly with MAP_SHARED can be accessed using
+// tagged addresses from multiple threads.
+static void test_multithreaded_mmap_shared_data(void) {
+ int watchdog = 1000000;
+ uint64_t * data = (uint64_t*) mmap(NULL,
+ kTagCount * kTagCount * sizeof(uint64_t),
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED,
+ -1,
+ 0);
+ CHECK(data != MAP_FAILED);
+
+ for (int i = 0; i < kTagCount; i++) {
+ data[i] = 0xbadbeef;
+ }
+
+ pid_t pid = fork();
+
+ // Write from the parent and check that the child observes the values.
+ for (int parent_tag = 0; parent_tag < kTagCount; parent_tag++) {
+ for (int child_tag = 0; child_tag < kTagCount; child_tag++) {
+ // Write (and read) a value that is unique to this iteration.
+ int index = (child_tag * kTagCount) + parent_tag;
+ uint64_t expected = UINT64_C(0x1111111100000000) + index;
+
+ if (pid != 0) {
+ // ---- Parent ----
+ // Write data to tagged addresses.
+ uint64_t volatile * tagged = get_tagged_address(data, parent_tag);
+ tagged[index] = expected;
+ __sync_synchronize();
+ } else {
+ // ---- Child ----
+ // Read data from a tagged address.
+ uint64_t volatile * tagged = get_tagged_address(data, child_tag);
+ while (tagged[index] != expected) {
+ CHECK(--watchdog > 0);
+ sched_yield();
+ __sync_synchronize();
+ }
+ }
+ }
+ }
+
+ // Do the same in reverse: write from the child.
+ for (int parent_tag = 0; parent_tag < kTagCount; parent_tag++) {
+ for (int child_tag = 0; child_tag < kTagCount; child_tag++) {
+ // Write (and read) a value that is unique to this iteration.
+ int index = (child_tag * kTagCount) + parent_tag;
+ uint64_t expected = UINT64_C(0x2222222200000000) + index;
+
+ if (pid != 0) {
+ // ---- Parent ----
+ // Read data from a tagged address.
+ uint64_t volatile * tagged = get_tagged_address(data, parent_tag);
+ while (tagged[index] != expected) {
+ CHECK(--watchdog > 0);
+ sched_yield();
+ __sync_synchronize();
+ }
+ } else {
+ // ---- Child ----
+ // Write data to tagged addresses.
+ uint64_t volatile * tagged = get_tagged_address(data, child_tag);
+ tagged[index] = expected;
+ __sync_synchronize();
+ }
+ }
+ }
+
+ if (pid != 0) {
+ // ---- Parent ----
+ int stat = 1;
+ CHECK(waitpid(pid, &stat, 0) == pid);
+ CHECK(stat == 0);
+ } else {
+ // ---- Child ----
+ exit(EXIT_SUCCESS);
+ }
+
+ CHECK(munmap(data, kTagCount * sizeof(uint64_t)) == 0);
+}
+
+
+#ifndef DISABLE_ASM
+
+
+static void test_load_store(void) {
+ uint64_t data_from[3] = {0xbadbeef, 0xbeefbad, 0xbeefbeef};
+ uint64_t data_to[3] = {0, 0, 0};
+ uint64_t* tagged_address_from_in = NULL;
+ uint64_t* tagged_address_from_out = NULL;
+ uint64_t* tagged_address_to_in = NULL;
+ uint64_t* tagged_address_to_out = NULL;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ tagged_address_from_in = get_tagged_address(data_from, tag);
+ tagged_address_to_in = get_tagged_address(data_to, tag);
+ asm volatile (
+ "ldp x10, x11, [%x[adr_from]] \n\t"
+ "stp x10, x11, [%x[adr_to]] \n\t"
+ "ldr x10, [%x[adr_from], #16] \n\t"
+ "str x10, [%x[adr_to], #16] \n\t"
+ : [adr_from] "=&r" (tagged_address_from_out),
+ [adr_to] "=&r" (tagged_address_to_out)
+ : "[adr_from]" (tagged_address_from_in),
+ "[adr_to]" (tagged_address_to_in)
+ : "x10",
+ "x11"
+ );
+ // Check that the tag is still present.
+ check_tag(tagged_address_from_out, tag);
+ check_tag(tagged_address_to_out, tag);
+ for (int i = 0; i < 3; i++) {
+ CHECK(data_to[i] == data_from[i]);
+ CHECK(tagged_address_to_in[i] == tagged_address_from_in[i]);
+ CHECK(tagged_address_to_out[i] == tagged_address_from_out[i]);
+ data_to[i] = 0;
+ }
+ }
+}
+
+
+static void test_load_store_preindex(void) {
+ uint64_t data_from[6] = {0xbadbeef,
+ 0xbeefbad,
+ 0xbeefbeef,
+ 0x123456789,
+ 0x987654321,
+ 0xabcdef};
+ uint64_t data_to[6] = {0, 0, 0, 0, 0, 0};
+ uint64_t* tagged_address_from_in = NULL;
+ uint64_t* tagged_address_from_out = NULL;
+ uint64_t* tagged_address_to_in = NULL;
+ uint64_t* tagged_address_to_out = NULL;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ tagged_address_from_in = get_tagged_address(data_from, tag);
+ tagged_address_to_in = get_tagged_address(data_to, tag);
+ asm volatile (
+ "add %x[adr_from], %x[adr_from], #8 \n\t"
+ "add %x[adr_to], %x[adr_to], #8 \n\t"
+ "ldp x10, x11, [%x[adr_from], #-8]! \n\t"
+ "stp x10, x11, [%x[adr_to], #-8]! \n\t"
+
+ "add %x[adr_from], %x[adr_from], #32 \n\t"
+ "add %x[adr_to], %x[adr_to], #32 \n\t"
+ "ldr x10, [%x[adr_from], #-16]! \n\t"
+ "str x10, [%x[adr_to], #-16]! \n\t"
+
+ "ldp x10, x11, [%x[adr_from], #8]! \n\t"
+ "stp x10, x11, [%x[adr_to], #8]! \n\t"
+ "ldr x10, [%x[adr_from], #16]! \n\t"
+ "str x10, [%x[adr_to], #16]! \n\t"
+ "sub %x[adr_from], %x[adr_from], #40 \n\t"
+ "sub %x[adr_to], %x[adr_to], #40 \n\t"
+ : [adr_from] "=&r" (tagged_address_from_out),
+ [adr_to] "=&r" (tagged_address_to_out)
+ : "[adr_from]" (tagged_address_from_in),
+ "[adr_to]" (tagged_address_to_in)
+ : "x10",
+ "x11"
+ );
+ // Check that the tag is still present.
+ check_tag(tagged_address_from_out, tag);
+ check_tag(tagged_address_to_out, tag);
+ for (int i = 0; i < 6; i++) {
+ CHECK(data_to[i] == data_from[i]);
+ CHECK(tagged_address_to_in[i] == tagged_address_from_in[i]);
+ CHECK(tagged_address_to_out[i] == tagged_address_from_out[i]);
+ data_to[i] = 0;
+ }
+ }
+}
+
+
+static void test_load_store_postindex(void) {
+ uint64_t data_from[6] = {0xbadbeef,
+ 0xbeefbad,
+ 0xbeefbeef,
+ 0x123456789,
+ 0x987654321,
+ 0xabcdef};
+ uint64_t data_to[6] = {0, 0, 0, 0, 0, 0};
+ uint64_t* tagged_address_from_in = NULL;
+ uint64_t* tagged_address_from_out = NULL;
+ uint64_t* tagged_address_to_in = NULL;
+ uint64_t* tagged_address_to_out = NULL;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ tagged_address_from_in = get_tagged_address(data_from, tag);
+ tagged_address_to_in = get_tagged_address(data_to, tag);
+ asm volatile (
+ "ldp x10, x11, [%x[adr_from]], #16 \n\t"
+ "stp x10, x11, [%x[adr_to]], #16 \n\t"
+ "ldr x10, [%x[adr_from]], #8 \n\t"
+ "str x10, [%x[adr_to]], #8 \n\t"
+
+ "add %x[adr_from], %x[adr_from], #16 \n\t"
+ "add %x[adr_to], %x[adr_to], #16 \n\t"
+ "ldr x10, [%x[adr_from]], #-16 \n\t"
+ "str x10, [%x[adr_to]], #-16 \n\t"
+ "ldp x10, x11, [%x[adr_from]], #-24 \n\t"
+ "stp x10, x11, [%x[adr_to]], #-24 \n\t"
+ : [adr_from] "=&r" (tagged_address_from_out),
+ [adr_to] "=&r" (tagged_address_to_out)
+ : "[adr_from]" (tagged_address_from_in),
+ "[adr_to]" (tagged_address_to_in)
+ : "x10",
+ "x11"
+ );
+ // Check that the tag is still present.
+ check_tag(tagged_address_from_out, tag);
+ check_tag(tagged_address_to_out, tag);
+ for (int i = 0; i < 6; i++) {
+ CHECK(data_to[i] == data_from[i]);
+ CHECK(tagged_address_to_in[i] == tagged_address_from_in[i]);
+ CHECK(tagged_address_to_out[i] == tagged_address_from_out[i]);
+ data_to[i] = 0;
+ }
+ }
+}
+
+
+static void test_load_store_register_offset(void) {
+ uint8_t data_from[kTagCount];
+ uint8_t* tagged_data_from_in = NULL;
+ uint8_t* tagged_data_from_out = NULL;
+ uint8_t data_to[kTagCount];
+ uint8_t* tagged_data_to_in = NULL;
+ uint8_t* tagged_data_to_out = NULL;
+ for (int tag1 = 0; tag1 < kTagCount; tag1++) {
+ for (int tag2 = 0; tag2 < kTagCount; tag2++) {
+ data_from[tag2] = tag2;
+ data_to[tag2] = 0xbb;
+ }
+ tagged_data_from_in = get_tagged_address(data_from, tag1);
+ tagged_data_to_in = get_tagged_address(data_to, tag1);
+ uint32_t counter = kTagCount;
+ asm volatile (
+ "0: \n\t"
+ "sub %w[cnt], %w[cnt], #8 \n\t"
+#ifdef DISABLE_TAGS
+ "mov w10, %w[cnt] \n\t"
+#else
+ "orr x10, %x[cnt], %x[cnt], lsl 54 \n\t"
+#endif
+ "ldr x11, [%x[data_from], x10] \n\t"
+ "str x11, [%x[data_to], x10] \n\t"
+ "cbnz %w[cnt], 0b \n\t"
+ : [cnt] "+&r" (counter),
+ [data_from] "=&r" (tagged_data_from_out),
+ [data_to] "=&r" (tagged_data_to_out)
+ : "[data_from]" (tagged_data_from_in),
+ "[data_to]" (tagged_data_to_in)
+ : "x10",
+ "x11"
+ );
+ check_tag(tagged_data_from_out, tag1);
+ check_tag(tagged_data_to_out, tag1);
+ for (int tag2 = 0; tag2 < kTagCount; tag2++) {
+ CHECK(tagged_data_from_out[tag2] == tag2);
+ CHECK(tagged_data_to_out[tag2] == tag2);
+ CHECK(data_to[tag2] == tag2);
+ }
+ }
+}
+
+
+static void test_load_store_s(void) {
+ uint32_t data_from[3] = {0xbadbeef,
+ 0xbeefbad,
+ 0xbeefbeef};
+ uint32_t data_to[3] = {0, 0, 0};
+ uint32_t* tagged_address_from_in = NULL;
+ uint32_t* tagged_address_from_out = NULL;
+ uint32_t* tagged_address_to_in = NULL;
+ uint32_t* tagged_address_to_out = NULL;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ tagged_address_from_in = get_tagged_address(data_from, tag);
+ tagged_address_to_in = get_tagged_address(data_to, tag);
+ asm volatile (
+ "ldp s16, s17, [%x[adr_from]] \n\t"
+ "stp s16, s17, [%x[adr_to]] \n\t"
+ "ldr s16, [%x[adr_from], #8] \n\t"
+ "str s16, [%x[adr_to], #8] \n\t"
+ : [adr_from] "=&r" (tagged_address_from_out),
+ [adr_to] "=&r" (tagged_address_to_out)
+ : "[adr_from]" (tagged_address_from_in),
+ "[adr_to]" (tagged_address_to_in)
+ : "s16",
+ "s17"
+ );
+ // Check that the tag is still present.
+ check_tag(tagged_address_from_out, tag);
+ check_tag(tagged_address_to_out, tag);
+ for (int i = 0; i < 3; i++) {
+ CHECK(tagged_address_to_in[i] == tagged_address_from_in[i]);
+ CHECK(tagged_address_to_out[i] == tagged_address_from_out[i]);
+ CHECK(data_to[i] == data_from[i]);
+ data_to[i] = 0;
+ }
+ }
+}
+
+
+static void test_load_store_d(void) {
+ uint64_t data_from[3] = {0xbadbeef,
+ 0xbeefbad,
+ 0xbeefbeef};
+ uint64_t data_to[3] = {0, 0, 0};
+ uint64_t* tagged_address_from_in = NULL;
+ uint64_t* tagged_address_from_out = NULL;
+ uint64_t* tagged_address_to_in = NULL;
+ uint64_t* tagged_address_to_out = NULL;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ tagged_address_from_in = get_tagged_address(data_from, tag);
+ tagged_address_to_in = get_tagged_address(data_to, tag);
+ asm volatile (
+ "ldp d16, d17, [%x[adr_from]] \n\t"
+ "stp d16, d17, [%x[adr_to]] \n\t"
+ "ldr d16, [%x[adr_from], #16] \n\t"
+ "str d16, [%x[adr_to], #16] \n\t"
+ : [adr_from] "=&r" (tagged_address_from_out),
+ [adr_to] "=&r" (tagged_address_to_out)
+ : "[adr_from]" (tagged_address_from_in),
+ "[adr_to]" (tagged_address_to_in)
+ : "d16",
+ "d17"
+ );
+ // Check that the tag is still present.
+ check_tag(tagged_address_from_out, tag);
+ check_tag(tagged_address_to_out, tag);
+ for (int i = 0; i < 3; i++) {
+ CHECK(tagged_address_to_in[i] == tagged_address_from_in[i]);
+ CHECK(tagged_address_to_out[i] == tagged_address_from_out[i]);
+ CHECK(data_to[i] == data_from[i]);
+ data_to[i] = 0;
+ }
+ }
+}
+
+
+static void test_load_store_q(void) {
+ uint64_t data_from[6] = {0xbadbeef,
+ 0xbeefbad,
+ 0xbeefbeef,
+ 0xbadbad,
+ 0x123456789,
+ 0x987654321};
+ uint64_t data_to[6] = {0, 0, 0, 0, 0, 0};
+ uint64_t* tagged_address_from_in = NULL;
+ uint64_t* tagged_address_from_out = NULL;
+ uint64_t* tagged_address_to_in = NULL;
+ uint64_t* tagged_address_to_out = NULL;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ tagged_address_from_in = get_tagged_address(data_from, tag);
+ tagged_address_to_in = get_tagged_address(data_to, tag);
+ asm volatile (
+ "ldp q16, q17, [%x[adr_from]] \n\t"
+ "stp q16, q17, [%x[adr_to]] \n\t"
+ "ldr q16, [%x[adr_from], #32] \n\t"
+ "str q16, [%x[adr_to], #32] \n\t"
+ : [adr_from] "=&r" (tagged_address_from_out),
+ [adr_to] "=&r" (tagged_address_to_out)
+ : "[adr_from]" (tagged_address_from_in),
+ "[adr_to]" (tagged_address_to_in)
+ : "q16",
+ "q17"
+ );
+ // Check that the tag is still present.
+ check_tag(tagged_address_from_out, tag);
+ check_tag(tagged_address_to_out, tag);
+ for (int i = 0; i < 6; i++) {
+ CHECK(tagged_address_to_in[i] == tagged_address_from_in[i]);
+ CHECK(tagged_address_to_out[i] == tagged_address_from_out[i]);
+ CHECK(data_to[i] == data_from[i]);
+ data_to[i] = 0;
+ }
+ }
+}
+
+
+static void test_load_store_neon(void) {
+ uint64_t data_from[10] = {0xbadbeef,
+ 0xbeefbad,
+ 0xbeefbeef,
+ 0xbadbad,
+ 0xabcddcba,
+ 0xdcbaabcd,
+ 0x543212345,
+ 0x123456789,
+ 0x987654321,
+ 0x123454321};
+ uint64_t data_to[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint64_t* tagged_address_from_in = NULL;
+ uint64_t* tagged_address_from_out = NULL;
+ uint64_t* tagged_address_to_in = NULL;
+ uint64_t* tagged_address_to_out = NULL;
+ for (int tag = 0; tag < kTagCount; tag++) {
+ tagged_address_from_in = get_tagged_address(data_from, tag);
+ tagged_address_to_in = get_tagged_address(data_to, tag);
+ asm volatile (
+ "ld1 {v16.D}[0], [%x[adr_from]], #8 \n\t"
+ "st1 {v16.D}[0], [%x[adr_to]], #8 \n\t"
+ "ld2 {v16.D, v17.D}[0], [%x[adr_from]], #16 \n\t"
+ "st2 {v16.D, v17.D}[0], [%x[adr_to]], #16 \n\t"
+ "ld3 {v16.D, v17.D, v18.D}[0], [%x[adr_from]], #24 \n\t"
+ "st3 {v16.D, v17.D, v18.D}[0], [%x[adr_to]], #24 \n\t"
+ "ld4 {v16.D, v17.D, v18.D, v19.D}[0], [%x[adr_from]], #32 \n\t"
+ "st4 {v16.D, v17.D, v18.D, v19.D}[0], [%x[adr_to]], #32 \n\t"
+ "sub %x[adr_to], %x[adr_to], #80 \n\t"
+ "sub %x[adr_from], %x[adr_from], #80 \n\t"
+ : [adr_from] "=&r" (tagged_address_from_out),
+ [adr_to] "=&r" (tagged_address_to_out)
+ : "[adr_from]" (tagged_address_from_in),
+ "[adr_to]" (tagged_address_to_in)
+ : "v16",
+ "v17",
+ "v18",
+ "v19"
+ );
+ // Check that the tag is still present.
+ check_tag(tagged_address_from_out, tag);
+ check_tag(tagged_address_to_out, tag);
+ for (int i = 0; i < 10; i++) {
+ CHECK(tagged_address_to_in[i] == tagged_address_from_in[i]);
+ CHECK(tagged_address_to_out[i] == tagged_address_from_out[i]);
+ CHECK(data_to[i] == data_from[i]);
+ data_to[i] = 0;
+ }
+ }
+}
+
+
+static void test_load_store_neon_ld1_register_offset(void) {
+ // We are allocating data for each tag with load/store of
+ // 4 uint16_t at a time.
+ const unsigned int data_size = kTagCount * 4;
+ uint16_t data_from[data_size];
+ uint16_t* tagged_data_from_in = NULL;
+ uint16_t* tagged_data_from_out = NULL;
+ uint16_t data_to[data_size];
+ uint16_t* tagged_data_to_in = NULL;
+ uint16_t* tagged_data_to_out = NULL;
+ for (int tag1 = 0; tag1 < kTagCount; tag1++) {
+ for(unsigned int i = 0; i < data_size; i++) {
+ data_from[i] = i;
+ data_to[i] = 0xbeef;
+ }
+ tagged_data_from_out = data_from;
+ tagged_data_to_out = data_to;
+ for (int tag2 = 0; tag2 < kTagCount; tag2++) {
+ // Initialize input data with the last output data, plus tag1.
+ tagged_data_from_in = get_tagged_address(tagged_data_from_out, tag1);
+ tagged_data_to_in = get_tagged_address(tagged_data_to_out, tag1);
+ asm volatile (
+ "mov x10, #8 \n\t"
+#ifndef DISABLE_TAGS
+ "orr x10, x10, %x[tag], lsl 56 \n\t"
+#endif
+ "ld1 {v16.D}[0], [%x[data_from]], x10 \n\t"
+ "st1 {v16.D}[0], [%x[data_to]], x10 \n\t"
+ : [data_from] "=&r" (tagged_data_from_out),
+ [data_to] "=&r" (tagged_data_to_out)
+ : "[data_from]" (tagged_data_from_in),
+#ifndef DISABLE_TAGS
+ [tag] "r" (tag2),
+#endif
+ "[data_to]" (tagged_data_to_in)
+ : "v16",
+ "x10"
+ );
+ check_tag(tagged_data_from_out, tag1 + tag2);
+ check_tag(tagged_data_to_out, tag1 + tag2);
+ // Clear the tags.
+ tagged_data_from_out = get_clear_address(tagged_data_from_out);
+ tagged_data_to_out = get_clear_address(tagged_data_to_out);
+ }
+ // Move data_out back to the first element.
+ tagged_data_from_out-=data_size;
+ tagged_data_to_out-=data_size;
+ for(unsigned int i = 0; i < data_size; i++) {
+ CHECK(tagged_data_from_out[i] == i);
+ CHECK(tagged_data_to_out[i] == i);
+ CHECK(data_to[i] == i);
+ }
+ }
+}
+
+
+static void test_load_store_neon_ld2_register_offset(void) {
+ // We are allocating data for each tag with load/store of
+ // 8 uint16_t at a time.
+ const unsigned int data_size = kTagCount * 8;
+ uint16_t data_from[data_size];
+ uint16_t* tagged_data_from_in = NULL;
+ uint16_t* tagged_data_from_out = NULL;
+ uint16_t data_to[data_size];
+ uint16_t* tagged_data_to_in = NULL;
+ uint16_t* tagged_data_to_out = NULL;
+ for (int tag1 = 0; tag1 < kTagCount; tag1++) {
+ for(unsigned int i = 0; i < data_size; i++) {
+ data_from[i] = i;
+ data_to[i] = 0xbeef;
+ }
+ tagged_data_from_out = data_from;
+ tagged_data_to_out = data_to;
+ for (int tag2 = 0; tag2 < kTagCount; tag2++) {
+ // Initialize input data with the last output data, plus tag1.
+ tagged_data_from_in = get_tagged_address(tagged_data_from_out, tag1);
+ tagged_data_to_in = get_tagged_address(tagged_data_to_out, tag1);
+ asm volatile (
+ "mov x10, #16 \n\t"
+#ifndef DISABLE_TAGS
+ "orr x10, x10, %x[tag], lsl 56 \n\t"
+#endif
+ "ld2 {v16.D, v17.D}[0], [%x[data_from]], x10 \n\t"
+ "st2 {v16.D, v17.D}[0], [%x[data_to]], x10 \n\t"
+ : [data_from] "=&r" (tagged_data_from_out),
+ [data_to] "=&r" (tagged_data_to_out)
+ : "[data_from]" (tagged_data_from_in),
+#ifndef DISABLE_TAGS
+ [tag] "r" (tag2),
+#endif
+ "[data_to]" (tagged_data_to_in)
+ : "v16",
+ "v17",
+ "x10"
+ );
+ check_tag(tagged_data_from_out, tag1 + tag2);
+ check_tag(tagged_data_to_out, tag1 + tag2);
+ // Clear the tags.
+ tagged_data_from_out = get_clear_address(tagged_data_from_out);
+ tagged_data_to_out = get_clear_address(tagged_data_to_out);
+ }
+ // Move data_out back to the first element.
+ tagged_data_from_out-=data_size;
+ tagged_data_to_out-=data_size;
+ for(unsigned int i = 0; i < data_size; i++) {
+ CHECK(tagged_data_from_out[i] == i);
+ CHECK(tagged_data_to_out[i] == i);
+ CHECK(data_to[i] == i);
+ }
+ }
+}
+
+
+static void test_load_store_neon_ld3_register_offset(void) {
+ // We are allocating data for each tag with load/store of
+ // 12 uint16_t at a time.
+ const unsigned int data_size = kTagCount * 12;
+ uint16_t data_from[data_size];
+ uint16_t* tagged_data_from_in = NULL;
+ uint16_t* tagged_data_from_out = NULL;
+ uint16_t data_to[data_size];
+ uint16_t* tagged_data_to_in = NULL;
+ uint16_t* tagged_data_to_out = NULL;
+ for (int tag1 = 0; tag1 < kTagCount; tag1++) {
+ for(unsigned int i = 0; i < data_size; i++) {
+ data_from[i] = i;
+ data_to[i] = 0xbeef;
+ }
+ tagged_data_from_out = data_from;
+ tagged_data_to_out = data_to;
+ for (int tag2 = 0; tag2 < kTagCount; tag2++) {
+ // Initialize input data with the last output data, plus tag1.
+ tagged_data_from_in = get_tagged_address(tagged_data_from_out, tag1);
+ tagged_data_to_in = get_tagged_address(tagged_data_to_out, tag1);
+ asm volatile (
+ "mov x10, #24 \n\t"
+#ifndef DISABLE_TAGS
+ "orr x10, x10, %x[tag], lsl 56 \n\t"
+#endif
+ "ld3 {v16.D, v17.D, v18.D}[0], [%x[data_from]], x10 \n\t"
+ "st3 {v16.D, v17.D, v18.D}[0], [%x[data_to]], x10 \n\t"
+ : [data_from] "=&r" (tagged_data_from_out),
+ [data_to] "=&r" (tagged_data_to_out)
+ : "[data_from]" (tagged_data_from_in),
+#ifndef DISABLE_TAGS
+ [tag] "r" (tag2),
+#endif
+ "[data_to]" (tagged_data_to_in)
+ : "v16",
+ "v17",
+ "v18",
+ "x10"
+ );
+ check_tag(tagged_data_from_out, tag1 + tag2);
+ check_tag(tagged_data_to_out, tag1 + tag2);
+ // Clear the tags.
+ tagged_data_from_out = get_clear_address(tagged_data_from_out);
+ tagged_data_to_out = get_clear_address(tagged_data_to_out);
+ }
+ // Move data_out back to the first element.
+ tagged_data_from_out-=data_size;
+ tagged_data_to_out-=data_size;
+ for(unsigned int i = 0; i < data_size; i++) {
+ CHECK(tagged_data_from_out[i] == i);
+ CHECK(tagged_data_to_out[i] == i);
+ CHECK(data_to[i] == i);
+ }
+ }
+}
+
+
+static void test_load_store_neon_ld4_register_offset(void) {
+ // We are allocating data for each tag with load/store of
+ // 16 uint16_t at a time.
+ const unsigned int data_size = kTagCount * 16;
+ uint16_t data_from[data_size];
+ uint16_t* tagged_data_from_in = NULL;
+ uint16_t* tagged_data_from_out = NULL;
+ uint16_t data_to[data_size];
+ uint16_t* tagged_data_to_in = NULL;
+ uint16_t* tagged_data_to_out = NULL;
+ for (int tag1 = 0; tag1 < kTagCount; tag1++) {
+ for(unsigned int i = 0; i < data_size; i++) {
+ data_from[i] = i;
+ data_to[i] = 0xbeef;
+ }
+ tagged_data_from_out = data_from;
+ tagged_data_to_out = data_to;
+ for (int tag2 = 0; tag2 < kTagCount; tag2++) {
+ // Initialize input data with the last output data, plus tag1.
+ tagged_data_from_in = get_tagged_address(tagged_data_from_out, tag1);
+ tagged_data_to_in = get_tagged_address(tagged_data_to_out, tag1);
+ asm volatile (
+ "mov x10, #32 \n\t"
+#ifndef DISABLE_TAGS
+ "orr x10, x10, %x[tag], lsl 56 \n\t"
+#endif
+ "ld4 {v16.D, v17.D, v18.D, v19.D}[0], [%x[data_from]], x10 \n\t"
+ "st4 {v16.D, v17.D, v18.D, v19.D}[0], [%x[data_to]], x10 \n\t"
+ : [data_from] "=&r" (tagged_data_from_out),
+ [data_to] "=&r" (tagged_data_to_out)
+ : "[data_from]" (tagged_data_from_in),
+#ifndef DISABLE_TAGS
+ [tag] "r" (tag2),
+#endif
+ "[data_to]" (tagged_data_to_in)
+ : "v16",
+ "v17",
+ "v18",
+ "v19",
+ "x10"
+ );
+ check_tag(tagged_data_from_out, tag1 + tag2);
+ check_tag(tagged_data_to_out, tag1 + tag2);
+ // Clear the tags.
+ tagged_data_from_out = get_clear_address(tagged_data_from_out);
+ tagged_data_to_out = get_clear_address(tagged_data_to_out);
+ }
+ // Move data_out back to the first element.
+ tagged_data_from_out-=data_size;
+ tagged_data_to_out-=data_size;
+ for(unsigned int i = 0; i < data_size; i++) {
+ CHECK(tagged_data_from_out[i] == i);
+ CHECK(tagged_data_to_out[i] == i);
+ CHECK(data_to[i] == i);
+ }
+ }
+}
+
+
+static void test_executable_memory(void) {
+ int ret;
+ int number_of_instructions = 4;
+ uint32_t* code = NULL;
+ uint64_t data;
+ uint64_t* data_adr = &data;
+ uint32_t* tagged_address_write = NULL;
+ uint32_t* tagged_address_exec = NULL;
+ code = (uint32_t*) mmap(NULL,
+ number_of_instructions * sizeof(uint32_t),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0);
+ CHECK(code != MAP_FAILED);
+ for (int tag1 = 0; tag1 < kTagCount; tag1++) {
+ // Write the following code with tagged addresses.
+ // add x0, x0, #1
+ // add x0, x0, #1
+ // add x0, x0, #1
+ // ret
+ tagged_address_write = get_tagged_address(code, tag1);
+ tagged_address_write[0] = 0x91000400;
+ tagged_address_write[1] = 0x91000400;
+ tagged_address_write[2] = 0x91000400;
+ tagged_address_write[3] = 0xd65f03c0;
+ __clear_cache((char*)tagged_address_write,
+ (char*)(&tagged_address_write[4]));
+ for (int tag2 = 0; tag2 < kTagCount; tag2++) {
+ data = 0;
+ // Branching to a tagged address should work (though the tag is cleared
+ // before the PC is written).
+ tagged_address_exec = get_tagged_address(code, tag2);
+ asm volatile(
+ "ldr x0, [%x[data]] \n\t"
+ "blr %x[code] \n\t"
+ "str x0, [%x[data]] \n\t"
+ : [data] "+&r" (data_adr)
+ : [code] "r" (tagged_address_exec)
+ : "x0", "x30"
+ );
+ CHECK(data == 3);
+ }
+ }
+ ret = munmap(code, number_of_instructions * sizeof(uint32_t));
+ CHECK(ret == 0);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Public interface.
+// -----------------------------------------------------------------------------
+
+
+#define MAX_TEST_COUNT 50
+struct {
+ int count;
+ struct {
+ char const * name;
+ void (*function)(void);
+ } tests[MAX_TEST_COUNT];
+} manifest;
+
+
+static void add_test_description(char const * name,
+ void (*function)(void)) {
+ CHECK(manifest.count < MAX_TEST_COUNT);
+ manifest.tests[manifest.count].name = name;
+ manifest.tests[manifest.count].function = function;
+ manifest.count++;
+}
+
+
+void test_init() {
+#define ADD_TEST_DESCRIPTION(name) add_test_description(#name, test_##name);
+
+ ADD_TEST_DESCRIPTION(read);
+ ADD_TEST_DESCRIPTION(write);
+ ADD_TEST_DESCRIPTION(sequential_read_heap);
+ ADD_TEST_DESCRIPTION(sequential_write_heap);
+ ADD_TEST_DESCRIPTION(sequential_read_stack);
+ ADD_TEST_DESCRIPTION(sequential_write_stack);
+ ADD_TEST_DESCRIPTION(large_heap);
+ ADD_TEST_DESCRIPTION(large_stack);
+ ADD_TEST_DESCRIPTION(signal);
+ ADD_TEST_DESCRIPTION(signal_handler);
+ ADD_TEST_DESCRIPTION(mmap_private_data);
+ ADD_TEST_DESCRIPTION(mmap_shared_data);
+ ADD_TEST_DESCRIPTION(large_mmap_private_data);
+ ADD_TEST_DESCRIPTION(large_mmap_shared_data);
+ ADD_TEST_DESCRIPTION(multithreaded_mmap_shared_data);
+#ifndef DISABLE_ASM
+ ADD_TEST_DESCRIPTION(load_store);
+ ADD_TEST_DESCRIPTION(load_store_preindex);
+ ADD_TEST_DESCRIPTION(load_store_postindex);
+ ADD_TEST_DESCRIPTION(load_store_register_offset);
+ ADD_TEST_DESCRIPTION(load_store_s);
+ ADD_TEST_DESCRIPTION(load_store_d);
+ ADD_TEST_DESCRIPTION(load_store_q);
+ ADD_TEST_DESCRIPTION(load_store_neon);
+ ADD_TEST_DESCRIPTION(load_store_neon_ld1_register_offset);
+ ADD_TEST_DESCRIPTION(load_store_neon_ld2_register_offset);
+ ADD_TEST_DESCRIPTION(load_store_neon_ld3_register_offset);
+ ADD_TEST_DESCRIPTION(load_store_neon_ld4_register_offset);
+ ADD_TEST_DESCRIPTION(executable_memory);
+#endif
+
+#undef ADD_TEST_DESCRIPTION
+}
+
+
+int test_run_by_name(char const * name) {
+ for (int i = 0; i < manifest.count; i++) {
+ if (strcmp(manifest.tests[i].name, name) == 0) {
+ manifest.tests[i].function();
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+void test_print_list() {
+ for (int i = 0; i < manifest.count; i++) {
+ puts(manifest.tests[i].name);
+ }
+}
+
+
diff --git a/test.h b/test.h
new file mode 100644
index 0000000..84bc05c
--- /dev/null
+++ b/test.h
@@ -0,0 +1,39 @@
+// Copyright 2014, ARM Limited
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of ARM Limited nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef TEST_H
+#define TEST_H
+
+// Initialize the test harness.
+void test_init();
+
+// Run the specified test. Returns -1 if the test doesn't exist, or 0 otherwise.
+int test_run_by_name(char const * name);
+
+// Print a list of test names (to stdout).
+void test_print_list();
+
+#endif