linux/tools/testing/selftests/arm64/mte/mte_common_util.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (C) 2020 ARM Limited
   3
   4#include <fcntl.h>
   5#include <sched.h>
   6#include <signal.h>
   7#include <stdio.h>
   8#include <stdlib.h>
   9#include <unistd.h>
  10
  11#include <linux/auxvec.h>
  12#include <sys/auxv.h>
  13#include <sys/mman.h>
  14#include <sys/prctl.h>
  15
  16#include <asm/hwcap.h>
  17
  18#include "kselftest.h"
  19#include "mte_common_util.h"
  20#include "mte_def.h"
  21
  22#define INIT_BUFFER_SIZE       256
  23
  24struct mte_fault_cxt cur_mte_cxt;
  25static unsigned int mte_cur_mode;
  26static unsigned int mte_cur_pstate_tco;
  27
  28void mte_default_handler(int signum, siginfo_t *si, void *uc)
  29{
  30        unsigned long addr = (unsigned long)si->si_addr;
  31
  32        if (signum == SIGSEGV) {
  33#ifdef DEBUG
  34                ksft_print_msg("INFO: SIGSEGV signal at pc=%lx, fault addr=%lx, si_code=%lx\n",
  35                                ((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code);
  36#endif
  37                if (si->si_code == SEGV_MTEAERR) {
  38                        if (cur_mte_cxt.trig_si_code == si->si_code)
  39                                cur_mte_cxt.fault_valid = true;
  40                        return;
  41                }
  42                /* Compare the context for precise error */
  43                else if (si->si_code == SEGV_MTESERR) {
  44                        if (cur_mte_cxt.trig_si_code == si->si_code &&
  45                            ((cur_mte_cxt.trig_range >= 0 &&
  46                              addr >= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
  47                              addr <= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||
  48                             (cur_mte_cxt.trig_range < 0 &&
  49                              addr <= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
  50                              addr >= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)))) {
  51                                cur_mte_cxt.fault_valid = true;
  52                                /* Adjust the pc by 4 */
  53                                ((ucontext_t *)uc)->uc_mcontext.pc += 4;
  54                        } else {
  55                                ksft_print_msg("Invalid MTE synchronous exception caught!\n");
  56                                exit(1);
  57                        }
  58                } else {
  59                        ksft_print_msg("Unknown SIGSEGV exception caught!\n");
  60                        exit(1);
  61                }
  62        } else if (signum == SIGBUS) {
  63                ksft_print_msg("INFO: SIGBUS signal at pc=%lx, fault addr=%lx, si_code=%lx\n",
  64                                ((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code);
  65                if ((cur_mte_cxt.trig_range >= 0 &&
  66                     addr >= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
  67                     addr <= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||
  68                    (cur_mte_cxt.trig_range < 0 &&
  69                     addr <= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
  70                     addr >= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range))) {
  71                        cur_mte_cxt.fault_valid = true;
  72                        /* Adjust the pc by 4 */
  73                        ((ucontext_t *)uc)->uc_mcontext.pc += 4;
  74                }
  75        }
  76}
  77
  78void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *))
  79{
  80        struct sigaction sa;
  81
  82        sa.sa_sigaction = handler;
  83        sa.sa_flags = SA_SIGINFO;
  84        sigemptyset(&sa.sa_mask);
  85        sigaction(signal, &sa, NULL);
  86}
  87
  88void mte_wait_after_trig(void)
  89{
  90        sched_yield();
  91}
  92
  93void *mte_insert_tags(void *ptr, size_t size)
  94{
  95        void *tag_ptr;
  96        int align_size;
  97
  98        if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {
  99                ksft_print_msg("FAIL: Addr=%lx: invalid\n", ptr);
 100                return NULL;
 101        }
 102        align_size = MT_ALIGN_UP(size);
 103        tag_ptr = mte_insert_random_tag(ptr);
 104        mte_set_tag_address_range(tag_ptr, align_size);
 105        return tag_ptr;
 106}
 107
 108void mte_clear_tags(void *ptr, size_t size)
 109{
 110        if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {
 111                ksft_print_msg("FAIL: Addr=%lx: invalid\n", ptr);
 112                return;
 113        }
 114        size = MT_ALIGN_UP(size);
 115        ptr = (void *)MT_CLEAR_TAG((unsigned long)ptr);
 116        mte_clear_tag_address_range(ptr, size);
 117}
 118
 119static void *__mte_allocate_memory_range(size_t size, int mem_type, int mapping,
 120                                         size_t range_before, size_t range_after,
 121                                         bool tags, int fd)
 122{
 123        void *ptr;
 124        int prot_flag, map_flag;
 125        size_t entire_size = size + range_before + range_after;
 126
 127        if (mem_type != USE_MALLOC && mem_type != USE_MMAP &&
 128            mem_type != USE_MPROTECT) {
 129                ksft_print_msg("FAIL: Invalid allocate request\n");
 130                return NULL;
 131        }
 132        if (mem_type == USE_MALLOC)
 133                return malloc(entire_size) + range_before;
 134
 135        prot_flag = PROT_READ | PROT_WRITE;
 136        if (mem_type == USE_MMAP)
 137                prot_flag |= PROT_MTE;
 138
 139        map_flag = mapping;
 140        if (fd == -1)
 141                map_flag = MAP_ANONYMOUS | map_flag;
 142        if (!(mapping & MAP_SHARED))
 143                map_flag |= MAP_PRIVATE;
 144        ptr = mmap(NULL, entire_size, prot_flag, map_flag, fd, 0);
 145        if (ptr == MAP_FAILED) {
 146                ksft_print_msg("FAIL: mmap allocation\n");
 147                return NULL;
 148        }
 149        if (mem_type == USE_MPROTECT) {
 150                if (mprotect(ptr, entire_size, prot_flag | PROT_MTE)) {
 151                        munmap(ptr, size);
 152                        ksft_print_msg("FAIL: mprotect PROT_MTE property\n");
 153                        return NULL;
 154                }
 155        }
 156        if (tags)
 157                ptr = mte_insert_tags(ptr + range_before, size);
 158        return ptr;
 159}
 160
 161void *mte_allocate_memory_tag_range(size_t size, int mem_type, int mapping,
 162                                    size_t range_before, size_t range_after)
 163{
 164        return __mte_allocate_memory_range(size, mem_type, mapping, range_before,
 165                                           range_after, true, -1);
 166}
 167
 168void *mte_allocate_memory(size_t size, int mem_type, int mapping, bool tags)
 169{
 170        return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, -1);
 171}
 172
 173void *mte_allocate_file_memory(size_t size, int mem_type, int mapping, bool tags, int fd)
 174{
 175        int index;
 176        char buffer[INIT_BUFFER_SIZE];
 177
 178        if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {
 179                ksft_print_msg("FAIL: Invalid mmap file request\n");
 180                return NULL;
 181        }
 182        /* Initialize the file for mappable size */
 183        lseek(fd, 0, SEEK_SET);
 184        for (index = INIT_BUFFER_SIZE; index < size; index += INIT_BUFFER_SIZE)
 185                write(fd, buffer, INIT_BUFFER_SIZE);
 186        index -= INIT_BUFFER_SIZE;
 187        write(fd, buffer, size - index);
 188        return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, fd);
 189}
 190
 191void *mte_allocate_file_memory_tag_range(size_t size, int mem_type, int mapping,
 192                                         size_t range_before, size_t range_after, int fd)
 193{
 194        int index;
 195        char buffer[INIT_BUFFER_SIZE];
 196        int map_size = size + range_before + range_after;
 197
 198        if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {
 199                ksft_print_msg("FAIL: Invalid mmap file request\n");
 200                return NULL;
 201        }
 202        /* Initialize the file for mappable size */
 203        lseek(fd, 0, SEEK_SET);
 204        for (index = INIT_BUFFER_SIZE; index < map_size; index += INIT_BUFFER_SIZE)
 205                write(fd, buffer, INIT_BUFFER_SIZE);
 206        index -= INIT_BUFFER_SIZE;
 207        write(fd, buffer, map_size - index);
 208        return __mte_allocate_memory_range(size, mem_type, mapping, range_before,
 209                                           range_after, true, fd);
 210}
 211
 212static void __mte_free_memory_range(void *ptr, size_t size, int mem_type,
 213                                    size_t range_before, size_t range_after, bool tags)
 214{
 215        switch (mem_type) {
 216        case USE_MALLOC:
 217                free(ptr - range_before);
 218                break;
 219        case USE_MMAP:
 220        case USE_MPROTECT:
 221                if (tags)
 222                        mte_clear_tags(ptr, size);
 223                munmap(ptr - range_before, size + range_before + range_after);
 224                break;
 225        default:
 226                ksft_print_msg("FAIL: Invalid free request\n");
 227                break;
 228        }
 229}
 230
 231void mte_free_memory_tag_range(void *ptr, size_t size, int mem_type,
 232                               size_t range_before, size_t range_after)
 233{
 234        __mte_free_memory_range(ptr, size, mem_type, range_before, range_after, true);
 235}
 236
 237void mte_free_memory(void *ptr, size_t size, int mem_type, bool tags)
 238{
 239        __mte_free_memory_range(ptr, size, mem_type, 0, 0, tags);
 240}
 241
 242void mte_initialize_current_context(int mode, uintptr_t ptr, ssize_t range)
 243{
 244        cur_mte_cxt.fault_valid = false;
 245        cur_mte_cxt.trig_addr = ptr;
 246        cur_mte_cxt.trig_range = range;
 247        if (mode == MTE_SYNC_ERR)
 248                cur_mte_cxt.trig_si_code = SEGV_MTESERR;
 249        else if (mode == MTE_ASYNC_ERR)
 250                cur_mte_cxt.trig_si_code = SEGV_MTEAERR;
 251        else
 252                cur_mte_cxt.trig_si_code = 0;
 253}
 254
 255int mte_switch_mode(int mte_option, unsigned long incl_mask)
 256{
 257        unsigned long en = 0;
 258
 259        if (!(mte_option == MTE_SYNC_ERR || mte_option == MTE_ASYNC_ERR ||
 260              mte_option == MTE_NONE_ERR || incl_mask <= MTE_ALLOW_NON_ZERO_TAG)) {
 261                ksft_print_msg("FAIL: Invalid mte config option\n");
 262                return -EINVAL;
 263        }
 264        en = PR_TAGGED_ADDR_ENABLE;
 265        if (mte_option == MTE_SYNC_ERR)
 266                en |= PR_MTE_TCF_SYNC;
 267        else if (mte_option == MTE_ASYNC_ERR)
 268                en |= PR_MTE_TCF_ASYNC;
 269        else if (mte_option == MTE_NONE_ERR)
 270                en |= PR_MTE_TCF_NONE;
 271
 272        en |= (incl_mask << PR_MTE_TAG_SHIFT);
 273        /* Enable address tagging ABI, mte error reporting mode and tag inclusion mask. */
 274        if (!prctl(PR_SET_TAGGED_ADDR_CTRL, en, 0, 0, 0) == 0) {
 275                ksft_print_msg("FAIL:prctl PR_SET_TAGGED_ADDR_CTRL for mte mode\n");
 276                return -EINVAL;
 277        }
 278        return 0;
 279}
 280
 281#define ID_AA64PFR1_MTE_SHIFT           8
 282#define ID_AA64PFR1_MTE                 2
 283
 284int mte_default_setup(void)
 285{
 286        unsigned long hwcaps = getauxval(AT_HWCAP);
 287        unsigned long en = 0;
 288        int ret;
 289
 290        if (!(hwcaps & HWCAP_CPUID)) {
 291                ksft_print_msg("FAIL: CPUID registers unavailable\n");
 292                return KSFT_FAIL;
 293        }
 294        /* Read ID_AA64PFR1_EL1 register */
 295        asm volatile("mrs %0, id_aa64pfr1_el1" : "=r"(hwcaps) : : "memory");
 296        if (((hwcaps >> ID_AA64PFR1_MTE_SHIFT) & MT_TAG_MASK) != ID_AA64PFR1_MTE) {
 297                ksft_print_msg("FAIL: MTE features unavailable\n");
 298                return KSFT_SKIP;
 299        }
 300        /* Get current mte mode */
 301        ret = prctl(PR_GET_TAGGED_ADDR_CTRL, en, 0, 0, 0);
 302        if (ret < 0) {
 303                ksft_print_msg("FAIL:prctl PR_GET_TAGGED_ADDR_CTRL with error =%d\n", ret);
 304                return KSFT_FAIL;
 305        }
 306        if (ret & PR_MTE_TCF_SYNC)
 307                mte_cur_mode = MTE_SYNC_ERR;
 308        else if (ret & PR_MTE_TCF_ASYNC)
 309                mte_cur_mode = MTE_ASYNC_ERR;
 310        else if (ret & PR_MTE_TCF_NONE)
 311                mte_cur_mode = MTE_NONE_ERR;
 312
 313        mte_cur_pstate_tco = mte_get_pstate_tco();
 314        /* Disable PSTATE.TCO */
 315        mte_disable_pstate_tco();
 316        return 0;
 317}
 318
 319void mte_restore_setup(void)
 320{
 321        mte_switch_mode(mte_cur_mode, MTE_ALLOW_NON_ZERO_TAG);
 322        if (mte_cur_pstate_tco == MT_PSTATE_TCO_EN)
 323                mte_enable_pstate_tco();
 324        else if (mte_cur_pstate_tco == MT_PSTATE_TCO_DIS)
 325                mte_disable_pstate_tco();
 326}
 327
 328int create_temp_file(void)
 329{
 330        int fd;
 331        char filename[] = "/dev/shm/tmp_XXXXXX";
 332
 333        /* Create a file in the tmpfs filesystem */
 334        fd = mkstemp(&filename[0]);
 335        if (fd == -1) {
 336                ksft_print_msg("FAIL: Unable to open temporary file\n");
 337                return 0;
 338        }
 339        unlink(&filename[0]);
 340        return fd;
 341}
 342