blob: 437b441bcd5c761e765b0a0fc41f95778de03613 [file] [log] [blame]
/*
* Copyright (c) 2011-2014 Wind River Systems, Inc.
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <mmustructs.h>
#include <linker/linker-defs.h>
/* 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 | MMU_ENTRY_EXECUTE_DISABLE);
#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 |
MMU_ENTRY_EXECUTE_DISABLE);
void _x86_mmu_get_flags(void *addr,
x86_page_entry_data_t *pde_flags,
x86_page_entry_data_t *pte_flags)
{
*pde_flags = (x86_page_entry_data_t)(X86_MMU_GET_PDE(addr)->value &
~(x86_page_entry_data_t)MMU_PDE_PAGE_TABLE_MASK);
if (*pde_flags & MMU_ENTRY_PRESENT) {
*pte_flags = (x86_page_entry_data_t)
(X86_MMU_GET_PTE(addr)->value &
~(x86_page_entry_data_t)MMU_PTE_PAGE_MASK);
} else {
*pte_flags = 0;
}
}
int _arch_buffer_validate(void *addr, size_t size, int write)
{
u32_t start_pde_num;
u32_t end_pde_num;
u32_t starting_pte_num;
u32_t ending_pte_num;
u32_t pde;
u32_t pte;
#ifdef CONFIG_X86_PAE_MODE
union x86_mmu_pae_pte pte_value;
u32_t start_pdpte_num = MMU_PDPTE_NUM(addr);
u32_t end_pdpte_num = MMU_PDPTE_NUM((char *)addr + size - 1);
u32_t pdpte;
#else
union x86_mmu_pte pte_value;
#endif
struct x86_mmu_page_table *pte_address;
start_pde_num = MMU_PDE_NUM(addr);
end_pde_num = MMU_PDE_NUM((char *)addr + size - 1);
starting_pte_num = MMU_PAGE_NUM((char *)addr);
#ifdef CONFIG_X86_PAE_MODE
for (pdpte = start_pdpte_num; pdpte <= end_pdpte_num; pdpte++) {
if (pdpte != start_pdpte_num) {
start_pde_num = 0;
}
if (pdpte != end_pdpte_num) {
end_pde_num = 0;
} else {
end_pde_num = MMU_PDE_NUM((char *)addr + size - 1);
}
struct x86_mmu_page_directory *pd_address =
X86_MMU_GET_PD_ADDR_INDEX(pdpte);
#endif
/* 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++) {
#ifdef CONFIG_X86_PAE_MODE
union x86_mmu_pae_pde pde_value =
pd_address->entry[pde];
#else
union x86_mmu_pde_pt pde_value =
X86_MMU_PD->entry[pde].pt;
#endif
if (!pde_value.p ||
!pde_value.us ||
(write && !pde_value.rw)) {
return -EPERM;
}
pte_address = (struct x86_mmu_page_table *)
(pde_value.page_table << MMU_PAGE_SHIFT);
/* 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.
* An optimization done to make sure a compare is
* done only once.
*/
for (pte = starting_pte_num;
pte <= ending_pte_num;
pte++) {
pte_value.value &=
pte_address->entry[pte].value;
}
if (!pte_value.p ||
!pte_value.us ||
(write && !pte_value.rw)) {
return -EPERM;
}
}
#ifdef CONFIG_X86_PAE_MODE
}
#endif
return 0;
}
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,
x86_page_entry_data_t flags,
x86_page_entry_data_t mask)
{
#ifdef CONFIG_X86_PAE_MODE
union x86_mmu_pae_pte *pte;
#else
union x86_mmu_pte *pte;
#endif
u32_t addr = (u32_t)ptr;
__ASSERT(!(addr & MMU_PAGE_MASK), "unaligned address provided");
__ASSERT(!(size & MMU_PAGE_MASK), "unaligned size provided");
while (size) {
#ifdef CONFIG_X86_PAE_MODE
/* TODO we're not generating 2MB entries at the moment */
__ASSERT(X86_MMU_GET_PDE(addr)->ps != 1, "2MB PDE found");
#else
/* TODO we're not generating 4MB entries at the moment */
__ASSERT(X86_MMU_GET_4MB_PDE(addr)->ps != 1, "4MB PDE found");
#endif
pte = X86_MMU_GET_PTE(addr);
pte->value = (pte->value & ~mask) | flags;
tlb_flush_page((void *)addr);
size -= MMU_PAGE_SIZE;
addr += MMU_PAGE_SIZE;
}
}