162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright IBM Corp. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 562306a36Sopenharmony_ci * under the terms of version 2.1 of the GNU Lesser General Public License 662306a36Sopenharmony_ci * as published by the Free Software Foundation. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This program is distributed in the hope that it would be useful, but 962306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1062306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <assert.h> 1562306a36Sopenharmony_ci#include <errno.h> 1662306a36Sopenharmony_ci#include <fcntl.h> 1762306a36Sopenharmony_ci#include <signal.h> 1862306a36Sopenharmony_ci#include <stdarg.h> 1962306a36Sopenharmony_ci#include <stdio.h> 2062306a36Sopenharmony_ci#include <stdlib.h> 2162306a36Sopenharmony_ci#include <string.h> 2262306a36Sopenharmony_ci#include <sys/mman.h> 2362306a36Sopenharmony_ci#include <sys/ptrace.h> 2462306a36Sopenharmony_ci#include <sys/syscall.h> 2562306a36Sopenharmony_ci#include <ucontext.h> 2662306a36Sopenharmony_ci#include <unistd.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "utils.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cichar *file_name; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciint in_test; 3362306a36Sopenharmony_civolatile int faulted; 3462306a36Sopenharmony_civolatile void *dar; 3562306a36Sopenharmony_ciint errors; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void segv(int signum, siginfo_t *info, void *ctxt_v) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci ucontext_t *ctxt = (ucontext_t *)ctxt_v; 4062306a36Sopenharmony_ci struct pt_regs *regs = ctxt->uc_mcontext.regs; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (!in_test) { 4362306a36Sopenharmony_ci fprintf(stderr, "Segfault outside of test !\n"); 4462306a36Sopenharmony_ci exit(1); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci faulted = 1; 4862306a36Sopenharmony_ci dar = (void *)regs->dar; 4962306a36Sopenharmony_ci regs->nip += 4; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline void do_read(const volatile void *addr) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci int ret; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci asm volatile("lwz %0,0(%1); twi 0,%0,0; isync;\n" 5762306a36Sopenharmony_ci : "=r" (ret) : "r" (addr) : "memory"); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline void do_write(const volatile void *addr) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci int val = 0x1234567; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci asm volatile("stw %0,0(%1); sync; \n" 6562306a36Sopenharmony_ci : : "r" (val), "r" (addr) : "memory"); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic inline void check_faulted(void *addr, long page, long subpage, int write) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int want_fault = (subpage == ((page + 3) % 16)); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (write) 7362306a36Sopenharmony_ci want_fault |= (subpage == ((page + 1) % 16)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (faulted != want_fault) { 7662306a36Sopenharmony_ci printf("Failed at %p (p=%ld,sp=%ld,w=%d), want=%s, got=%s !\n", 7762306a36Sopenharmony_ci addr, page, subpage, write, 7862306a36Sopenharmony_ci want_fault ? "fault" : "pass", 7962306a36Sopenharmony_ci faulted ? "fault" : "pass"); 8062306a36Sopenharmony_ci ++errors; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (faulted) { 8462306a36Sopenharmony_ci if (dar != addr) { 8562306a36Sopenharmony_ci printf("Fault expected at %p and happened at %p !\n", 8662306a36Sopenharmony_ci addr, dar); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci faulted = 0; 8962306a36Sopenharmony_ci asm volatile("sync" : : : "memory"); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int run_test(void *addr, unsigned long size) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned int *map; 9662306a36Sopenharmony_ci long i, j, pages, err; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci pages = size / 0x10000; 9962306a36Sopenharmony_ci map = malloc(pages * 4); 10062306a36Sopenharmony_ci assert(map); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * for each page, mark subpage i % 16 read only and subpage 10462306a36Sopenharmony_ci * (i + 3) % 16 inaccessible 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci for (i = 0; i < pages; i++) { 10762306a36Sopenharmony_ci map[i] = (0x40000000 >> (((i + 1) * 2) % 32)) | 10862306a36Sopenharmony_ci (0xc0000000 >> (((i + 3) * 2) % 32)); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci err = syscall(__NR_subpage_prot, addr, size, map); 11262306a36Sopenharmony_ci if (err) { 11362306a36Sopenharmony_ci perror("subpage_perm"); 11462306a36Sopenharmony_ci return 1; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci free(map); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci in_test = 1; 11962306a36Sopenharmony_ci errors = 0; 12062306a36Sopenharmony_ci for (i = 0; i < pages; i++) { 12162306a36Sopenharmony_ci for (j = 0; j < 16; j++, addr += 0x1000) { 12262306a36Sopenharmony_ci do_read(addr); 12362306a36Sopenharmony_ci check_faulted(addr, i, j, 0); 12462306a36Sopenharmony_ci do_write(addr); 12562306a36Sopenharmony_ci check_faulted(addr, i, j, 1); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci in_test = 0; 13062306a36Sopenharmony_ci if (errors) { 13162306a36Sopenharmony_ci printf("%d errors detected\n", errors); 13262306a36Sopenharmony_ci return 1; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int syscall_available(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int rc; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci errno = 0; 14362306a36Sopenharmony_ci rc = syscall(__NR_subpage_prot, 0, 0, 0); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return rc == 0 || (errno != ENOENT && errno != ENOSYS); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint test_anon(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci unsigned long align; 15162306a36Sopenharmony_ci struct sigaction act = { 15262306a36Sopenharmony_ci .sa_sigaction = segv, 15362306a36Sopenharmony_ci .sa_flags = SA_SIGINFO 15462306a36Sopenharmony_ci }; 15562306a36Sopenharmony_ci void *mallocblock; 15662306a36Sopenharmony_ci unsigned long mallocsize; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci SKIP_IF(!syscall_available()); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (getpagesize() != 0x10000) { 16162306a36Sopenharmony_ci fprintf(stderr, "Kernel page size must be 64K!\n"); 16262306a36Sopenharmony_ci return 1; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci sigaction(SIGSEGV, &act, NULL); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci mallocsize = 4 * 16 * 1024 * 1024; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci FAIL_IF(posix_memalign(&mallocblock, 64 * 1024, mallocsize)); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci align = (unsigned long)mallocblock; 17262306a36Sopenharmony_ci if (align & 0xffff) 17362306a36Sopenharmony_ci align = (align | 0xffff) + 1; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci mallocblock = (void *)align; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci printf("allocated malloc block of 0x%lx bytes at %p\n", 17862306a36Sopenharmony_ci mallocsize, mallocblock); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci printf("testing malloc block...\n"); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return run_test(mallocblock, mallocsize); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciint test_file(void) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct sigaction act = { 18862306a36Sopenharmony_ci .sa_sigaction = segv, 18962306a36Sopenharmony_ci .sa_flags = SA_SIGINFO 19062306a36Sopenharmony_ci }; 19162306a36Sopenharmony_ci void *fileblock; 19262306a36Sopenharmony_ci off_t filesize; 19362306a36Sopenharmony_ci int fd; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci SKIP_IF(!syscall_available()); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci fd = open(file_name, O_RDWR); 19862306a36Sopenharmony_ci if (fd == -1) { 19962306a36Sopenharmony_ci perror("failed to open file"); 20062306a36Sopenharmony_ci return 1; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci sigaction(SIGSEGV, &act, NULL); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci filesize = lseek(fd, 0, SEEK_END); 20562306a36Sopenharmony_ci if (filesize & 0xffff) 20662306a36Sopenharmony_ci filesize &= ~0xfffful; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci fileblock = mmap(NULL, filesize, PROT_READ | PROT_WRITE, 20962306a36Sopenharmony_ci MAP_SHARED, fd, 0); 21062306a36Sopenharmony_ci if (fileblock == MAP_FAILED) { 21162306a36Sopenharmony_ci perror("failed to map file"); 21262306a36Sopenharmony_ci return 1; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci printf("allocated %s for 0x%lx bytes at %p\n", 21562306a36Sopenharmony_ci file_name, filesize, fileblock); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci printf("testing file map...\n"); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return run_test(fileblock, filesize); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciint main(int argc, char *argv[]) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci int rc; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci rc = test_harness(test_anon, "subpage_prot_anon"); 22762306a36Sopenharmony_ci if (rc) 22862306a36Sopenharmony_ci return rc; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (argc > 1) 23162306a36Sopenharmony_ci file_name = argv[1]; 23262306a36Sopenharmony_ci else 23362306a36Sopenharmony_ci file_name = "tempfile"; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return test_harness(test_file, "subpage_prot_file"); 23662306a36Sopenharmony_ci} 237