diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index a22b9310b3..4a8458bd77 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -16,8 +16,8 @@ choice config ARCH_INTEL64 bool "Intel x86_64" - select ARCH_HAVE_MPU - select ARCH_USE_MPU + select ARCH_HAVE_MMU + select ARCH_USE_MMU select ARCH_HAVE_TICKLESS select ARCH_HAVE_STACKCHECK select ARCH_HAVE_RNG diff --git a/arch/x86_64/include/intel64/irq.h b/arch/x86_64/include/intel64/irq.h index 3f3d27c2d9..d27019d457 100644 --- a/arch/x86_64/include/intel64/irq.h +++ b/arch/x86_64/include/intel64/irq.h @@ -558,6 +558,25 @@ static inline void set_pcid(uint64_t pcid) } } +static inline void set_cr3(uint64_t cr3) +{ + asm volatile("mov %0, %%cr3" : "=rm"(cr3) : : "memory"); +} + +static inline uint64_t get_cr3(void) +{ + uint64_t cr3; + asm volatile("mov %%cr3, %0" : "=rm"(cr3) : : "memory"); + return cr3; +} + +static inline uint64_t get_pml4(void) +{ + /* Aligned to a 4-KByte boundary */ + + return get_cr3() & 0xfffffffffffff000; +} + static inline unsigned long read_msr(unsigned int msr) { uint32_t low; diff --git a/arch/x86_64/src/common/CMakeLists.txt b/arch/x86_64/src/common/CMakeLists.txt index f50ff808e3..600a643f05 100644 --- a/arch/x86_64/src/common/CMakeLists.txt +++ b/arch/x86_64/src/common/CMakeLists.txt @@ -41,4 +41,8 @@ if(CONFIG_ARCH_X86_64_ACPI) list(APPEND SRCS x86_64_acpi.c) endif() +if(CONFIG_ARCH_USE_MMU) + list(APPEND SRCS x86_64_mmu.c) +endif() + target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/x86_64/src/common/Make.defs b/arch/x86_64/src/common/Make.defs index 12db901b99..97756c9e91 100644 --- a/arch/x86_64/src/common/Make.defs +++ b/arch/x86_64/src/common/Make.defs @@ -32,3 +32,7 @@ endif ifeq ($(CONFIG_ARCH_X86_64_ACPI),y) CMN_CSRCS += x86_64_acpi.c endif + +ifeq ($(CONFIG_ARCH_USE_MMU),y) +CMN_CSRCS += x86_64_mmu.c +endif diff --git a/arch/x86_64/src/common/addrenv.h b/arch/x86_64/src/common/addrenv.h new file mode 100644 index 0000000000..9c6dc9a1ca --- /dev/null +++ b/arch/x86_64/src/common/addrenv.h @@ -0,0 +1,89 @@ +/**************************************************************************** + * arch/x86_64/src/common/addrenv.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_X86_64_SRC_COMMON_ADDRENV_H +#define __ARCH_X86_64_SRC_COMMON_ADDRENV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "x86_64_internal.h" + +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Aligned size of the kernel stack */ + +#ifdef CONFIG_ARCH_KERNEL_STACK +# define ARCH_KERNEL_STACKSIZE STACK_ALIGN_UP(CONFIG_ARCH_KERNEL_STACKSIZE) +#endif + +/* Base address for address environment */ + +#if CONFIG_ARCH_TEXT_VBASE != 0 +# define ARCH_ADDRENV_VBASE (CONFIG_ARCH_TEXT_VBASE) +#else +# define ARCH_ADDRENV_VBASE (CONFIG_ARCH_DATA_VBASE) +#endif + +/* Maximum user address environment size */ + +#define ARCH_ADDRENV_MAX_SIZE (0x40000000) + +/* User address environment end */ + +#define ARCH_ADDRENV_VEND (ARCH_ADDRENV_VBASE + ARCH_ADDRENV_MAX_SIZE - 1) + +/* Flags for kernel page tables */ + +#define MMU_KPGT_FLAGS (X86_PAGE_WR) + +/* Mark user memory if in kernel build */ + +#ifndef CONFIG_BUILD_KERNEL +# define MMU_USER_DEFAULT (X86_PAGE_USER) +#else +# define MMU_USER_DEFAULT (0) +#endif + +/* Flags for user page tables */ + +#define MMU_UPGT_FLAGS (X86_PAGE_WR | MMU_USER_DEFAULT) + +/* Flags for user FLASH (RX) and user RAM (RW) */ + +#define MMU_UDATA_FLAGS (X86_PAGE_WR | MMU_USER_DEFAULT) +#define MMU_UTEXT_FLAGS (X86_PAGE_WR | MMU_USER_DEFAULT) + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#endif /* CONFIG_ARCH_ADDRENV */ +#endif /* __ARCH_X86_64_SRC_COMMON_ADDRENV_H */ diff --git a/arch/x86_64/src/common/x86_64_mmu.c b/arch/x86_64/src/common/x86_64_mmu.c new file mode 100644 index 0000000000..99482a9578 --- /dev/null +++ b/arch/x86_64/src/common/x86_64_mmu.c @@ -0,0 +1,146 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_mmu.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "x86_64_internal.h" +#include "x86_64_mmu.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mmu_ln_setentry + * + * Description: + * Set a level n translation table entry. + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * paddr - The physical address to be mapped + * vaddr - The virtual address to be mapped + * mmuflags - The MMU flags to use in the mapping. + * + ****************************************************************************/ + +void mmu_ln_setentry(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, uint32_t mmuflags) +{ + uintptr_t *lntable = (uintptr_t *)lnvaddr; + uint32_t index; + + DEBUGASSERT(ptlevel >= 0 && ptlevel < X86_MMU_PT_LEVELS); + + /* Make sure the entry is valid */ + + mmuflags |= X86_PAGE_PRESENT; + + /* Calculate index for lntable */ + + index = X86_MMU_VADDR_INDEX(vaddr, ptlevel); + + /* Save it */ + + lntable[index] = (paddr | mmuflags); + + /* Update with memory by flushing the cache(s) */ + + up_invalid_tlb(vaddr, vaddr + 1); +} + +/**************************************************************************** + * Name: mmu_ln_getentry + * + * Description: + * Get a level n translation table entry. + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * vaddr - The virtual address to get pte for + * + ****************************************************************************/ + +uintptr_t mmu_ln_getentry(uint32_t ptlevel, uintptr_t lnvaddr, + uintptr_t vaddr) +{ + uintptr_t *lntable = (uintptr_t *)lnvaddr; + uint32_t index; + + DEBUGASSERT(ptlevel >= 0 && ptlevel < X86_MMU_PT_LEVELS); + + index = X86_MMU_VADDR_INDEX(vaddr, ptlevel); + + return lntable[index]; +} + +/**************************************************************************** + * Name: mmu_ln_map_region + * + * Description: + * Set a translation table region for level n + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * paddr - The physical address to be mapped + * vaddr - The virtual address to be mapped + * size - The size of the region in bytes + * mmuflags - The MMU flags to use in the mapping. + * + ****************************************************************************/ + +void mmu_ln_map_region(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, size_t size, uint32_t mmuflags) +{ + uintptr_t end_paddr = paddr + size; + size_t page_size = X86_MMU_PAGE_SIZE; + + DEBUGASSERT(ptlevel >= 0 && ptlevel < X86_MMU_PT_LEVELS); + + while (paddr < end_paddr) + { + mmu_ln_setentry(ptlevel, lnvaddr, paddr, vaddr, mmuflags); + paddr += page_size; + vaddr += page_size; + } +} diff --git a/arch/x86_64/src/common/x86_64_mmu.h b/arch/x86_64/src/common/x86_64_mmu.h new file mode 100644 index 0000000000..ac95dbc746 --- /dev/null +++ b/arch/x86_64/src/common/x86_64_mmu.h @@ -0,0 +1,178 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_mmu.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_X86_64_SRC_COMMON_X86_64_MMU_H +#define __ARCH_X86_64_SRC_COMMON_X86_64_MMU_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 4 level paging, 4K pages */ + +#define X86_MMU_PT_LEVELS 4 +#define X86_MMU_PAGE_SHIFT 12 +#define X86_MMU_PAGE_SIZE (1 << X86_MMU_PAGE_SHIFT) +#define X86_MMU_ENTRIES_PER_PGT (X86_MMU_PAGE_SIZE / 8) + +/* Get virtual address shift for a given paging level. + * NOTE: in this implementation PTL4 has index 0, PT has index 3 ! + */ + +#define X86_MMU_PADDR_SHIFT 12 +#define X86_MMU_VPN_WIDTH 9 +#define X86_MMU_VPN_MASK ((1 << X86_MMU_VPN_WIDTH) - 1) +#define X86_MMU_VADDR_SHIFT(n) (X86_MMU_PADDR_SHIFT + X86_MMU_VPN_WIDTH * \ + (X86_MMU_PT_LEVELS - ((n) + 1))) + +/* Get index in a given page table level for a given virtual address */ + +#define X86_MMU_VADDR_INDEX(vaddr, ptlevel) \ + ((vaddr >> X86_MMU_VADDR_SHIFT(ptlevel)) & X86_MMU_VPN_MASK) + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mmu_cr3_reg + * + * Description: + * Utility function to build cr3 register value for input parameters + * + * Input Parameters: + * pgbase - The physical base address of the translation table base + * asid - Address space identifier. Not used now. + * + ****************************************************************************/ + +static inline uintptr_t mmu_cr3_reg(uintptr_t pgbase, uint16_t asid) +{ + uintptr_t reg; + + UNUSED(asid); + + reg = pgbase; + return reg; +} + +/**************************************************************************** + * Name: mmu_pte_to_paddr + * + * Description: + * Extract physical address from PTE + * + * Input Parameters: + * pte - Page table entry + * + * Returned Value: + * Physical address from PTE + * + ****************************************************************************/ + +static inline uintptr_t mmu_pte_to_paddr(uintptr_t pte) +{ + uintptr_t paddr = 0; + + if (pte & X86_PAGE_PRESENT) + { + paddr = pte; + + /* Get page addres - remove flags */ + + paddr &= 0x0dfffffffffff000; + } + + return paddr; +} + +/**************************************************************************** + * Name: mmu_ln_setentry + * + * Description: + * Set a level n translation table entry. + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * paddr - The physical address to be mapped. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * vaddr - The virtual address to be mapped. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * mmuflags - The MMU flags to use in the mapping. + * + ****************************************************************************/ + +void mmu_ln_setentry(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, uint32_t mmuflags); + +/**************************************************************************** + * Name: mmu_ln_getentry + * + * Description: + * Get a level n translation table entry. + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * vaddr - The virtual address to get pte for. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * + ****************************************************************************/ + +uintptr_t mmu_ln_getentry(uint32_t ptlevel, uintptr_t lnvaddr, + uintptr_t vaddr); + +/**************************************************************************** + * Name: mmu_ln_map_region + * + * Description: + * Set a translation table region for level n + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * paddr - The physical address to be mapped. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * vaddr - The virtual address to be mapped. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * size - The size of the region in bytes + * mmuflags - The MMU flags to use in the mapping. + * + ****************************************************************************/ + +void mmu_ln_map_region(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, size_t size, uint32_t mmuflags); + +#endif /* __ARCH_X86_64_SRC_COMMON_X86_64_MMU_H */