2013-08-02 19:11:57 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* arch/arm/src/armv7-a/arm_mmu.c
|
|
|
|
*
|
2021-03-24 09:12:29 +01:00
|
|
|
* 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
|
2013-08-02 19:11:57 +02:00
|
|
|
*
|
2021-03-24 09:12:29 +01:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2013-08-02 19:11:57 +02:00
|
|
|
*
|
2021-03-24 09:12:29 +01:00
|
|
|
* 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.
|
2013-08-02 19:11:57 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
2019-03-19 17:37:13 +01:00
|
|
|
#include "cp15_cacheops.h"
|
2013-08-02 19:11:57 +02:00
|
|
|
#include "mmu.h"
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l1_setentry
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set a one level 1 translation table entry. Only a single L1 page table
|
|
|
|
* is supported.
|
|
|
|
*
|
2014-08-25 19:18:32 +02:00
|
|
|
* Input Parameters:
|
2013-08-02 19:11:57 +02:00
|
|
|
* paddr - The physical address to be mapped. Must be aligned to a 1MB
|
|
|
|
* address boundary
|
|
|
|
* vaddr - The virtual address to be mapped. Must be aligned to a 1MB
|
|
|
|
* address boundary
|
|
|
|
* mmuflags - The MMU flags to use in the mapping.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_l1_setentry(uint32_t paddr, uint32_t vaddr, uint32_t mmuflags)
|
|
|
|
{
|
2022-05-06 09:52:23 +02:00
|
|
|
uint32_t *l1table = mmu_l1_pgtable();
|
2013-08-02 19:11:57 +02:00
|
|
|
uint32_t index = vaddr >> 20;
|
|
|
|
|
|
|
|
/* Save the page table entry */
|
|
|
|
|
2014-08-25 19:18:32 +02:00
|
|
|
l1table[index] = (paddr | mmuflags);
|
2013-08-02 19:11:57 +02:00
|
|
|
|
|
|
|
/* Flush the data cache entry. Make sure that the modified contents
|
|
|
|
* of the page table are flushed into physical memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
cp15_clean_dcache_bymva((uint32_t)&l1table[index]);
|
|
|
|
|
|
|
|
/* Invalidate the TLB cache associated with virtual address range */
|
|
|
|
|
2019-01-06 14:38:04 +01:00
|
|
|
mmu_invalidate_region(vaddr, SECTION_SIZE);
|
2013-08-02 19:11:57 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-08-25 19:18:32 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l1_restore
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Restore one L1 table entry previously returned by mmu_l1_getentry() (or
|
|
|
|
* any other encoded L1 page table value).
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* vaddr - A virtual address to be mapped
|
|
|
|
* l1entry - The value to write into the page table entry
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#if !defined(CONFIG_ARCH_ROMPGTABLE) && defined(CONFIG_ARCH_ADDRENV)
|
2014-08-25 21:28:13 +02:00
|
|
|
void mmu_l1_restore(uintptr_t vaddr, uint32_t l1entry)
|
2014-08-25 19:18:32 +02:00
|
|
|
{
|
2022-05-06 09:52:23 +02:00
|
|
|
uint32_t *l1table = mmu_l1_pgtable();
|
2014-08-25 19:18:32 +02:00
|
|
|
uint32_t index = vaddr >> 20;
|
|
|
|
|
|
|
|
/* Set the encoded page table entry */
|
|
|
|
|
|
|
|
l1table[index] = l1entry;
|
|
|
|
|
|
|
|
/* Flush the data cache entry. Make sure that the modified contents
|
|
|
|
* of the page table are flushed into physical memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
cp15_clean_dcache_bymva((uint32_t)&l1table[index]);
|
|
|
|
|
|
|
|
/* Invalidate the TLB cache associated with virtual address range */
|
|
|
|
|
2019-01-06 14:38:04 +01:00
|
|
|
mmu_invalidate_region(vaddr & PMD_PTE_PADDR_MASK, SECTION_SIZE);
|
2014-08-25 19:18:32 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-08-02 19:11:57 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l2_setentry
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set one small (4096B) entry in a level2 translation table.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* l2vaddr - the virtual address of the beginning of the L2 translation
|
|
|
|
* table.
|
|
|
|
* paddr - The physical address to be mapped. Must be aligned to a 4KB
|
|
|
|
* address boundary
|
|
|
|
* vaddr - The virtual address to be mapped. Must be aligned to a 4KB
|
|
|
|
* address boundary
|
|
|
|
* mmuflags - The MMU flags to use in the mapping.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_l2_setentry(uint32_t l2vaddr, uint32_t paddr, uint32_t vaddr,
|
|
|
|
uint32_t mmuflags)
|
|
|
|
{
|
2015-10-07 00:23:32 +02:00
|
|
|
uint32_t *l2table = (uint32_t *)l2vaddr;
|
2013-08-02 19:11:57 +02:00
|
|
|
uint32_t index;
|
|
|
|
|
|
|
|
/* The table divides a 1Mb address space up into 256 entries, each
|
|
|
|
* corresponding to 4Kb of address space. The page table index is
|
|
|
|
* related to the offset from the beginning of 1Mb region.
|
|
|
|
*/
|
|
|
|
|
|
|
|
index = (vaddr & 0x000ff000) >> 12;
|
|
|
|
|
|
|
|
/* Save the table entry */
|
|
|
|
|
|
|
|
l2table[index] = (paddr | mmuflags);
|
|
|
|
|
|
|
|
/* Flush the data cache entry. Make sure that the modified contents
|
|
|
|
* of the page table are flushed into physical memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
cp15_clean_dcache_bymva((uint32_t)&l2table[index]);
|
|
|
|
|
|
|
|
/* Invalidate the TLB cache associated with virtual address range */
|
|
|
|
|
|
|
|
cp15_invalidate_tlb_bymva(vaddr);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
2013-10-13 21:08:05 +02:00
|
|
|
* Name: mmu_l1_map_region
|
2013-08-02 19:11:57 +02:00
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set multiple level 1 translation table entries in order to map a
|
|
|
|
* region of memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mapping - Describes the mapping to be performed.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
2013-10-13 21:08:05 +02:00
|
|
|
void mmu_l1_map_region(const struct section_mapping_s *mapping)
|
2013-08-02 19:11:57 +02:00
|
|
|
{
|
|
|
|
uint32_t paddr = mapping->physbase;
|
|
|
|
uint32_t vaddr = mapping->virtbase;
|
|
|
|
uint32_t mmuflags = mapping->mmuflags;
|
|
|
|
int i;
|
|
|
|
|
2013-12-16 20:48:20 +01:00
|
|
|
/* Loop, writing each mapping into the L1 page table */
|
2013-08-02 19:11:57 +02:00
|
|
|
|
|
|
|
for (i = 0; i < mapping->nsections; i++)
|
|
|
|
{
|
|
|
|
mmu_l1_setentry(paddr, vaddr, mmuflags);
|
|
|
|
paddr += SECTION_SIZE;
|
|
|
|
vaddr += SECTION_SIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-03-19 18:30:37 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l1_map_regions
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set multiple level 1 translation table entries in order to map a region
|
|
|
|
* array of memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mappings - Describes the array of mappings to be performed.
|
|
|
|
* count - The number of mappings to be performed.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_l1_map_regions(const struct section_mapping_s *mappings,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
mmu_l1_map_region(&mappings[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-10-12 10:45:58 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l1_map_page
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set level 1 page entrie in order to map a region
|
|
|
|
* array of memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mapping - Describes the mapping to be performed.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_l1_map_page(const struct section_mapping_s *mapping)
|
|
|
|
{
|
|
|
|
uint32_t virtaddr = mapping->virtbase;
|
|
|
|
uint32_t l2table = mapping->physbase;
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < mapping->nsections; i++)
|
|
|
|
{
|
|
|
|
mmu_l1_setentry(l2table, virtaddr, mapping->mmuflags);
|
|
|
|
|
|
|
|
/* Update the L2 page table address for the next L1 table entry. */
|
|
|
|
|
|
|
|
virtaddr += SECTION_SIZE;
|
|
|
|
l2table += 1024;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l1_map_pages
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set multiple level 1 page entries in order to map a region
|
|
|
|
* array of memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mappings - Describes the mapping to be performed.
|
|
|
|
* count - The number of mappings to be performed.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_l1_map_pages(const struct section_mapping_s *mappings,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
mmu_l1_map_page(&mappings[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l2_map_page
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set level 2 page entrie in order to map a region
|
|
|
|
* array of memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mapping - Describes the mapping to be performed.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_l2_map_page(const struct page_mapping_s *mapping)
|
|
|
|
{
|
|
|
|
uint32_t l2table = mapping->l2table;
|
|
|
|
struct page_entry_s *entry;
|
|
|
|
uint32_t paddr;
|
|
|
|
uint32_t vaddr;
|
|
|
|
uint32_t entry_cnt;
|
|
|
|
uint32_t page_cnt;
|
|
|
|
|
|
|
|
for (entry_cnt = 0; entry_cnt < mapping->entrynum; entry_cnt++)
|
|
|
|
{
|
|
|
|
entry = (struct page_entry_s *)&mapping->entry[entry_cnt];
|
|
|
|
paddr = entry->physbase;
|
|
|
|
vaddr = entry->virtbase;
|
|
|
|
|
|
|
|
for (page_cnt = 0; page_cnt < entry->npages; page_cnt++)
|
|
|
|
{
|
|
|
|
mmu_l2_setentry(l2table, paddr, vaddr, entry->mmuflags);
|
|
|
|
|
|
|
|
paddr += 4096;
|
|
|
|
vaddr += 4096;
|
|
|
|
|
|
|
|
if ((vaddr & 0x000ff000) == 0)
|
|
|
|
{
|
|
|
|
l2table += 1024;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_l2_map_pages
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Set multiple level 2 page entries in order to map a region
|
|
|
|
* array of memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mappings - Describes the mapping to be performed.
|
|
|
|
* count - The number of mappings to be performed.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_l2_map_pages(const struct page_mapping_s *mappings,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
mmu_l2_map_page(&mappings[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-08-02 19:11:57 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mmu_invalidate_region
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Invalidate TLBs for a range of addresses (all 4KB aligned).
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* vaddr - The beginning of the region to invalidate.
|
|
|
|
* size - The size of the region in bytes to be invalidated.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_ROMPGTABLE
|
|
|
|
void mmu_invalidate_region(uint32_t vstart, size_t size)
|
|
|
|
{
|
|
|
|
uint32_t vaddr = vstart & 0xfffff000;
|
2019-01-06 14:38:04 +01:00
|
|
|
uint32_t vend = vstart + size;
|
2013-08-02 19:11:57 +02:00
|
|
|
|
2014-04-02 02:27:08 +02:00
|
|
|
/* Loop, invalidating regions */
|
2013-08-02 19:11:57 +02:00
|
|
|
|
|
|
|
while (vaddr < vend)
|
|
|
|
{
|
|
|
|
cp15_invalidate_tlb_bymva(vaddr);
|
|
|
|
vaddr += 4096;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|