| /* |
| * Copyright (c) 2020 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <ztest.h> |
| #include <sys/mem_manage.h> |
| #include <toolchain.h> |
| |
| /* 32-bit IA32 page tables have no mechanism to restrict execution */ |
| #if defined(CONFIG_X86) && !defined(CONFIG_X86_64) && !defined(CONFIG_X86_PAE) |
| #define SKIP_EXECUTE_TESTS |
| #endif |
| |
| #define BASE_FLAGS (K_MEM_CACHE_WB) |
| volatile bool expect_fault; |
| |
| static uint8_t __aligned(CONFIG_MMU_PAGE_SIZE) |
| test_page[2 * CONFIG_MMU_PAGE_SIZE]; |
| |
| void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *pEsf) |
| { |
| printk("Caught system error -- reason %d\n", reason); |
| |
| if (expect_fault && reason == 0) { |
| expect_fault = false; |
| ztest_test_pass(); |
| } else { |
| printk("Unexpected fault during test"); |
| k_fatal_halt(reason); |
| } |
| } |
| |
| |
| /* z_phys_map() doesn't have alignment requirements, any oddly-sized buffer |
| * can get mapped. This will span two pages. |
| */ |
| #define BUF_SIZE 5003 |
| #define BUF_OFFSET 1238 |
| |
| /** |
| * Show that mapping an irregular size buffer works and RW flag is respected |
| * |
| * @ingroup kernel_memprotect_tests |
| */ |
| void test_z_phys_map_rw(void) |
| { |
| uint8_t *mapped_rw, *mapped_ro; |
| uint8_t *buf = test_page + BUF_OFFSET; |
| |
| expect_fault = false; |
| |
| /* Map in a page that allows writes */ |
| z_phys_map(&mapped_rw, (uintptr_t)buf, |
| BUF_SIZE, BASE_FLAGS | K_MEM_PERM_RW); |
| |
| /* Initialize buf with some bytes */ |
| for (int i = 0; i < BUF_SIZE; i++) { |
| mapped_rw[i] = (uint8_t)(i % 256); |
| } |
| |
| /* Map again this time only allowing reads */ |
| z_phys_map(&mapped_ro, (uintptr_t)buf, |
| BUF_SIZE, BASE_FLAGS); |
| |
| /* Check that the mapped area contains the expected data. */ |
| for (int i = 0; i < BUF_SIZE; i++) { |
| zassert_equal(buf[i], mapped_ro[i], |
| "unequal byte at index %d", i); |
| } |
| |
| /* This should explode since writes are forbidden */ |
| expect_fault = true; |
| mapped_ro[0] = 42; |
| |
| printk("shouldn't get here\n"); |
| ztest_test_fail(); |
| } |
| |
| #ifndef SKIP_EXECUTE_TESTS |
| extern char __test_mem_map_start[]; |
| extern char __test_mem_map_end[]; |
| extern char __test_mem_map_size[]; |
| |
| __in_section_unique(test_mem_map) __used |
| static void transplanted_function(bool *executed) |
| { |
| *executed = true; |
| } |
| |
| /** |
| * Show that mapping with/withour K_MEM_PERM_EXEC works as expected |
| * |
| * @ingroup kernel_memprotect_tests |
| */ |
| void test_z_phys_map_exec(void) |
| { |
| uint8_t *mapped_exec, *mapped_ro; |
| bool executed = false; |
| void (*func)(bool *executed); |
| |
| expect_fault = false; |
| |
| /* |
| * Need to reference the function or else linker would |
| * garbage collected it. |
| */ |
| func = transplanted_function; |
| |
| /* Now map with execution enabled and try to run the copied fn */ |
| z_phys_map(&mapped_exec, (uintptr_t)__test_mem_map_start, |
| (uintptr_t)__test_mem_map_size, |
| BASE_FLAGS | K_MEM_PERM_EXEC); |
| |
| func = (void (*)(bool *executed))mapped_exec; |
| func(&executed); |
| zassert_true(executed, "function did not execute"); |
| |
| /* Now map without execution and execution should now fail */ |
| z_phys_map(&mapped_ro, (uintptr_t)__test_mem_map_start, |
| (uintptr_t)__test_mem_map_size, BASE_FLAGS); |
| |
| func = (void (*)(bool *executed))mapped_ro; |
| expect_fault = true; |
| func(&executed); |
| |
| printk("shouldn't get here\n"); |
| ztest_test_fail(); |
| } |
| #else |
| void test_z_phys_map_exec(void) |
| { |
| ztest_test_skip(); |
| } |
| #endif /* SKIP_EXECUTE_TESTS */ |
| |
| /** |
| * Show that memory mapping doesn't have unintended side effects |
| * |
| * @ingroup kernel_memprotect_tests |
| */ |
| void test_z_phys_map_side_effect(void) |
| { |
| uint8_t *mapped; |
| |
| expect_fault = false; |
| |
| /* z_phys_map() is supposed to always create fresh mappings. |
| * Show that by mapping test_page to an RO region, we can still |
| * modify test_page. |
| */ |
| z_phys_map(&mapped, (uintptr_t)test_page, |
| sizeof(test_page), BASE_FLAGS); |
| |
| /* Should NOT fault */ |
| test_page[0] = 42; |
| |
| /* Should fault */ |
| expect_fault = true; |
| mapped[0] = 42; |
| printk("shouldn't get here\n"); |
| ztest_test_fail(); |
| } |
| |
| /** |
| * Basic k_mem_map() functionality |
| * |
| * Does not exercise K_MEM_MAP_* control flags, just default behavior |
| */ |
| void test_k_mem_map(void) |
| { |
| size_t free_mem, free_mem_after_map; |
| char *mapped; |
| int i; |
| |
| free_mem = k_mem_free_get(); |
| zassert_not_equal(free_mem, 0, "no free memory"); |
| printk("Free memory: %zu\n", free_mem); |
| |
| mapped = k_mem_map(CONFIG_MMU_PAGE_SIZE, K_MEM_PERM_RW); |
| zassert_not_null(mapped, "failed to map memory"); |
| printk("mapped a page to %p\n", mapped); |
| |
| /* Page should be zeroed */ |
| for (i = 0; i < CONFIG_MMU_PAGE_SIZE; i++) { |
| zassert_equal(mapped[i], '\x00', "page not zeroed"); |
| } |
| |
| free_mem_after_map = k_mem_free_get(); |
| printk("Free memory now: %zu\n", free_mem_after_map); |
| zassert_equal(free_mem, free_mem_after_map + CONFIG_MMU_PAGE_SIZE, |
| "incorrect free memory accounting"); |
| |
| /* Show we can write to page without exploding */ |
| (void)memset(mapped, '\xFF', CONFIG_MMU_PAGE_SIZE); |
| for (i = 0; i < CONFIG_MMU_PAGE_SIZE; i++) { |
| zassert_true(mapped[i] == '\xFF', |
| "incorrect value 0x%hhx read at index %d", |
| mapped[i], i); |
| } |
| |
| /* TODO: Un-map the mapped page to clean up, once we have that |
| * capability |
| */ |
| } |
| |
| /* ztest main entry*/ |
| void test_main(void) |
| { |
| #ifdef CONFIG_DEMAND_PAGING |
| /* This test sets up multiple mappings of RAM pages, which is only |
| * allowed for pinned memory |
| */ |
| k_mem_pin(test_page, sizeof(test_page)); |
| #endif |
| ztest_test_suite(test_mem_map, |
| ztest_unit_test(test_z_phys_map_rw), |
| ztest_unit_test(test_z_phys_map_exec), |
| ztest_unit_test(test_z_phys_map_side_effect), |
| ztest_unit_test(test_k_mem_map) |
| ); |
| ztest_run_test_suite(test_mem_map); |
| } |