diff --git a/demos/m1_memmap.c b/demos/m1_memmap.c new file mode 100644 index 0000000..da514e4 --- /dev/null +++ b/demos/m1_memmap.c @@ -0,0 +1,82 @@ +#include + +#include "../ptedit_header.h" + +int is_present(size_t entry) { + return (entry & 3) == 3; +} + +void dump(int do_dump, size_t entry, char *type) { + if (do_dump) { + for (int i = 0; i < 4; i++) { + printf("%s", type); + ptedit_print_entry_line(entry, i); + } + } +} + +int main(int argc, char *argv[]) { + if (ptedit_init()) { + printf("Error: Could not initalize PTEditor, did you load the kernel module?\n"); + return 1; + } + + int dump_entry = 1; + size_t pid = 0; + if (argc >= 2) { + pid = atoi(argv[1]); + } + + printf("Dumping PID %zd\n", pid); + + size_t root = ptedit_get_paging_root(pid); + size_t pagesize = ptedit_get_pagesize(); + size_t pml4[pagesize / sizeof(size_t)], pdpt[pagesize / sizeof(size_t)], + pd[pagesize / sizeof(size_t)], pt[pagesize / sizeof(size_t)]; + + ptedit_read_physical_page((root / pagesize)*4, (char *)pml4); + + int pml4i, pdpti, pdi, pti; + size_t mem_usage = 0; + + /* Iterate through PML4 entries */ + for (pml4i = 0; pml4i < 2048; pml4i++) { + size_t pml4_entry = pml4[pml4i]; + if (!is_present(pml4_entry)) + continue; + dump(dump_entry, pml4_entry, ""); + + /* Iterate through PDPT entries */ + ptedit_read_physical_page(ptedit_get_pfn(pml4_entry), (char *)pdpt); + for (pdpti = 0; pdpti < 2048; pdpti++) { + size_t pdpt_entry = pdpt[pdpti]; + if (!is_present(pdpt_entry)) + continue; + dump(dump_entry, pdpt_entry, "PDPT"); + + /* Iterate through PD entries */ + ptedit_read_physical_page(ptedit_get_pfn(pdpt_entry), (char *)pd); + for (pdi = 0; pdi < 2048; pdi++) { + size_t pd_entry = pd[pdi]; + if (!is_present(pd_entry)) + continue; + dump(dump_entry, pd_entry, " PD "); + + /* Iterate through PT entries */ + ptedit_read_physical_page(ptedit_get_pfn(pd_entry), (char *)pt); + for (pti = 0; pti < 2048; pti++) { + size_t pt_entry = pt[pti]; + if (!is_present(pt_entry)) + continue; + dump(dump_entry, pt_entry, " PT "); + // only certain entries are addressable on m1 and it depends on the page directory root + mem_usage += 16384; + } + } + } + } + + printf("Used memory: %zd KB\n", mem_usage / 1024); + + ptedit_cleanup(); +} diff --git a/module/pteditor.c b/module/pteditor.c index e4e7ba1..b5590e4 100644 --- a/module/pteditor.c +++ b/module/pteditor.c @@ -537,16 +537,6 @@ static long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned lon (void)from_user(&paging, (void*)ioctl_param, sizeof(paging)); mm = get_mm(paging.pid); -#if defined(__aarch64__) - if(!mm || (mm && !mm->pgd)) { - // M1 Asahi Linux workaround with the limitation that it only works for the current process - asm volatile("mrs %0, ttbr0_el1" : "=r" (paging.root)); - paging.root &= ~1; - (void)to_user((void*)ioctl_param, &paging, sizeof(paging)); - return 0; - } -#endif - if(!mm) return 1; #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(!mm_is_locked) mmap_read_lock(mm); diff --git a/ptedit.c b/ptedit.c index 73c8e49..9db9d1f 100644 --- a/ptedit.c +++ b/ptedit.c @@ -42,6 +42,7 @@ typedef struct { int has_pgd, has_p4d, has_pud, has_pmd, has_pt; int pgd_entries, p4d_entries, pud_entries, pmd_entries, pt_entries; int page_offset; + int pfn_offset; } ptedit_paging_definition_t; static ptedit_paging_definition_t ptedit_paging_definition; @@ -364,10 +365,10 @@ ptedit_fnc void ptedit_print_entry_line(size_t entry, int line) { } #elif defined(__aarch64__) if (line == 0 || line == 3) { - printf("+--+--+--+---+-+--+------------------+--+-+-+-+--+---+-+\n"); + printf("+--+--+--+---+-+---+--+------------------+--+-+-+-+--+---+-+\n"); } if (line == 1) { - printf("| ?| ?|XN|PXN|C| ?| PFN |NG|A|S|P|NS|MAI|T|\n"); + printf("| ?| ?|XN|PXN|C|DBM| ?| PFN |NG|A|S|P|NS|MAI|T|\n"); } if (line == 2) { printf("|"); @@ -376,7 +377,8 @@ ptedit_fnc void ptedit_print_entry_line(size_t entry, int line) { PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 54)); PEDIT_PRINT_B(" %d ", PTEDIT_B(entry, 53)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 52)); - PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 51) << 3) | (PTEDIT_B(entry, 50) << 2) | (PTEDIT_B(entry, 49) << 1) | PTEDIT_B(entry, 48)); + PEDIT_PRINT_B(" %d ", PTEDIT_B(entry, 51)); + PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 50) << 2) | (PTEDIT_B(entry, 49) << 1) | PTEDIT_B(entry, 48)); printf(" %16p |", (void*)((entry >> 12) & ((1ull << 36) - 1))); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 11)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 10)); @@ -468,6 +470,7 @@ ptedit_fnc int ptedit_init() { ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; + ptedit_paging_definition.pfn_offset = 0; #elif defined(__aarch64__) if(ptedit_get_pagesize() == 16384) { ptedit_paging_definition.has_pgd = 1; @@ -481,7 +484,7 @@ ptedit_fnc int ptedit_init() { ptedit_paging_definition.pmd_entries = 11; ptedit_paging_definition.pt_entries = 11; ptedit_paging_definition.page_offset = 14; - ptedit_use_implementation(PTEDIT_IMPL_USER_PREAD); // M1 workaround + ptedit_paging_definition.pfn_offset = 2; } else { ptedit_paging_definition.has_pgd = 1; ptedit_paging_definition.has_p4d = 0; @@ -494,6 +497,7 @@ ptedit_fnc int ptedit_init() { ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; + ptedit_paging_definition.pfn_offset = 0; } #endif return 0; @@ -565,14 +569,14 @@ ptedit_fnc int ptedit_get_pagesize() { ptedit_fnc void ptedit_read_physical_page(size_t pfn, char* buffer) { #if defined(LINUX) if (ptedit_umem > 0) { - if (pread(ptedit_umem, buffer, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { + if (pread(ptedit_umem, buffer, ptedit_pagesize, (pfn >> ptedit_paging_definition.pfn_offset) * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)buffer; - page.pfn = pfn; + page.pfn = pfn >> ptedit_paging_definition.pfn_offset; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_READ_PAGE, (size_t)&page); } #else @@ -587,14 +591,14 @@ ptedit_fnc void ptedit_read_physical_page(size_t pfn, char* buffer) { ptedit_fnc void ptedit_write_physical_page(size_t pfn, char* content) { #if defined(LINUX) if (ptedit_umem > 0) { - if (pwrite(ptedit_umem, content, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { + if (pwrite(ptedit_umem, content, ptedit_pagesize, (pfn >> ptedit_paging_definition.pfn_offset) * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)content; - page.pfn = pfn; + page.pfn = pfn >> ptedit_paging_definition.pfn_offset; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_WRITE_PAGE, (size_t)&page); } #else diff --git a/ptedit_header.h b/ptedit_header.h index 9642380..10e44ad 100644 --- a/ptedit_header.h +++ b/ptedit_header.h @@ -13,6 +13,8 @@ #undef LINUX #endif +#include + #if defined(LINUX) #define PTEDITOR_DEVICE_NAME "pteditor" @@ -1041,6 +1043,7 @@ typedef struct { int has_pgd, has_p4d, has_pud, has_pmd, has_pt; int pgd_entries, p4d_entries, pud_entries, pmd_entries, pt_entries; int page_offset; + int pfn_offset; } ptedit_paging_definition_t; static ptedit_paging_definition_t ptedit_paging_definition; @@ -1363,10 +1366,10 @@ ptedit_fnc void ptedit_print_entry_line(size_t entry, int line) { } #elif defined(__aarch64__) if (line == 0 || line == 3) { - printf("+--+--+--+---+-+--+------------------+--+-+-+-+--+---+-+\n"); + printf("+--+--+--+---+-+---+--+------------------+--+-+-+-+--+---+-+\n"); } if (line == 1) { - printf("| ?| ?|XN|PXN|C| ?| PFN |NG|A|S|P|NS|MAI|T|\n"); + printf("| ?| ?|XN|PXN|C|DBM| ?| PFN |NG|A|S|P|NS|MAI|T|\n"); } if (line == 2) { printf("|"); @@ -1375,7 +1378,8 @@ ptedit_fnc void ptedit_print_entry_line(size_t entry, int line) { PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 54)); PEDIT_PRINT_B(" %d ", PTEDIT_B(entry, 53)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 52)); - PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 51) << 3) | (PTEDIT_B(entry, 50) << 2) | (PTEDIT_B(entry, 49) << 1) | PTEDIT_B(entry, 48)); + PEDIT_PRINT_B(" %d ", PTEDIT_B(entry, 51)); + PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 50) << 2) | (PTEDIT_B(entry, 49) << 1) | PTEDIT_B(entry, 48)); printf(" %16p |", (void*)((entry >> 12) & ((1ull << 36) - 1))); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 11)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 10)); @@ -1467,6 +1471,7 @@ ptedit_fnc int ptedit_init() { ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; + ptedit_paging_definition.pfn_offset = 0; #elif defined(__aarch64__) if(ptedit_get_pagesize() == 16384) { ptedit_paging_definition.has_pgd = 1; @@ -1480,7 +1485,7 @@ ptedit_fnc int ptedit_init() { ptedit_paging_definition.pmd_entries = 11; ptedit_paging_definition.pt_entries = 11; ptedit_paging_definition.page_offset = 14; - ptedit_use_implementation(PTEDIT_IMPL_USER_PREAD); // M1 workaround + ptedit_paging_definition.pfn_offset = 2; } else { ptedit_paging_definition.has_pgd = 1; ptedit_paging_definition.has_p4d = 0; @@ -1493,6 +1498,7 @@ ptedit_fnc int ptedit_init() { ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; + ptedit_paging_definition.pfn_offset = 0; } #endif return 0; @@ -1564,14 +1570,14 @@ ptedit_fnc int ptedit_get_pagesize() { ptedit_fnc void ptedit_read_physical_page(size_t pfn, char* buffer) { #if defined(LINUX) if (ptedit_umem > 0) { - if (pread(ptedit_umem, buffer, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { + if (pread(ptedit_umem, buffer, ptedit_pagesize, (pfn >> ptedit_paging_definition.pfn_offset) * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)buffer; - page.pfn = pfn; + page.pfn = pfn >> ptedit_paging_definition.pfn_offset; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_READ_PAGE, (size_t)&page); } #else @@ -1586,14 +1592,14 @@ ptedit_fnc void ptedit_read_physical_page(size_t pfn, char* buffer) { ptedit_fnc void ptedit_write_physical_page(size_t pfn, char* content) { #if defined(LINUX) if (ptedit_umem > 0) { - if (pwrite(ptedit_umem, content, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { + if (pwrite(ptedit_umem, content, ptedit_pagesize, (pfn >> ptedit_paging_definition.pfn_offset) * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)content; - page.pfn = pfn; + page.pfn = pfn >> ptedit_paging_definition.pfn_offset; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_WRITE_PAGE, (size_t)&page); } #else diff --git a/test/Makefile b/test/Makefile index 5d77242..0c1f0d8 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,8 +1,11 @@ -all: tests +all: tests m1_tests tests: tests.c utest.h ../ptedit_header.h gcc -Os tests.c -std=gnu99 -o tests -fsanitize=address +m1: tests.c utest.h ../ptedit_header.h + gcc -Os m1_tests.c -std=gnu99 -o m1_tests -fsanitize=address + clean: - rm -f tests + rm -f tests m1_tests diff --git a/test/m1_tests.c b/test/m1_tests.c new file mode 100644 index 0000000..722a54a --- /dev/null +++ b/test/m1_tests.c @@ -0,0 +1,468 @@ +#include "utest.h" +#include "../ptedit_header.h" +#include +#include + +UTEST_STATE(); + +#define PAGE_ALIGN_CHAR char __attribute__((aligned(4*4096))) + +void flush(void *p) { + asm volatile("DC CIVAC, %0" ::"r"(p)); + asm volatile("DSB ISH"); + asm volatile("ISB"); +} + +#ifndef MAP_HUGE_2MB +#if defined(LINUX) +#include +#endif +#ifndef MAP_HUGE_2MB +#define MAP_HUGE_2MB (21 << 26) +#endif +#endif + +PAGE_ALIGN_CHAR page1[4*4096]; +PAGE_ALIGN_CHAR page2[4*4096]; +PAGE_ALIGN_CHAR scratch[4*4096]; +PAGE_ALIGN_CHAR accessor[4*4096]; + +// ========================================================================= +// Helper functions +// ========================================================================= + +#if defined(LINUX) +size_t hrtime() { + struct timespec t1; + clock_gettime(CLOCK_MONOTONIC, &t1); + return t1.tv_sec * 1000 * 1000 * 1000ULL + t1.tv_nsec; +} +#else +size_t hrtime() { + __int64 wintime; + GetSystemTimePreciseAsFileTime((FILETIME*)&wintime); + return wintime; +} +#endif + +typedef void (*access_time_callback_t)(void*); + +size_t access_time_ext(void *ptr, size_t MEASUREMENTS, access_time_callback_t cb) { + size_t start = 0, end = 0, sum = 0; + + for (int i = 0; i < MEASUREMENTS; i++) { + start = hrtime(); + *((volatile size_t*)ptr); + end = hrtime(); + sum += (end - start); + if(cb) cb(ptr); + } + + return (size_t) (10 * sum / MEASUREMENTS); +} + +size_t access_time(void *ptr) { + return access_time_ext(ptr, 1000000, NULL); +} + +int entry_equal(ptedit_entry_t* e1, ptedit_entry_t* e2) { + int diff = 0; + if((e1->valid & PTEDIT_VALID_MASK_PGD) && (e2->valid & PTEDIT_VALID_MASK_PGD)) { + diff |= e1->pgd ^ e2->pgd; + } + if((e1->valid & PTEDIT_VALID_MASK_P4D) && (e2->valid & PTEDIT_VALID_MASK_P4D)) { + diff |= e1->p4d ^ e2->p4d; + } + if((e1->valid & PTEDIT_VALID_MASK_PUD) && (e2->valid & PTEDIT_VALID_MASK_PUD)) { + diff |= e1->pud ^ e2->pud; + } + if((e1->valid & PTEDIT_VALID_MASK_PMD) && (e2->valid & PTEDIT_VALID_MASK_PMD)) { + diff |= e1->pmd ^ e2->pmd; + } + if((e1->valid & PTEDIT_VALID_MASK_PTE) && (e2->valid & PTEDIT_VALID_MASK_PTE)) { + diff |= e1->pte ^ e2->pte; + } + return !diff; +} + +// ========================================================================= +// Resolving addresses +// ========================================================================= + + +UTEST(resolve, resolve_basic) { + ptedit_entry_t vm = ptedit_resolve(page1, 0); + ASSERT_TRUE(vm.pgd); + ASSERT_TRUE(vm.pte); + ASSERT_TRUE(vm.valid & PTEDIT_VALID_MASK_PTE); + ASSERT_TRUE(vm.valid & PTEDIT_VALID_MASK_PGD); +} + +UTEST(resolve, resolve_valid_mask) { + ptedit_entry_t vm = ptedit_resolve(page1, 0); + if(vm.valid & PTEDIT_VALID_MASK_PGD) ASSERT_TRUE(vm.pgd); + if(vm.valid & PTEDIT_VALID_MASK_P4D) ASSERT_TRUE(vm.p4d); + if(vm.valid & PTEDIT_VALID_MASK_PMD) ASSERT_TRUE(vm.pmd); + if(vm.valid & PTEDIT_VALID_MASK_PUD) ASSERT_TRUE(vm.pud); + if(vm.valid & PTEDIT_VALID_MASK_PTE) ASSERT_TRUE(vm.pte); +} + +UTEST(resolve, resolve_deterministic) { + ptedit_entry_t vm1 = ptedit_resolve(page1, 0); + ptedit_entry_t vm2 = ptedit_resolve(page1, 0); + ASSERT_TRUE(entry_equal(&vm1, &vm2)); +} + +UTEST(resolve, resolve_different) { + ptedit_entry_t vm1 = ptedit_resolve(page1, 0); + ptedit_entry_t vm2 = ptedit_resolve(page2, 0); + ASSERT_FALSE(entry_equal(&vm1, &vm2)); +} + +UTEST(resolve, resolve_invalid) { + ptedit_entry_t vm1 = ptedit_resolve(0, 0); + ASSERT_FALSE(vm1.valid & PTEDIT_VALID_MASK_PTE); +} + +UTEST(resolve, resolve_invalid_pid) { + ptedit_entry_t vm1 = ptedit_resolve(page1, -1); + ASSERT_FALSE(vm1.valid); +} + +UTEST(resolve, resolve_page_offset) { + ptedit_entry_t vm1 = ptedit_resolve(page1, 0); + ptedit_entry_t vm2 = ptedit_resolve(page1 + 1, 0); + vm1.vaddr = vm2.vaddr = 0; + ASSERT_TRUE(entry_equal(&vm1, &vm2)); + ptedit_entry_t vm3 = ptedit_resolve(page1 + 1024, 0); + vm1.vaddr = vm3.vaddr = 0; + ASSERT_TRUE(entry_equal(&vm1, &vm3)); + ptedit_entry_t vm4 = ptedit_resolve(page1 + 4095, 0); + vm1.vaddr = vm4.vaddr = 0; + ASSERT_TRUE(entry_equal(&vm1, &vm4)); +} + + +// ========================================================================= +// Updating addresses +// ========================================================================= + +UTEST(update, nop) { + ptedit_entry_t vm1 = ptedit_resolve(scratch, 0); + ASSERT_TRUE(vm1.valid); + size_t valid = vm1.valid; + vm1.valid = 0; + ptedit_update(scratch, 0, &vm1); + vm1.valid = valid; + ptedit_entry_t vm2 = ptedit_resolve(scratch, 0); + ASSERT_TRUE(entry_equal(&vm1, &vm2)); +} + +UTEST(update, pte_nop) { + ptedit_entry_t vm1 = ptedit_resolve(scratch, 0); + ASSERT_TRUE(vm1.valid); + size_t valid = vm1.valid; + vm1.valid = PTEDIT_VALID_MASK_PTE; + ptedit_update(scratch, 0, &vm1); + vm1.valid = valid; + ptedit_entry_t vm2 = ptedit_resolve(scratch, 0); + ASSERT_TRUE(entry_equal(&vm1, &vm2)); +} + +UTEST(update, new_pte) { + ptedit_entry_t vm = ptedit_resolve(scratch, 0); + ptedit_entry_t vm1 = ptedit_resolve(scratch, 0); + ASSERT_TRUE(vm1.valid); + size_t pte = vm1.pte; + vm1.pte = ptedit_set_pfn(vm1.pte, 0x1234); + vm1.valid = PTEDIT_VALID_MASK_PTE; + ptedit_update(scratch, 0, &vm1); + + ptedit_entry_t check = ptedit_resolve(scratch, 0); + ASSERT_NE((size_t)ptedit_cast(check.pte, ptedit_pte_t).pfn, ptedit_get_pfn(pte)); + ASSERT_EQ((size_t)ptedit_cast(check.pte, ptedit_pte_t).pfn, 0x1234); + + vm1.valid = PTEDIT_VALID_MASK_PTE; + vm1.pte = pte; + ptedit_update(scratch, 0, &vm1); + + ptedit_entry_t vm2 = ptedit_resolve(scratch, 0); + ASSERT_TRUE(entry_equal(&vm, &vm2)); +} + +// ========================================================================= +// PTEs +// ========================================================================= + +UTEST(pte, get_pfn) { + ptedit_entry_t vm = ptedit_resolve(page1, 0); + ASSERT_EQ(ptedit_get_pfn(vm.pte), (size_t)ptedit_cast(vm.pte, ptedit_pte_t).pfn); +} + +UTEST(pte, get_pte_pfn) { + ptedit_entry_t vm = ptedit_resolve(page1, 0); + ASSERT_EQ(ptedit_pte_get_pfn(page1, 0), (size_t)ptedit_cast(vm.pte, ptedit_pte_t).pfn); +} + +UTEST(pte, get_pte_pfn_invalid) { + ASSERT_FALSE(ptedit_pte_get_pfn(0, 0)); +} + +UTEST(pte, pte_present) { + ptedit_entry_t vm = ptedit_resolve(page1, 0); + ASSERT_EQ((size_t)ptedit_cast(vm.pte, ptedit_pte_t).present, PTEDIT_PAGE_PRESENT); +} + +UTEST(pte, pte_set_pfn_basic) { + size_t entry = 0; + ASSERT_EQ(entry, ptedit_set_pfn(entry, 0)); + ASSERT_NE(entry, ptedit_set_pfn(entry, 1)); + ASSERT_EQ(entry, ptedit_set_pfn(ptedit_set_pfn(entry, 1234), 0)); + ASSERT_GT(ptedit_set_pfn(entry, 2), ptedit_set_pfn(entry, 1)); + entry = (size_t)-1; + ASSERT_NE(0, ptedit_set_pfn(entry, 0)); +} + +UTEST(pte, pte_set_pfn) { + ASSERT_TRUE(accessor[0] == 2); + size_t accessor_pfn = ptedit_pte_get_pfn(accessor, 0); + ASSERT_TRUE(accessor_pfn); + size_t page1_pfn = ptedit_pte_get_pfn(page1, 0); + ASSERT_TRUE(page1_pfn); + size_t page2_pfn = ptedit_pte_get_pfn(page2, 0); + ASSERT_TRUE(page2_pfn); + ptedit_pte_set_pfn(accessor, 0, page1_pfn); + ASSERT_TRUE(accessor[0] == 0); + ptedit_pte_set_pfn(accessor, 0, page2_pfn); + ASSERT_TRUE(accessor[0] == 1); + ptedit_pte_set_pfn(accessor, 0, accessor_pfn); + ASSERT_TRUE(accessor[0] == 2); +} + + +// ========================================================================= +// Physical Pages +// ========================================================================= + +UTEST(page, read) { + char buffer[4*4096]; + size_t pfn = ptedit_pte_get_pfn(page1, 0); + ASSERT_TRUE(pfn); + ptedit_read_physical_page(pfn, buffer); + ASSERT_TRUE(!memcmp(buffer, page1, sizeof(buffer))); + pfn = ptedit_pte_get_pfn(page2, 0); + ASSERT_TRUE(pfn); + ptedit_read_physical_page(pfn, buffer); + ASSERT_TRUE(!memcmp(buffer, page2, sizeof(buffer))); +} + +UTEST(page, write) { + char buffer[4*4096]; + size_t pfn = ptedit_pte_get_pfn(scratch, 0); + ASSERT_TRUE(pfn); + ptedit_write_physical_page(pfn, page1); + ptedit_read_physical_page(pfn, buffer); + ASSERT_TRUE(!memcmp(page1, buffer, sizeof(buffer))); + ptedit_write_physical_page(pfn, page2); + ptedit_read_physical_page(pfn, buffer); + ASSERT_TRUE(!memcmp(page2, buffer, sizeof(buffer))); +} + +// ========================================================================= +// Paging +// ========================================================================= + +UTEST(paging, get_root) { + size_t root = ptedit_get_paging_root(0); + ASSERT_TRUE(root); +} + +UTEST(paging, get_root_deterministic) { + size_t root = ptedit_get_paging_root(0); + ASSERT_TRUE(root); + size_t root_check = ptedit_get_paging_root(0); + ASSERT_EQ(root, root_check); +} + +UTEST(paging, get_root_invalid_pid) { + size_t root = ptedit_get_paging_root(-1); + ASSERT_FALSE(root); +} + +/*UTEST(paging, root_page_aligned) { + size_t root = ptedit_get_paging_root(0); + ASSERT_TRUE(root); + ASSERT_FALSE(root % ptedit_get_pagesize()); +}*/ + +UTEST(paging, correct_root) { + size_t buffer[4*4096 / sizeof(size_t)]; + size_t root = ptedit_get_paging_root(0); + ptedit_read_physical_page((root / ptedit_get_pagesize())*4, (char*)buffer); + ptedit_entry_t vm = ptedit_resolve(0, 0); + ASSERT_EQ(vm.pgd, buffer[(root % ptedit_get_pagesize()) / sizeof(size_t)]); +} + +// ========================================================================= +// Memory Types +// ========================================================================= + +UTEST(memtype, get) { + ASSERT_TRUE(ptedit_get_mts()); +} + +UTEST(memtype, get_deterministic) { + ASSERT_EQ(ptedit_get_mts(), ptedit_get_mts()); +} + +UTEST(memtype, uncachable) { + ASSERT_NE(ptedit_find_first_mt(PTEDIT_MT_UC), -1); +} + +UTEST(memtype, writeback) { + ASSERT_NE(ptedit_find_first_mt(PTEDIT_MT_WB), -1); +} + +UTEST(memtype, apply) { + size_t entry = 0; + ASSERT_NE(ptedit_apply_mt(entry, 1), entry); + ASSERT_EQ(ptedit_apply_mt(entry, 0), entry); +} + +UTEST(memtype, apply_huge) { + size_t entry = 0; + ASSERT_NE(ptedit_apply_mt_huge(entry, 1), entry); + ASSERT_EQ(ptedit_apply_mt_huge(entry, 0), entry); +} + +UTEST(memtype, extract) { + ASSERT_TRUE(ptedit_extract_mt(ptedit_apply_mt(0, 5)) == 5); + ASSERT_TRUE(ptedit_extract_mt(ptedit_apply_mt((size_t)-1, 2)) == 2); +} + +UTEST(memtype, extract_huge) { + ASSERT_TRUE(ptedit_extract_mt_huge(ptedit_apply_mt_huge(0, 5)) == 5); + ASSERT_TRUE(ptedit_extract_mt_huge(ptedit_apply_mt_huge((size_t)-1, 2)) == 2); +} + +UTEST(memtype, uncachable_access_time) { + if(getenv("TRAVISCI")) { + ASSERT_TRUE(1); + } else { + int uc_mt = ptedit_find_first_mt(PTEDIT_MT_UC); + ASSERT_NE(uc_mt, -1); + int wb_mt = ptedit_find_first_mt(PTEDIT_MT_WB); + ASSERT_NE(wb_mt, -1); + + flush(scratch); + size_t before = access_time(scratch); + + ptedit_entry_t entry = ptedit_resolve(scratch, 0); + size_t pte = entry.pte; + ASSERT_TRUE(entry.valid); + ASSERT_TRUE(entry.pte); + entry.pte = ptedit_apply_mt(entry.pte, uc_mt); + entry.valid = PTEDIT_VALID_MASK_PTE; + ptedit_update(scratch, 0, &entry); + + flush(scratch); + size_t uc = access_time(scratch); + + entry.pte = pte; + entry.valid = PTEDIT_VALID_MASK_PTE; + ptedit_update(scratch, 0, &entry); + + size_t after = access_time(scratch); + + ASSERT_LT(after + 5, uc); + ASSERT_LT(before + 5, uc); + } +} + +UTEST(memtype, uncachable_huge_page_access_time) { + if(getenv("TRAVISCI")) { + ASSERT_TRUE(1); + } else { + char* huge_page = mmap(0, (2*1024*1024), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS|MAP_POPULATE|MAP_HUGETLB|MAP_HUGE_2MB, -1, 0); + if (huge_page != MAP_FAILED) { + int uc_mt = ptedit_find_first_mt(PTEDIT_MT_UC); + ASSERT_NE(uc_mt, -1); + int wb_mt = ptedit_find_first_mt(PTEDIT_MT_WB); + ASSERT_NE(wb_mt, -1); + + flush(huge_page); + size_t before = access_time(huge_page); + + ptedit_entry_t entry = ptedit_resolve(huge_page, 0); + size_t pmd = entry.pmd; + ASSERT_TRUE(entry.valid); + ASSERT_TRUE(entry.pmd); + + entry.pmd = ptedit_apply_mt_huge(entry.pmd, uc_mt); + entry.valid = PTEDIT_VALID_MASK_PMD; + ptedit_update(huge_page, 0, &entry); + + flush(huge_page); + size_t uc = access_time(huge_page); + + entry.pmd = pmd; + entry.valid = PTEDIT_VALID_MASK_PMD; + ptedit_update(huge_page, 0, &entry); + + size_t after = access_time(huge_page); + + munmap(huge_page, (2*1024*1024)); + + ASSERT_LT(after + 5, uc); + ASSERT_LT(before + 5, uc); + } else { + fprintf(stdout, "Note: Could not allocate huge page.\n"); + } + } +} + +// ========================================================================= +// TLB +// ========================================================================= + +UTEST(tlb, invalid_tlb_invalidate_method) { + int ret = ptedit_switch_tlb_invalidation(3); + ASSERT_TRUE(ret); +} + +UTEST(tlb, valid_tlb_invalidate_method) { + int ret = ptedit_switch_tlb_invalidation(PTEDITOR_TLB_INVALIDATION_KERNEL); + ASSERT_FALSE(ret); +} + +UTEST(tlb, access_time_kernel_tlb_flush) { + ptedit_switch_tlb_invalidation(PTEDITOR_TLB_INVALIDATION_KERNEL); + int flushed = access_time_ext(scratch, 100, ptedit_invalidate_tlb); + int normal = access_time_ext(scratch, 100, NULL); + ASSERT_GT(flushed, normal); +} + +UTEST(tlb, access_time_custom_tlb_flush) { + ptedit_switch_tlb_invalidation(PTEDITOR_TLB_INVALIDATION_CUSTOM); + int flushed = access_time_ext(scratch, 100, ptedit_invalidate_tlb); + int normal = access_time_ext(scratch, 100, NULL); + ASSERT_GT(flushed, normal); +} + +int main(int argc, const char *const argv[]) { + if(ptedit_init()) { + printf("Could not initialize PTEditor, did you load the kernel module?\n"); + return 1; + } + memset(scratch, 0, sizeof(scratch)); + memset(page1, 0, sizeof(page1)); + memset(page2, 1, sizeof(page2)); + memset(accessor, 2, sizeof(accessor)); + +// ptedit_use_implementation(PTEDIT_IMPL_USER_PREAD); + + int result = utest_main(argc, argv); + + ptedit_cleanup(); + return result; +}