blob: fd8137788d2f74084cd35fb254d36587b1a05a93 [file] [log] [blame]
/****************************************************************************
* arch/risc-v/src/esp32c3-legacy/esp32c3_userspace.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 <assert.h>
#include <debug.h>
#include <stdint.h>
#include <stdlib.h>
#include <nuttx/userspace.h>
#include <arch/board/board_memorymap.h>
#include "riscv_internal.h"
#include "esp32c3.h"
#include "esp32c3_userspace.h"
#include "hardware/esp32c3_cache_memory.h"
#include "hardware/extmem_reg.h"
#ifdef CONFIG_BUILD_PROTECTED
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define USER_IMAGE_OFFSET CONFIG_ESP32C3_USER_IMAGE_OFFSET
#define MMU_BLOCK0_VADDR SOC_DROM_LOW
#define MMU_SIZE 0x3f0000
#define MMU_BLOCK63_VADDR (MMU_BLOCK0_VADDR + MMU_SIZE)
/* Cache MMU block size */
#define MMU_BLOCK_SIZE 0x00010000 /* 64 KB */
/* Cache MMU address mask (MMU tables ignore bits which are zero) */
#define MMU_FLASH_MASK (~(MMU_BLOCK_SIZE - 1))
/****************************************************************************
* Private Types
****************************************************************************/
struct user_image_load_header_s
{
uintptr_t drom_vma; /* Destination address (VMA) for DROM region */
uintptr_t drom_lma; /* Flash offset (LMA) for start of DROM region */
uintptr_t drom_size; /* Size of DROM region */
uintptr_t irom_vma; /* Destination address (VMA) for IROM region */
uintptr_t irom_lma; /* Flash offset (LMA) for start of IROM region */
uintptr_t irom_size; /* Size of IROM region */
};
/****************************************************************************
* ROM Function Prototypes
****************************************************************************/
extern uint32_t cache_suspend_icache(void);
extern void cache_resume_icache(uint32_t val);
extern void cache_invalidate_icache_all(void);
extern int cache_dbus_mmu_set(uint32_t ext_ram, uint32_t vaddr,
uint32_t paddr, uint32_t psize, uint32_t num,
uint32_t fixed);
extern int cache_ibus_mmu_set(uint32_t ext_ram, uint32_t vaddr,
uint32_t paddr, uint32_t psize, uint32_t num,
uint32_t fixed);
/****************************************************************************
* Private Data
****************************************************************************/
static struct user_image_load_header_s g_header;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: calc_mmu_pages
*
* Description:
* Calculate the required number of MMU pages for mapping a given region
* from External Flash into Internal RAM.
*
* Input Parameters:
* size - Length of the region to map
* vaddr - Starting External Flash offset to map to Internal RAM
*
* Returned Value:
* None.
*
****************************************************************************/
static inline uint32_t calc_mmu_pages(uint32_t size, uint32_t vaddr)
{
return (size + (vaddr - (vaddr & MMU_FLASH_MASK)) + MMU_BLOCK_SIZE - 1) /
MMU_BLOCK_SIZE;
}
/****************************************************************************
* Name: configure_mmu
*
* Description:
* Configure the External Flash MMU and Cache for enabling access to code
* and read-only data of the userspace image.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static noinline_function IRAM_ATTR void configure_mmu(void)
{
uint32_t drom_lma_aligned;
uint32_t drom_vma_aligned;
uint32_t drom_page_count;
uint32_t irom_lma_aligned;
uint32_t irom_vma_aligned;
uint32_t irom_page_count;
size_t partition_offset = USER_IMAGE_OFFSET;
uint32_t app_drom_lma = partition_offset + g_header.drom_lma;
uint32_t app_drom_size = g_header.drom_size;
uint32_t app_drom_vma = g_header.drom_vma;
uint32_t app_irom_lma = partition_offset + g_header.irom_lma;
uint32_t app_irom_size = g_header.irom_size;
uint32_t app_irom_vma = g_header.irom_vma;
uint32_t autoload = cache_suspend_icache();
cache_invalidate_icache_all();
drom_lma_aligned = app_drom_lma & MMU_FLASH_MASK;
drom_vma_aligned = app_drom_vma & MMU_FLASH_MASK;
drom_page_count = calc_mmu_pages(app_drom_size, app_drom_vma);
ASSERT(cache_dbus_mmu_set(MMU_ACCESS_FLASH, drom_vma_aligned,
drom_lma_aligned, 64,
(int)drom_page_count, 0) == 0);
irom_lma_aligned = app_irom_lma & MMU_FLASH_MASK;
irom_vma_aligned = app_irom_vma & MMU_FLASH_MASK;
irom_page_count = calc_mmu_pages(app_irom_size, app_irom_vma);
ASSERT(cache_ibus_mmu_set(MMU_ACCESS_FLASH, irom_vma_aligned,
irom_lma_aligned, 64,
(int)irom_page_count, 0) == 0);
cache_resume_icache(autoload);
}
/****************************************************************************
* Name: map_flash
*
* Description:
* Map a region of the External Flash memory to Internal RAM.
*
* Input Parameters:
* src_addr - Starting External Flash offset to map to Internal RAM
* size - Length of the region to map
*
* Returned Value:
* None.
*
****************************************************************************/
static noinline_function IRAM_ATTR const void *map_flash(uint32_t src_addr,
uint32_t size)
{
uint32_t src_addr_aligned;
uint32_t page_count;
uint32_t autoload = cache_suspend_icache();
cache_invalidate_icache_all();
src_addr_aligned = src_addr & MMU_FLASH_MASK;
page_count = calc_mmu_pages(size, src_addr);
ASSERT(cache_dbus_mmu_set(MMU_ACCESS_FLASH, MMU_BLOCK63_VADDR,
src_addr_aligned, 64, (int)page_count, 0) == 0);
cache_resume_icache(autoload);
return (void *)(MMU_BLOCK63_VADDR + (src_addr - src_addr_aligned));
}
/****************************************************************************
* Name: load_header
*
* Description:
* Load IROM and DROM information from image header to enable the correct
* configuration of the Flash MMU and Cache.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static void load_header(void)
{
size_t length = sizeof(struct user_image_load_header_s);
const uint8_t *data =
(const uint8_t *)map_flash(USER_IMAGE_OFFSET, length);
DEBUGASSERT(data != NULL);
memcpy(&g_header, data, length);
}
/****************************************************************************
* Name: initialize_data
*
* Description:
* Initialize data sections of the userspace image.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static void initialize_data(void)
{
uint8_t *dest;
uint8_t *end;
size_t length = USERSPACE->us_dataend - USERSPACE->us_datastart;
const uint8_t *src =
(const uint8_t *)map_flash(USER_IMAGE_OFFSET + USERSPACE->us_datasource,
length);
DEBUGASSERT(src != NULL);
dest = (uint8_t *)USERSPACE->us_datastart;
end = (uint8_t *)USERSPACE->us_dataend;
while (dest != end)
{
*dest++ = *src++;
}
}
/****************************************************************************
* Name: configure_mpu
*
* Description:
* Configure the MPU for kernel/userspace separation.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static void configure_mpu(void)
{
const uintptr_t R = PMPCFG_R;
const uintptr_t RW = PMPCFG_R | PMPCFG_W;
const uintptr_t RX = PMPCFG_R | PMPCFG_X;
const uintptr_t RWX = PMPCFG_R | PMPCFG_W | PMPCFG_X;
/* Ensure PMP had not been previously configured by the bootloader */
DEBUGASSERT(riscv_configured_pmp_regions() == 0);
/* Region for the userspace read-only data in SPI Flash */
riscv_config_pmp_region(0, PMPCFG_A_TOR, UDROM_START, 0);
riscv_config_pmp_region(1, PMPCFG_A_TOR | R, UDROM_END, 0);
/* Region for the userspace data.
* NOTE: User-space heap may extend further than UDRAM_END.
*/
riscv_config_pmp_region(2, PMPCFG_A_TOR, UDRAM_START, 0);
riscv_config_pmp_region(3, PMPCFG_A_TOR | RW, SOC_DRAM_HIGH, 0);
/* Region for the memory-mapped functions located in internal ROM */
riscv_config_pmp_region(4, PMPCFG_L | PMPCFG_A_NAPOT | R,
SOC_DROM_MASK_LOW,
SOC_DROM_MASK_HIGH - SOC_DROM_MASK_LOW);
riscv_config_pmp_region(5, PMPCFG_L | PMPCFG_A_TOR | RX,
SOC_IROM_MASK_HIGH,
0);
/* Region for the exception vectors located in internal SRAM area reserved
* for the kernel space.
*/
riscv_config_pmp_region(6, PMPCFG_A_NAPOT | RX,
VECTORS_START,
VECTORS_END - VECTORS_START);
/* Region for the userspace code located in internal SRAM */
riscv_config_pmp_region(7, PMPCFG_A_TOR, UIRAM_START, 0);
riscv_config_pmp_region(8, PMPCFG_A_TOR | RWX, UIRAM_END, 0);
/* Region for the userspace code in SPI Flash */
riscv_config_pmp_region(9, PMPCFG_A_TOR, UIROM_START, 0);
riscv_config_pmp_region(10, PMPCFG_A_TOR | RX, UIROM_END, 0);
/* Region for peripheral addresses */
riscv_config_pmp_region(11, PMPCFG_A_TOR, SOC_PERIPHERAL_HIGH, 0);
/* Region for the remainder of the address space */
riscv_config_pmp_region(12, PMPCFG_L | PMPCFG_A_TOR, UINT32_MAX, 0);
riscv_config_pmp_region(13, PMPCFG_L | PMPCFG_A_NA4, UINT32_MAX, 0);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32c3_userspace
*
* Description:
* For the case of the separate user/kernel space build, perform whatever
* platform specific initialization of the user memory is required.
* Normally this just means initializing the userspace .data and .bss
* segments.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
void esp32c3_userspace(void)
{
uint8_t *dest;
uint8_t *end;
/* Load IROM and DROM information from image header */
load_header();
/* Configure the MMU for enabling access to the userspace image */
configure_mmu();
/* Clear all of userspace .bss */
DEBUGASSERT(USERSPACE->us_bssstart != 0 && USERSPACE->us_bssend != 0 &&
USERSPACE->us_bssstart <= USERSPACE->us_bssend);
dest = (uint8_t *)USERSPACE->us_bssstart;
end = (uint8_t *)USERSPACE->us_bssend;
while (dest != end)
{
*dest++ = 0;
}
/* Initialize all of userspace .data */
DEBUGASSERT(USERSPACE->us_datasource != 0 &&
USERSPACE->us_datastart != 0 && USERSPACE->us_dataend != 0 &&
USERSPACE->us_datastart <= USERSPACE->us_dataend);
initialize_data();
/* Configure MPU / PMP to grant access to the userspace */
configure_mpu();
}
#endif /* CONFIG_BUILD_PROTECTED */