Path: blob/master/tools/testing/selftests/arm64/mte/mte_common_util.c
26296 views
// SPDX-License-Identifier: GPL-2.01// Copyright (C) 2020 ARM Limited23#include <fcntl.h>4#include <sched.h>5#include <signal.h>6#include <stdio.h>7#include <stdlib.h>8#include <time.h>9#include <unistd.h>1011#include <linux/auxvec.h>12#include <sys/auxv.h>13#include <sys/mman.h>14#include <sys/prctl.h>1516#include <asm/hwcap.h>1718#include "kselftest.h"19#include "mte_common_util.h"20#include "mte_def.h"2122#ifndef SA_EXPOSE_TAGBITS23#define SA_EXPOSE_TAGBITS 0x0000080024#endif2526#define INIT_BUFFER_SIZE 2562728struct mte_fault_cxt cur_mte_cxt;29bool mtefar_support;30bool mtestonly_support;31static unsigned int mte_cur_mode;32static unsigned int mte_cur_pstate_tco;33static bool mte_cur_stonly;3435void mte_default_handler(int signum, siginfo_t *si, void *uc)36{37struct sigaction sa;38unsigned long addr = (unsigned long)si->si_addr;39unsigned char si_tag, si_atag;4041sigaction(signum, NULL, &sa);4243if (sa.sa_flags & SA_EXPOSE_TAGBITS) {44si_tag = MT_FETCH_TAG(addr);45si_atag = MT_FETCH_ATAG(addr);46addr = MT_CLEAR_TAGS(addr);47} else {48si_tag = 0;49si_atag = 0;50}5152if (signum == SIGSEGV) {53#ifdef DEBUG54ksft_print_msg("INFO: SIGSEGV signal at pc=%lx, fault addr=%lx, si_code=%lx, si_tag=%x, si_atag=%x\n",55((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code, si_tag, si_atag);56#endif57if (si->si_code == SEGV_MTEAERR) {58if (cur_mte_cxt.trig_si_code == si->si_code)59cur_mte_cxt.fault_valid = true;60else61ksft_print_msg("Got unexpected SEGV_MTEAERR at pc=%llx, fault addr=%lx\n",62((ucontext_t *)uc)->uc_mcontext.pc,63addr);64return;65}66/* Compare the context for precise error */67else if (si->si_code == SEGV_MTESERR) {68if ((!mtefar_support && si_atag) || (si_atag != MT_FETCH_ATAG(cur_mte_cxt.trig_addr))) {69ksft_print_msg("Invalid MTE synchronous exception caught for address tag! si_tag=%x, si_atag: %x\n", si_tag, si_atag);70exit(KSFT_FAIL);71}7273if (cur_mte_cxt.trig_si_code == si->si_code &&74((cur_mte_cxt.trig_range >= 0 &&75addr >= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&76addr <= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||77(cur_mte_cxt.trig_range < 0 &&78addr <= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&79addr >= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)))) {80cur_mte_cxt.fault_valid = true;81/* Adjust the pc by 4 */82((ucontext_t *)uc)->uc_mcontext.pc += 4;83} else {84ksft_print_msg("Invalid MTE synchronous exception caught!\n");85exit(1);86}87} else {88ksft_print_msg("Unknown SIGSEGV exception caught!\n");89exit(1);90}91} else if (signum == SIGBUS) {92ksft_print_msg("INFO: SIGBUS signal at pc=%llx, fault addr=%lx, si_code=%x\n",93((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code);94if ((cur_mte_cxt.trig_range >= 0 &&95addr >= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&96addr <= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||97(cur_mte_cxt.trig_range < 0 &&98addr <= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&99addr >= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range))) {100cur_mte_cxt.fault_valid = true;101/* Adjust the pc by 4 */102((ucontext_t *)uc)->uc_mcontext.pc += 4;103}104}105}106107void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *),108bool export_tags)109{110struct sigaction sa;111112sa.sa_sigaction = handler;113sa.sa_flags = SA_SIGINFO;114115if (export_tags && signal == SIGSEGV)116sa.sa_flags |= SA_EXPOSE_TAGBITS;117118sigemptyset(&sa.sa_mask);119sigaction(signal, &sa, NULL);120}121122void mte_wait_after_trig(void)123{124sched_yield();125}126127void *mte_insert_tags(void *ptr, size_t size)128{129void *tag_ptr;130int align_size;131132if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {133ksft_print_msg("FAIL: Addr=%p: invalid\n", ptr);134return NULL;135}136align_size = MT_ALIGN_UP(size);137tag_ptr = mte_insert_random_tag(ptr);138mte_set_tag_address_range(tag_ptr, align_size);139return tag_ptr;140}141142void mte_clear_tags(void *ptr, size_t size)143{144if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {145ksft_print_msg("FAIL: Addr=%p: invalid\n", ptr);146return;147}148size = MT_ALIGN_UP(size);149ptr = (void *)MT_CLEAR_TAG((unsigned long)ptr);150mte_clear_tag_address_range(ptr, size);151}152153void *mte_insert_atag(void *ptr)154{155unsigned char atag;156157atag = mtefar_support ? (random() % MT_ATAG_MASK) + 1 : 0;158return (void *)MT_SET_ATAG((unsigned long)ptr, atag);159}160161void *mte_clear_atag(void *ptr)162{163return (void *)MT_CLEAR_ATAG((unsigned long)ptr);164}165166static void *__mte_allocate_memory_range(size_t size, int mem_type, int mapping,167size_t range_before, size_t range_after,168bool tags, int fd)169{170void *ptr;171int prot_flag, map_flag;172size_t entire_size = size + range_before + range_after;173174switch (mem_type) {175case USE_MALLOC:176return malloc(entire_size) + range_before;177case USE_MMAP:178case USE_MPROTECT:179break;180default:181ksft_print_msg("FAIL: Invalid allocate request\n");182return NULL;183}184185prot_flag = PROT_READ | PROT_WRITE;186if (mem_type == USE_MMAP)187prot_flag |= PROT_MTE;188189map_flag = mapping;190if (fd == -1)191map_flag = MAP_ANONYMOUS | map_flag;192if (!(mapping & MAP_SHARED))193map_flag |= MAP_PRIVATE;194ptr = mmap(NULL, entire_size, prot_flag, map_flag, fd, 0);195if (ptr == MAP_FAILED) {196ksft_perror("mmap()");197return NULL;198}199if (mem_type == USE_MPROTECT) {200if (mprotect(ptr, entire_size, prot_flag | PROT_MTE)) {201ksft_perror("mprotect(PROT_MTE)");202munmap(ptr, size);203return NULL;204}205}206if (tags)207ptr = mte_insert_tags(ptr + range_before, size);208return ptr;209}210211void *mte_allocate_memory_tag_range(size_t size, int mem_type, int mapping,212size_t range_before, size_t range_after)213{214return __mte_allocate_memory_range(size, mem_type, mapping, range_before,215range_after, true, -1);216}217218void *mte_allocate_memory(size_t size, int mem_type, int mapping, bool tags)219{220return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, -1);221}222223void *mte_allocate_file_memory(size_t size, int mem_type, int mapping, bool tags, int fd)224{225int index;226char buffer[INIT_BUFFER_SIZE];227228if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {229ksft_print_msg("FAIL: Invalid mmap file request\n");230return NULL;231}232/* Initialize the file for mappable size */233lseek(fd, 0, SEEK_SET);234for (index = INIT_BUFFER_SIZE; index < size; index += INIT_BUFFER_SIZE) {235if (write(fd, buffer, INIT_BUFFER_SIZE) != INIT_BUFFER_SIZE) {236ksft_perror("initialising buffer");237return NULL;238}239}240index -= INIT_BUFFER_SIZE;241if (write(fd, buffer, size - index) != size - index) {242ksft_perror("initialising buffer");243return NULL;244}245return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, fd);246}247248void *mte_allocate_file_memory_tag_range(size_t size, int mem_type, int mapping,249size_t range_before, size_t range_after, int fd)250{251int index;252char buffer[INIT_BUFFER_SIZE];253int map_size = size + range_before + range_after;254255if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {256ksft_print_msg("FAIL: Invalid mmap file request\n");257return NULL;258}259/* Initialize the file for mappable size */260lseek(fd, 0, SEEK_SET);261for (index = INIT_BUFFER_SIZE; index < map_size; index += INIT_BUFFER_SIZE)262if (write(fd, buffer, INIT_BUFFER_SIZE) != INIT_BUFFER_SIZE) {263ksft_perror("initialising buffer");264return NULL;265}266index -= INIT_BUFFER_SIZE;267if (write(fd, buffer, map_size - index) != map_size - index) {268ksft_perror("initialising buffer");269return NULL;270}271return __mte_allocate_memory_range(size, mem_type, mapping, range_before,272range_after, true, fd);273}274275static void __mte_free_memory_range(void *ptr, size_t size, int mem_type,276size_t range_before, size_t range_after, bool tags)277{278switch (mem_type) {279case USE_MALLOC:280free(ptr - range_before);281break;282case USE_MMAP:283case USE_MPROTECT:284if (tags)285mte_clear_tags(ptr, size);286munmap(ptr - range_before, size + range_before + range_after);287break;288default:289ksft_print_msg("FAIL: Invalid free request\n");290break;291}292}293294void mte_free_memory_tag_range(void *ptr, size_t size, int mem_type,295size_t range_before, size_t range_after)296{297__mte_free_memory_range(ptr, size, mem_type, range_before, range_after, true);298}299300void mte_free_memory(void *ptr, size_t size, int mem_type, bool tags)301{302__mte_free_memory_range(ptr, size, mem_type, 0, 0, tags);303}304305void mte_initialize_current_context(int mode, uintptr_t ptr, ssize_t range)306{307cur_mte_cxt.fault_valid = false;308cur_mte_cxt.trig_addr = ptr;309cur_mte_cxt.trig_range = range;310if (mode == MTE_SYNC_ERR)311cur_mte_cxt.trig_si_code = SEGV_MTESERR;312else if (mode == MTE_ASYNC_ERR)313cur_mte_cxt.trig_si_code = SEGV_MTEAERR;314else315cur_mte_cxt.trig_si_code = 0;316}317318int mte_switch_mode(int mte_option, unsigned long incl_mask, bool stonly)319{320unsigned long en = 0;321322switch (mte_option) {323case MTE_NONE_ERR:324case MTE_SYNC_ERR:325case MTE_ASYNC_ERR:326break;327default:328ksft_print_msg("FAIL: Invalid MTE option %x\n", mte_option);329return -EINVAL;330}331332if (incl_mask & ~MT_INCLUDE_TAG_MASK) {333ksft_print_msg("FAIL: Invalid incl_mask %lx\n", incl_mask);334return -EINVAL;335}336337en = PR_TAGGED_ADDR_ENABLE;338switch (mte_option) {339case MTE_SYNC_ERR:340en |= PR_MTE_TCF_SYNC;341break;342case MTE_ASYNC_ERR:343en |= PR_MTE_TCF_ASYNC;344break;345case MTE_NONE_ERR:346en |= PR_MTE_TCF_NONE;347break;348}349350if (mtestonly_support && stonly)351en |= PR_MTE_STORE_ONLY;352353en |= (incl_mask << PR_MTE_TAG_SHIFT);354/* Enable address tagging ABI, mte error reporting mode and tag inclusion mask. */355if (prctl(PR_SET_TAGGED_ADDR_CTRL, en, 0, 0, 0) != 0) {356ksft_print_msg("FAIL:prctl PR_SET_TAGGED_ADDR_CTRL for mte mode\n");357return -EINVAL;358}359return 0;360}361362int mte_default_setup(void)363{364unsigned long hwcaps2 = getauxval(AT_HWCAP2);365unsigned long hwcaps3 = getauxval(AT_HWCAP3);366unsigned long en = 0;367int ret;368369/* To generate random address tag */370srandom(time(NULL));371372if (!(hwcaps2 & HWCAP2_MTE))373ksft_exit_skip("MTE features unavailable\n");374375mtefar_support = !!(hwcaps3 & HWCAP3_MTE_FAR);376377if (hwcaps3 & HWCAP3_MTE_STORE_ONLY)378mtestonly_support = true;379380/* Get current mte mode */381ret = prctl(PR_GET_TAGGED_ADDR_CTRL, en, 0, 0, 0);382if (ret < 0) {383ksft_print_msg("FAIL:prctl PR_GET_TAGGED_ADDR_CTRL with error =%d\n", ret);384return KSFT_FAIL;385}386if (ret & PR_MTE_TCF_SYNC)387mte_cur_mode = MTE_SYNC_ERR;388else if (ret & PR_MTE_TCF_ASYNC)389mte_cur_mode = MTE_ASYNC_ERR;390else if (ret & PR_MTE_TCF_NONE)391mte_cur_mode = MTE_NONE_ERR;392393mte_cur_stonly = (ret & PR_MTE_STORE_ONLY) ? true : false;394395mte_cur_pstate_tco = mte_get_pstate_tco();396/* Disable PSTATE.TCO */397mte_disable_pstate_tco();398return 0;399}400401void mte_restore_setup(void)402{403mte_switch_mode(mte_cur_mode, MTE_ALLOW_NON_ZERO_TAG, mte_cur_stonly);404if (mte_cur_pstate_tco == MT_PSTATE_TCO_EN)405mte_enable_pstate_tco();406else if (mte_cur_pstate_tco == MT_PSTATE_TCO_DIS)407mte_disable_pstate_tco();408}409410int create_temp_file(void)411{412int fd;413char filename[] = "/dev/shm/tmp_XXXXXX";414415/* Create a file in the tmpfs filesystem */416fd = mkstemp(&filename[0]);417if (fd == -1) {418ksft_perror(filename);419ksft_print_msg("FAIL: Unable to open temporary file\n");420return 0;421}422unlink(&filename[0]);423return fd;424}425426427