diff --git a/testing/mtetest/CMakeLists.txt b/testing/mtetest/CMakeLists.txt new file mode 100644 index 0000000000..a3a9737b81 --- /dev/null +++ b/testing/mtetest/CMakeLists.txt @@ -0,0 +1,23 @@ +# ############################################################################## +# apps/testing/mtetest/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_TESTING_MTE) + nuttx_add_application(NAME mtetest COMPILE_FLAGS ${CFLAGS} SRCS mtetest.c) +endif() diff --git a/testing/mtetest/Kconfig b/testing/mtetest/Kconfig new file mode 100644 index 0000000000..e4574d5814 --- /dev/null +++ b/testing/mtetest/Kconfig @@ -0,0 +1,11 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config TESTING_MTE + tristate "MTE instruction set test" + depends on ARM64_MTE + default n + ---help--- + Enable MTE instruction set test diff --git a/testing/mtetest/Make.defs b/testing/mtetest/Make.defs new file mode 100644 index 0000000000..e927e21187 --- /dev/null +++ b/testing/mtetest/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/testing/mtetest/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_TESTING_MTE),) +CONFIGURED_APPS += $(APPDIR)/testing/mtetest +endif diff --git a/testing/mtetest/Makefile b/testing/mtetest/Makefile new file mode 100644 index 0000000000..b95a2a04fb --- /dev/null +++ b/testing/mtetest/Makefile @@ -0,0 +1,28 @@ +############################################################################ +# apps/testing/mtetest/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +MAINSRC = mtetest.c +PROGNAME = mtetest +PRIORITY = $(CONFIG_TESTING_KASAN_PRIORITY) +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) + +include $(APPDIR)/Application.mk diff --git a/testing/mtetest/mtetest.c b/testing/mtetest/mtetest.c new file mode 100644 index 0000000000..a08ef38258 --- /dev/null +++ b/testing/mtetest/mtetest.c @@ -0,0 +1,371 @@ +/**************************************************************************** + * apps/testing/mtetest/mtetest.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MTETEST_BUFFER_LEN 512 + +struct mte_test_s +{ + FAR const char *name; + FAR void (*func)(void); +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void mtetest1(void); +static void mtetest2(void); +static void mtetest3(void); +static void mtetest4(void); +static void mtetest5(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The instruction requires a 16-byte aligned memory block. */ + +static aligned_data(16) char g_buffer[MTETEST_BUFFER_LEN]; + +static const struct mte_test_s g_mtetest[] = +{ + { "mtetest1", mtetest1 }, + { "mtetest2", mtetest2 }, + { "mtetest3", mtetest3 }, + { "mtetest4", mtetest4 }, + { "mtetest5", mtetest5 }, + { NULL, NULL } +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tagset + * + * Description: + * Applies tags to a memory block starting from the pointer `p` for a + * given size (`size`). The function iterates through the memory in + * 16-byte chunks and uses the `stg` (store tag) instruction to assign + * a tag to each address. + * + * - `p`: The starting pointer to the memory block. + * - `size`: The size of the memory block (in bytes) to tag. + * + * The function uses inline assembly to store the tag at each memory + * location in the specified region, ensuring that the entire block is + * tagged. + ****************************************************************************/ + +static void __attribute__((noinline)) tagset(void *p, size_t size) +{ + size_t i; + for (i = 0; i < size; i += 16) + { + asm("stg %0, [%0]" : : "r"(p + i)); + } +} + +/**************************************************************************** + * Name: tagcheck + * + * Description: + * Verifies the consistency of tags in memory block starting from ptr `p` + * for the given `size`. The function checks each 16-byte chunk using the + * `ldg` (load tag) instruction to load the tag and compares it with the + * original pointer `p` to ensure consistency. + * + * - `p`: The starting pointer to the memory block. + * - `size`: The size of the memory block (in bytes) to check for + * tag consistency. + * + * The function loads the tag for each chunk and ensures that the tag + * matches the expected value. If the tag does not match, the function + * triggers an assertion failure, providing a mechanism to validate + * correct memory tagging and access. + ****************************************************************************/ + +static void __attribute__((noinline)) tagcheck(void *p, size_t size) +{ + size_t i; + void *c; + + for (i = 0; i < size; i += 16) + { + asm("ldg %0, [%1]" : "=r"(c) : "r"(p + i), "0"(p)); + assert(c == p); + } +} + +/**************************************************************************** + * Name: mtetest1 + * + * Description: + * 1. Initializes the pointer `p0` to point to `g_buffer`, which is assumed + * to contain enough data. + * 2. Uses the assembly instruction `irg` to create a tagged pointer `p1` + * from `p0`. Asserts that `p1` is not equal to `p0`, confirming the + * tagging operation worked. + * 3. Uses the assembly instruction `subp` to calculate the difference + * between `p0` and `p1`, storing it in `c`. Asserts that `c` is zero, + * confirming that `p1` and `p0` are the same address. + * 4. Uses `stg` to store the tag from `p1` at the address of `p1`. + * 5. Uses `ldg` to load the tag from `p0` into `p2`. Asserts that `p1` + * and `p2` are equal, confirming the tag stored at `p0` is correctly + * retrieved into `p2`. + ****************************************************************************/ + +static void mtetest1(void) +{ + long c; + int *p0; + int *p1; + int *p2; + + p0 = (int *)g_buffer; + + asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(1l)); + assert(p1 != p0); + + asm("subp %0,%1,%2" : "=r"(c) : "r"(p0), "r"(p1)); + assert(c == 0); + + asm("stg %0, [%0]" : : "r"(p1)); + asm("ldg %0, [%1]" : "=r"(p2) : "r"(p0), "0"(p0)); + assert(p1 == p2); +} + +/**************************************************************************** + * Name: mtetest2 + * + * Description: + * 1. Initializes the pointer `p0` to point to `g_buffer`, which is assumed + * to contain sufficient data. + * 2. Uses the assembly instruction `irg` to create a tagged pointer `p1` + * from `p0` using `excl`. The `gmi` instruction is used to modify `excl`, + * and asserts that `excl` is different from 1, confirming the change. + * 3. Creates a second tagged pointer `p2` using the modified `excl` and + * asserts that `p1` and `p2` are different, validating that two distinct + * tagged pointers are created. + ****************************************************************************/ + +static void mtetest2(void) +{ + long excl = 1; + int *p0; + int *p1; + int *p2; + + p0 = (int *)g_buffer; + + /* Create two differently tagged pointers. */ + + asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(excl)); + asm("gmi %0,%1,%0" : "+r"(excl) : "r" (p1)); + assert(excl != 1); + + asm("irg %0,%1,%2" : "=r"(p2) : "r"(p0), "r"(excl)); + assert(p1 != p2); +} + +/**************************************************************************** + * Name: mtetest3 + * + * Description: + * 1. Initializes `p0` to point to `g_buffer`, which is assumed to contain + * enough data. + * 2. Uses the assembly instruction `irg` to create a tagged pointer `p1` + * from `p0`. It then uses `gmi` to modify the `excl` value, ensuring it + * is different from 1 (validated by an `assert`). + * 3. Uses `irg` again to create a tagged pointer `p2` from `p0`. Asserts + * that `p1` and `p2` are different, validating the creation of two + * distinct tagged pointers. + * 4. Stores the tag from the first pointer (`p1`) using the assembly + * instruction `stg`. + * 5. Stores the value at `p1` using the assembly instruction `str`, followed + * by a `yield` to allow other tasks to execute. + ****************************************************************************/ + +static void mtetest3(void) +{ + long excl = 1; + int *p0; + int *p1; + int *p2; + + p0 = (int *)g_buffer; + + /* Create two differently tagged pointers. */ + + asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(excl)); + asm("gmi %0,%1,%0" : "+r"(excl) : "r" (p1)); + assert(excl != 1); + + asm("irg %0,%1,%2" : "=r"(p2) : "r"(p0), "r"(excl)); + assert(p1 != p2); + + /* Store the tag from the first pointer. */ + + asm("stg %0, [%0]" : : "r"(p1)); + asm("str %0, [%0]; yield" : : "r"(p1)); +} + +/**************************************************************************** + * Name: mtetest4 + * + * Description: + * 1. Initializes the pointer `p0` to point to `g_buffer`, which is assumed + * to contain sufficient data. + * 2. Uses the assembly instruction `irg` (likely a custom instruction) to + * process `p0` and `excl`, storing the result in `p1`. + * 3. Calls the `tagset` function with `p1` and `MTETEST_BUFFER_LEN` to set + * tags for the buffer. + * 4. Calls the `tagcheck` function with `p1` and `MTETEST_BUFFER_LEN` to + * verify the tags for the buffer. + ****************************************************************************/ + +static void mtetest4(void) +{ + long excl = 1; + int *p0; + int *p1; + + p0 = (int *)g_buffer; + + /* Tag the pointer. */ + + asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(excl)); + + tagset(p1, MTETEST_BUFFER_LEN); + tagcheck(p1, MTETEST_BUFFER_LEN); +} + +/**************************************************************************** + * Name: mtetest5 + * + * Description: + * 1. Initializes the pointer `p0` to point to `g_buffer`, which is assumed + * to contain enough data. + * 2. Uses the assembly instruction `irg` (possibly a custom instruction) + * to process `p0` and `excl`, storing the result in `p1`. + * 3. Uses the assembly instruction `stg` to store data at the address + * `p0 + 16`. + * 4. Uses standard C syntax to assign the value 1 to the address `p0 + 16`. + * 5. Uses the assembly instruction `stg` to store data at the address + * `p1 + 16`. + * 6. Uses `assert` to verify that the value at `p1 + 16` is 1, ensuring + * that the assignment was successful. + ****************************************************************************/ + +static void mtetest5(void) +{ + long excl = 1; + int *p0; + int *p1; + + p0 = (int *)g_buffer; + + /* Tag the pointer. */ + + asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(excl)); + + /* Assign value 1 to the address p0 + 16 */ + + asm("stg %0, [%0]" : : "r"(p0 + 16)); + *(p0 + 16) = 1; + + asm("stg %0, [%0]" : : "r"(p1 + 16)); + assert(1 == *(p1 + 16)); +} + +static void spawn_test_process(const struct mte_test_s *test) +{ + char *args[3]; + int status; + pid_t pid; + + args[0] = "mtetest"; + args[1] = (char *)test->name; + args[2] = NULL; + + if (posix_spawn(&pid, "mtetest", NULL, NULL, args, environ) != 0) + { + perror("posix_spawn"); + return; + } + + waitpid(pid, &status, 0); + printf("Test '%s' completed\n", test->name); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int i; + + if (argc < 2) + { + /* Loop through the tests and spawn a process for each one */ + + for (i = 0; g_mtetest[i].name != NULL; i++) + { + printf("Spawning process for test: %s\n", g_mtetest[i].name); + spawn_test_process(&g_mtetest[i]); + } + + printf("All tests completed.\n"); + } + else + { + for (i = 0; g_mtetest[i].name != NULL; i++) + { + if (strcmp(argv[1], g_mtetest[i].name) == 0) + { + printf("Running test: %s\n", g_mtetest[i].name); + g_mtetest[i].func(); + break; + } + } + } + + return 0; +}