| /**************************************************************************** |
| * 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 */ |