blob: 657aaabfda3e63e045e0bcd67a8f00cad531ab04 [file] [log] [blame]
/****************************************************************************
* arch/risc-v/src/litex/litex_mm_init.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 <nuttx/config.h>
#include <nuttx/arch.h>
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <arch/board/board_memorymap.h>
#include "litex_memorymap.h"
#include "riscv_internal.h"
#include "riscv_mmu.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Map the whole I/O memory with vaddr = paddr mappings */
#define MMU_IO_BASE CONFIG_LITEX_MMU_IO_BASE
#define MMU_IO_SIZE CONFIG_LITEX_MMU_IO_SIZE
#ifdef CONFIG_ARCH_MMU_TYPE_SV32
/* Physical and virtual addresses to page tables (vaddr = paddr mapping) */
#define PGT_L1_PBASE (uintptr_t)&m_l1_pgtable
#define PGT_L2_PBASE (uintptr_t)&m_l2_pgtable
#define PGT_L1_VBASE PGT_L1_PBASE
#define PGT_L2_VBASE PGT_L2_PBASE
#define PGT_L1_SIZE (CONFIG_LITEX_MMU_L1_SIZE)
#define PGT_L2_SIZE (CONFIG_LITEX_MMU_L2_SIZE)
#define SLAB_COUNT (sizeof(m_l2_pgtable) / RV_MMU_PAGE_SIZE)
#define KMM_PAGE_SIZE RV_MMU_L2_PAGE_SIZE
#define KMM_PBASE PGT_L2_PBASE
#define KMM_PBASE_IDX 2
#define KMM_SPBASE PGT_L1_PBASE
#define KMM_SPBASE_IDX 1
#else
#error No valid MMU defined.
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct pgalloc_slab_s
{
sq_entry_t *next;
void *memory;
};
typedef struct pgalloc_slab_s pgalloc_slab_t;
/****************************************************************************
* Private Data
****************************************************************************/
/* Kernel mappings simply here, mapping is vaddr=paddr */
static size_t m_l1_pgtable[PGT_L1_SIZE] locate_data(".pgtables");
static size_t m_l2_pgtable[PGT_L2_SIZE] locate_data(".pgtables");
/* Kernel mappings (L1 base) */
uintptr_t g_kernel_mappings = PGT_L1_VBASE;
uintptr_t g_kernel_pgt_pbase = PGT_L1_PBASE;
/* L2 page table allocator */
static sq_queue_t g_free_slabs;
static pgalloc_slab_t g_slabs[SLAB_COUNT];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: slab_init
*
* Description:
* Initialize slab allocator for L2 page table entries
*
* Input Parameters:
* start - Beginning of the L2 page table pool
*
****************************************************************************/
static void slab_init(uintptr_t start)
{
int i;
sq_init(&g_free_slabs);
for (i = 0; i < SLAB_COUNT; i++)
{
g_slabs[i].memory = (void *)start;
sq_addlast((sq_entry_t *)&g_slabs[i], (sq_queue_t *)&g_free_slabs);
start += RV_MMU_PAGE_SIZE;
}
}
/****************************************************************************
* Name: slab_alloc
*
* Description:
* Allocate single slab for L2 page table entry
*
****************************************************************************/
static uintptr_t slab_alloc(void)
{
pgalloc_slab_t *slab = (pgalloc_slab_t *)sq_remfirst(&g_free_slabs);
return slab ? (uintptr_t)slab->memory : 0;
}
/****************************************************************************
* Name: map_region
*
* Description:
* Map a region of physical memory to the L2 page table
*
* Input Parameters:
* paddr - Beginning of the physical address mapping
* vaddr - Beginning of the virtual address mapping
* size - Size of the region in bytes
* mmuflags - The MMU flags to use in the mapping
*
****************************************************************************/
static void map_region(uintptr_t paddr, uintptr_t vaddr, size_t size,
uint32_t mmuflags)
{
uintptr_t endaddr;
uintptr_t pbase;
int npages;
int i;
int j;
/* How many pages */
npages = (size + RV_MMU_PAGE_MASK) >> RV_MMU_PAGE_SHIFT;
endaddr = vaddr + size;
for (i = 0; i < npages; i += RV_MMU_PAGE_ENTRIES)
{
/* See if a L2 mapping exists ? */
pbase = mmu_pte_to_paddr(mmu_ln_getentry(
KMM_SPBASE_IDX, KMM_SPBASE, vaddr));
if (!pbase)
{
/* No, allocate 1 page, this must not fail */
pbase = slab_alloc();
DEBUGASSERT(pbase);
/* Map it to the L2 table */
mmu_ln_setentry(
KMM_SPBASE_IDX, KMM_SPBASE, pbase, vaddr, MMU_UPGT_FLAGS);
}
/* Then add the L2 mappings */
for (j = 0; j < RV_MMU_PAGE_ENTRIES && vaddr < endaddr; j++)
{
mmu_ln_setentry(KMM_PBASE_IDX, pbase, paddr, vaddr, mmuflags);
paddr += KMM_PAGE_SIZE;
vaddr += KMM_PAGE_SIZE;
}
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: litex_kernel_mappings
*
* Description:
* Setup kernel mappings when usinc CONFIG_BUILD_KERNEL. Sets up the kernel
* MMU mappings.
*
****************************************************************************/
void litex_kernel_mappings(void)
{
/* Initialize slab allocator for L2 page tables */
slab_init(KMM_PBASE);
/* Begin mapping memory to MMU; note that at this point the MMU is not yet
* active, so the page table virtual addresses are actually physical
* addresses and so forth. M-mode does not perform translations anyhow, so
* this mapping is quite simple to do
*/
binfo("map I/O regions\n");
mmu_ln_map_region(1, PGT_L1_VBASE, MMU_IO_BASE, MMU_IO_BASE,
MMU_IO_SIZE, MMU_IO_FLAGS);
/* Map the kernel text and data for L2 */
binfo("map kernel text\n");
map_region(KFLASH_START, KFLASH_START, KFLASH_SIZE, MMU_KTEXT_FLAGS);
binfo("map kernel data\n");
map_region(KSRAM_START, KSRAM_START, KSRAM_SIZE, MMU_KDATA_FLAGS);
binfo("map the page pool\n");
map_region(PGPOOL_START, PGPOOL_START, PGPOOL_SIZE, MMU_KDATA_FLAGS);
}
/****************************************************************************
* Name: litex_mm_init
*
* Description:
* Setup kernel mappings when using CONFIG_BUILD_KERNEL. Sets up kernel MMU
* mappings. Function also sets the first address environment (satp value).
*
****************************************************************************/
void litex_mm_init(void)
{
/* Setup the kernel mappings */
litex_kernel_mappings();
/* Enable MMU (note: system is still in M-mode) */
binfo("mmu_enable: satp=%" PRIuPTR "\n", g_kernel_pgt_pbase);
mmu_enable(g_kernel_pgt_pbase, 0);
}