/* * Copyright (c) 2011-2014 Wind River Systems, Inc. * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include /* Ref to _x86_mmu_buffer_validate documentation for details */ #define USER_PERM_BIT_POS ((u32_t)0x1) #define GET_RW_PERM(flags) (flags & BUFF_WRITEABLE) #define GET_US_PERM(flags) ((flags & BUFF_USER) >> USER_PERM_BIT_POS) /* Common regions for all x86 processors. * Peripheral I/O ranges configured at the SOC level */ /* Mark text and rodata as read-only. * Userspace may read all text and rodata. */ MMU_BOOT_REGION((u32_t)&_image_rom_start, (u32_t)&_image_rom_size, MMU_ENTRY_READ | MMU_ENTRY_USER); #ifdef CONFIG_APPLICATION_MEMORY /* User threads by default can read/write app-level memory. */ MMU_BOOT_REGION((u32_t)&__app_ram_start, (u32_t)&__app_ram_size, MMU_ENTRY_WRITE | MMU_ENTRY_USER); #endif /* __kernel_ram_size includes all unused memory, which is used for heaps. * User threads cannot access this unless granted at runtime. This is done * automatically for stacks. */ MMU_BOOT_REGION((u32_t)&__kernel_ram_start, (u32_t)&__kernel_ram_size, MMU_ENTRY_WRITE | MMU_ENTRY_RUNTIME_USER); /** * brief check page directory flags * * This routine reads the flags of the pde and then compares it to the * rw_permission and us_permissions. * * return true-if the permissions of the pde matches the request */ static inline u32_t check_pde_flags(volatile union x86_mmu_pde_pt pde, u32_t rw_permission, u32_t us_permission) { /* If rw bit is 0 then read is enabled else if 1 then * read-write access is enabled. (flags & 0x1) returns a * RW permission required. if READ is requested and the rw bit says * it has write permission this passes through. But if a write * permission is requested and rw bit says only read is * allowed then this fails. */ /* The privilage permisions possible combinations between request and * the the mmu configuraiont are * s s -> both supervisor = valid * s u -> PDE is supervisor and requested is user = invalid * u s -> PDE is user and requested is supervisor = valid * u u -> both user = valid */ return(pde.p && (pde.rw >= rw_permission) && !(pde.us < us_permission)); } /** * brief check page table entry flags * * This routine reads the flags of the pte and then compares it to the * rw_permission and us_permissions. * * return true-if the permissions of the pde matches the request */ static inline u32_t check_pte_flags(union x86_mmu_pte pte, u32_t rw_permission, u32_t us_permission) { /* Ref to check_pde_flag for doc */ return(pte.p && (pte.rw >= rw_permission) && !(pte.us < us_permission)); } void _x86_mmu_get_flags(void *addr, u32_t *pde_flags, u32_t *pte_flags) { int pde_index, pte_index; union x86_mmu_pde *pde; union x86_mmu_pte *pte; struct x86_mmu_page_table *pt; pde_index = MMU_PDE_NUM(addr); pte_index = MMU_PAGE_NUM(addr); pde = &X86_MMU_PD->entry[pde_index]; pt = (struct x86_mmu_page_table *)(pde->pt.page_table << 12); pte = &pt->entry[pte_index]; *pde_flags = pde->pt.value & ~MMU_PDE_PAGE_TABLE_MASK; *pte_flags = pte->value & ~MMU_PTE_PAGE_MASK; } /** * @brief check page table entry flags * * This routine checks if the buffer is avaialable to the whoever calls * this API. * @a addr start address of the buffer * @a size the size of the buffer * @a flags permisions to check. * Consists of 2 bits the bit0 represents the RW permissions * The bit1 represents the user/supervisor permissions * Use macro BUFF_READABLE/BUFF_WRITEABLE or BUFF_USER to build the flags * * @return true-if the permissions of the pde matches the request */ int _x86_mmu_buffer_validate(void *addr, size_t size, int flags) { int status = 0; u32_t start_pde_num; u32_t end_pde_num; /* union x86_mmu_pde_pt pde_status; */ volatile u32_t validity = 1; u32_t starting_pte_num; u32_t ending_pte_num; struct x86_mmu_page_table *pte_address; u32_t rw_permission; u32_t us_permission; u32_t pde; u32_t pte; union x86_mmu_pte pte_value; start_pde_num = MMU_PDE_NUM(addr); end_pde_num = MMU_PDE_NUM((char *)addr + size - 1); rw_permission = GET_RW_PERM(flags); us_permission = GET_US_PERM(flags); starting_pte_num = MMU_PAGE_NUM((char *)addr + size - 1); /* Iterate for all the pde's the buffer might take up. * (depends on the size of the buffer and start address of the buff) */ for (pde = start_pde_num; pde <= end_pde_num; pde++) { validity &= check_pde_flags(X86_MMU_PD->entry[pde].pt, rw_permission, us_permission); /* If pde is invalid exit immediately. */ if (validity == 0) { break; } /* Get address of the page table from the pde. * This is a 20 bit address, so shift it by 12. * This gives us 4KB aligned page table. */ pte_address = (struct x86_mmu_page_table *) (X86_MMU_PD->entry[pde].pt.page_table << 12); /* loop over all the possible page tables for the required * size. If the pde is not the last one then the last pte * would be 1023. So each pde will be using all the * page table entires except for the last pde. * For the last pde, pte is calculated using the last * memory address of the buffer. */ if (pde != end_pde_num) { ending_pte_num = 1023; } else { ending_pte_num = MMU_PAGE_NUM((char *)addr + size - 1); } /* For all the pde's appart from the starting pde, will have * the start pte number as zero. */ if (pde != start_pde_num) { starting_pte_num = 0; } pte_value.value = 0xFFFFFFFF; /* Bitwise AND all the pte values. */ for (pte = starting_pte_num; pte <= ending_pte_num; pte++) { pte_value.value &= pte_address->entry[pte].value; } validity &= check_pte_flags(pte_value, rw_permission, us_permission); } if (validity == 0) { status = -EPERM; } return status; } static inline void tlb_flush_page(void *addr) { /* Invalidate TLB entries corresponding to the page containing the * specified address */ char *page = (char *)addr; __asm__ ("invlpg %0" :: "m" (*page)); } void _x86_mmu_set_flags(void *ptr, size_t size, u32_t flags, u32_t mask) { int pde_index, pte_index; union x86_mmu_pde *pde; union x86_mmu_pte *pte; struct x86_mmu_page_table *pt; u32_t addr = (u32_t)ptr; __ASSERT(!(addr & MMU_PAGE_MASK), "unaligned address provided"); __ASSERT(!(size & MMU_PAGE_MASK), "unaligned size provided"); while (size) { pde_index = MMU_PDE_NUM(addr); pde = &X86_MMU_PD->entry[pde_index]; /* TODO we're not generating 4MB entries at the moment */ __ASSERT(pde->fourmb.ps != 1, "4MB PDE found"); pt = (struct x86_mmu_page_table *)(pde->pt.page_table << 12); pte_index = MMU_PAGE_NUM(addr); pte = &pt->entry[pte_index]; pte->value = (pte->value & ~mask) | flags; tlb_flush_page((void *)addr); size -= MMU_PAGE_SIZE; addr += MMU_PAGE_SIZE; } }