-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests: arm64: add hugetlb mte tests
The tests cover mmap, mprotect hugetlb with MTE prot and COW. Signed-off-by: Yang Shi <yang@os.amperecomputing.com> Link: https://lore.kernel.org/r/20241001225220.271178-2-yang@os.amperecomputing.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
- Loading branch information
Yang Shi
authored and
Catalin Marinas
committed
Oct 16, 2024
1 parent
25c17c4
commit 27879e8
Showing
1 changed file
with
285 additions
and
0 deletions.
There are no files selected for viewing
285 changes: 285 additions & 0 deletions
285
tools/testing/selftests/arm64/mte/check_hugetlb_options.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
// Copyright (C) 2024 Ampere Computing LLC | ||
|
||
#define _GNU_SOURCE | ||
|
||
#include <errno.h> | ||
#include <fcntl.h> | ||
#include <signal.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <ucontext.h> | ||
#include <sys/mman.h> | ||
#include <sys/stat.h> | ||
#include <sys/types.h> | ||
#include <sys/wait.h> | ||
|
||
#include "kselftest.h" | ||
#include "mte_common_util.h" | ||
#include "mte_def.h" | ||
|
||
#define TAG_CHECK_ON 0 | ||
#define TAG_CHECK_OFF 1 | ||
|
||
static unsigned long default_huge_page_size(void) | ||
{ | ||
unsigned long hps = 0; | ||
char *line = NULL; | ||
size_t linelen = 0; | ||
FILE *f = fopen("/proc/meminfo", "r"); | ||
|
||
if (!f) | ||
return 0; | ||
while (getline(&line, &linelen, f) > 0) { | ||
if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { | ||
hps <<= 10; | ||
break; | ||
} | ||
} | ||
|
||
free(line); | ||
fclose(f); | ||
return hps; | ||
} | ||
|
||
static bool is_hugetlb_allocated(void) | ||
{ | ||
unsigned long hps = 0; | ||
char *line = NULL; | ||
size_t linelen = 0; | ||
FILE *f = fopen("/proc/meminfo", "r"); | ||
|
||
if (!f) | ||
return false; | ||
while (getline(&line, &linelen, f) > 0) { | ||
if (sscanf(line, "Hugetlb: %lu kB", &hps) == 1) { | ||
hps <<= 10; | ||
break; | ||
} | ||
} | ||
|
||
free(line); | ||
fclose(f); | ||
|
||
if (hps > 0) | ||
return true; | ||
|
||
return false; | ||
} | ||
|
||
static void write_sysfs(char *str, unsigned long val) | ||
{ | ||
FILE *f; | ||
|
||
f = fopen(str, "w"); | ||
if (!f) { | ||
ksft_print_msg("ERR: missing %s\n", str); | ||
return; | ||
} | ||
fprintf(f, "%lu", val); | ||
fclose(f); | ||
} | ||
|
||
static void allocate_hugetlb() | ||
{ | ||
write_sysfs("/proc/sys/vm/nr_hugepages", 2); | ||
} | ||
|
||
static void free_hugetlb() | ||
{ | ||
write_sysfs("/proc/sys/vm/nr_hugepages", 0); | ||
} | ||
|
||
static int check_child_tag_inheritance(char *ptr, int size, int mode) | ||
{ | ||
int i, parent_tag, child_tag, fault, child_status; | ||
pid_t child; | ||
|
||
parent_tag = MT_FETCH_TAG((uintptr_t)ptr); | ||
fault = 0; | ||
|
||
child = fork(); | ||
if (child == -1) { | ||
ksft_print_msg("FAIL: child process creation\n"); | ||
return KSFT_FAIL; | ||
} else if (child == 0) { | ||
mte_initialize_current_context(mode, (uintptr_t)ptr, size); | ||
/* Do copy on write */ | ||
memset(ptr, '1', size); | ||
mte_wait_after_trig(); | ||
if (cur_mte_cxt.fault_valid == true) { | ||
fault = 1; | ||
goto check_child_tag_inheritance_err; | ||
} | ||
for (i = 0; i < size; i += MT_GRANULE_SIZE) { | ||
child_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i))); | ||
if (parent_tag != child_tag) { | ||
ksft_print_msg("FAIL: child mte tag (%d) mismatch\n", i); | ||
fault = 1; | ||
goto check_child_tag_inheritance_err; | ||
} | ||
} | ||
check_child_tag_inheritance_err: | ||
_exit(fault); | ||
} | ||
/* Wait for child process to terminate */ | ||
wait(&child_status); | ||
if (WIFEXITED(child_status)) | ||
fault = WEXITSTATUS(child_status); | ||
else | ||
fault = 1; | ||
return (fault) ? KSFT_FAIL : KSFT_PASS; | ||
} | ||
|
||
static int check_mte_memory(char *ptr, int size, int mode, int tag_check) | ||
{ | ||
mte_initialize_current_context(mode, (uintptr_t)ptr, size); | ||
memset(ptr, '1', size); | ||
mte_wait_after_trig(); | ||
if (cur_mte_cxt.fault_valid == true) | ||
return KSFT_FAIL; | ||
|
||
return KSFT_PASS; | ||
} | ||
|
||
static int check_hugetlb_memory_mapping(int mem_type, int mode, int mapping, int tag_check) | ||
{ | ||
char *ptr, *map_ptr; | ||
int result; | ||
unsigned long map_size; | ||
|
||
map_size = default_huge_page_size(); | ||
|
||
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); | ||
map_ptr = (char *)mte_allocate_memory(map_size, mem_type, mapping, false); | ||
if (check_allocated_memory(map_ptr, map_size, mem_type, false) != KSFT_PASS) | ||
return KSFT_FAIL; | ||
|
||
mte_initialize_current_context(mode, (uintptr_t)map_ptr, map_size); | ||
/* Only mte enabled memory will allow tag insertion */ | ||
ptr = mte_insert_tags((void *)map_ptr, map_size); | ||
if (!ptr || cur_mte_cxt.fault_valid == true) { | ||
ksft_print_msg("FAIL: Insert tags on anonymous mmap memory\n"); | ||
munmap((void *)map_ptr, map_size); | ||
return KSFT_FAIL; | ||
} | ||
result = check_mte_memory(ptr, map_size, mode, tag_check); | ||
mte_clear_tags((void *)ptr, map_size); | ||
mte_free_memory((void *)map_ptr, map_size, mem_type, false); | ||
if (result == KSFT_FAIL) | ||
return KSFT_FAIL; | ||
|
||
return KSFT_PASS; | ||
} | ||
|
||
static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping) | ||
{ | ||
char *map_ptr; | ||
int prot_flag, result; | ||
unsigned long map_size; | ||
|
||
prot_flag = PROT_READ | PROT_WRITE; | ||
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); | ||
map_size = default_huge_page_size(); | ||
map_ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping, | ||
0, 0); | ||
if (check_allocated_memory_range(map_ptr, map_size, mem_type, | ||
0, 0) != KSFT_PASS) | ||
return KSFT_FAIL; | ||
/* Try to clear PROT_MTE property and verify it by tag checking */ | ||
if (mprotect(map_ptr, map_size, prot_flag)) { | ||
mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type, | ||
0, 0); | ||
ksft_print_msg("FAIL: mprotect not ignoring clear PROT_MTE property\n"); | ||
return KSFT_FAIL; | ||
} | ||
result = check_mte_memory(map_ptr, map_size, mode, TAG_CHECK_ON); | ||
mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type, 0, 0); | ||
if (result != KSFT_PASS) | ||
return KSFT_FAIL; | ||
|
||
return KSFT_PASS; | ||
} | ||
|
||
static int check_child_hugetlb_memory_mapping(int mem_type, int mode, int mapping) | ||
{ | ||
char *ptr; | ||
int result; | ||
unsigned long map_size; | ||
|
||
map_size = default_huge_page_size(); | ||
|
||
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); | ||
ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping, | ||
0, 0); | ||
if (check_allocated_memory_range(ptr, map_size, mem_type, | ||
0, 0) != KSFT_PASS) | ||
return KSFT_FAIL; | ||
result = check_child_tag_inheritance(ptr, map_size, mode); | ||
mte_free_memory_tag_range((void *)ptr, map_size, mem_type, 0, 0); | ||
if (result == KSFT_FAIL) | ||
return result; | ||
|
||
return KSFT_PASS; | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
int err; | ||
|
||
err = mte_default_setup(); | ||
if (err) | ||
return err; | ||
|
||
/* Register signal handlers */ | ||
mte_register_signal(SIGBUS, mte_default_handler); | ||
mte_register_signal(SIGSEGV, mte_default_handler); | ||
|
||
allocate_hugetlb(); | ||
|
||
if (!is_hugetlb_allocated()) { | ||
ksft_print_msg("ERR: Unable allocate hugetlb pages\n"); | ||
return KSFT_FAIL; | ||
} | ||
|
||
/* Set test plan */ | ||
ksft_set_plan(12); | ||
|
||
mte_enable_pstate_tco(); | ||
|
||
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF), | ||
"Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check off\n"); | ||
|
||
mte_disable_pstate_tco(); | ||
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_NONE_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF), | ||
"Check hugetlb memory with private mapping, no error mode, mmap memory and tag check off\n"); | ||
|
||
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), | ||
"Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check on\n"); | ||
evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), | ||
"Check hugetlb memory with private mapping, sync error mode, mmap/mprotect memory and tag check on\n"); | ||
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), | ||
"Check hugetlb memory with private mapping, async error mode, mmap memory and tag check on\n"); | ||
evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), | ||
"Check hugetlb memory with private mapping, async error mode, mmap/mprotect memory and tag check on\n"); | ||
|
||
evaluate_test(check_clear_prot_mte_flag(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), | ||
"Check clear PROT_MTE flags with private mapping, sync error mode and mmap memory\n"); | ||
evaluate_test(check_clear_prot_mte_flag(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), | ||
"Check clear PROT_MTE flags with private mapping and sync error mode and mmap/mprotect memory\n"); | ||
|
||
evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), | ||
"Check child hugetlb memory with private mapping, precise mode and mmap memory\n"); | ||
evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), | ||
"Check child hugetlb memory with private mapping, precise mode and mmap memory\n"); | ||
evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), | ||
"Check child hugetlb memory with private mapping, precise mode and mmap/mprotect memory\n"); | ||
evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), | ||
"Check child hugetlb memory with private mapping, precise mode and mmap/mprotect memory\n"); | ||
|
||
mte_restore_setup(); | ||
free_hugetlb(); | ||
ksft_print_cnts(); | ||
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL; | ||
} |