| /**************************************************************************** |
| * arch/xtensa/src/esp32/esp32_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 "chip.h" |
| #include "xtensa.h" |
| #include "xtensa_attr.h" |
| #ifdef CONFIG_ESP32_USER_DATA_EXTMEM |
| #include "esp32_spiram.h" |
| #endif |
| #include "esp32_userspace.h" |
| #include "hardware/esp32_dport.h" |
| #include "hardware/esp32_pid.h" |
| |
| #ifdef CONFIG_BUILD_PROTECTED |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define USER_IMAGE_OFFSET CONFIG_ESP32_USER_IMAGE_OFFSET |
| |
| #define MMU_BLOCK0_VADDR SOC_DROM_LOW |
| #define MMU_SIZE 0x320000 |
| #define MMU_BLOCK50_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 void cache_read_enable(int cpu); |
| extern void cache_read_disable(int cpu); |
| extern void cache_flush(int cpu); |
| extern unsigned int cache_flash_mmu_set(int cpu_no, int pid, |
| unsigned int vaddr, |
| unsigned int paddr, |
| int psize, int num); |
| |
| /**************************************************************************** |
| * 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_flash_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_flash_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; |
| |
| cache_read_disable(0); |
| cache_flush(0); |
| |
| 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_flash_mmu_set(0, PIDCTRL_PID_KERNEL, drom_vma_aligned, |
| drom_lma_aligned, 64, |
| (int)drom_page_count) == 0); |
| ASSERT(cache_flash_mmu_set(1, PIDCTRL_PID_KERNEL, drom_vma_aligned, |
| drom_lma_aligned, 64, |
| (int)drom_page_count) == 0); |
| ASSERT(cache_flash_mmu_set(0, PIDCTRL_PID_USER, drom_vma_aligned, |
| drom_lma_aligned, 64, |
| (int)drom_page_count) == 0); |
| ASSERT(cache_flash_mmu_set(1, PIDCTRL_PID_USER, drom_vma_aligned, |
| drom_lma_aligned, 64, |
| (int)drom_page_count) == 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_flash_mmu_set(0, PIDCTRL_PID_KERNEL, irom_vma_aligned, |
| irom_lma_aligned, 64, |
| (int)irom_page_count) == 0); |
| ASSERT(cache_flash_mmu_set(1, PIDCTRL_PID_KERNEL, irom_vma_aligned, |
| irom_lma_aligned, 64, |
| (int)irom_page_count) == 0); |
| ASSERT(cache_flash_mmu_set(0, PIDCTRL_PID_USER, irom_vma_aligned, |
| irom_lma_aligned, 64, |
| (int)irom_page_count) == 0); |
| ASSERT(cache_flash_mmu_set(1, PIDCTRL_PID_USER, irom_vma_aligned, |
| irom_lma_aligned, 64, |
| (int)irom_page_count) == 0); |
| |
| cache_read_enable(0); |
| } |
| |
| /**************************************************************************** |
| * Name: configure_sram_mmu |
| * |
| * Description: |
| * Configure the External SRAM MMU and Cache for enabling access to the |
| * data of the userspace image. |
| * |
| * Input Parameters: |
| * None. |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ESP32_USER_DATA_EXTMEM |
| static noinline_function IRAM_ATTR void configure_sram_mmu(void) |
| { |
| #ifdef CONFIG_SMP |
| uint32_t regval; |
| #endif |
| |
| /* Enable external RAM in MMU */ |
| |
| ASSERT(cache_sram_mmu_set(0, PIDCTRL_PID_KERNEL, SOC_EXTRAM_DATA_LOW, 0, |
| 32, 128) == 0); |
| ASSERT(cache_sram_mmu_set(0, PIDCTRL_PID_USER, SOC_EXTRAM_DATA_LOW, 0, |
| 32, 128) == 0); |
| |
| /* Flush and enable icache for APP CPU */ |
| |
| #ifdef CONFIG_SMP |
| regval = getreg32(DPORT_APP_CACHE_CTRL1_REG); |
| regval &= ~(1 << DPORT_APP_CACHE_MASK_DRAM1); |
| putreg32(regval, DPORT_APP_CACHE_CTRL1_REG); |
| ASSERT(cache_sram_mmu_set(1, PIDCTRL_PID_KERNEL, SOC_EXTRAM_DATA_LOW, 0, |
| 32, 128) == 0); |
| ASSERT(cache_sram_mmu_set(1, PIDCTRL_PID_USER, SOC_EXTRAM_DATA_LOW, 0, |
| 32, 128) == 0); |
| #endif |
| } |
| #endif |
| |
| /**************************************************************************** |
| * 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; |
| |
| cache_read_disable(0); |
| cache_flush(0); |
| |
| src_addr_aligned = src_addr & MMU_FLASH_MASK; |
| page_count = calc_mmu_pages(size, src_addr); |
| ASSERT(cache_flash_mmu_set(0, PIDCTRL_PID_KERNEL, MMU_BLOCK50_VADDR, |
| src_addr_aligned, 64, (int)page_count) == 0); |
| |
| cache_read_enable(0); |
| |
| return (void *)(MMU_BLOCK50_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) |
| { |
| /* 8K page size for mappings both IRAM and DRAM */ |
| |
| REG_SET_FIELD(DPORT_IMMU_PAGE_MODE_REG, DPORT_IMMU_PAGE_MODE, 0); |
| REG_SET_FIELD(DPORT_DMMU_PAGE_MODE_REG, DPORT_DMMU_PAGE_MODE, 0); |
| |
| /* 1:1 mapping for IRAM */ |
| |
| REG_SET_FIELD(DPORT_IMMU_TABLE0_REG, DPORT_IMMU_TABLE0, 0x00); |
| REG_SET_FIELD(DPORT_IMMU_TABLE1_REG, DPORT_IMMU_TABLE1, 0x51); |
| REG_SET_FIELD(DPORT_IMMU_TABLE2_REG, DPORT_IMMU_TABLE2, 0x52); |
| REG_SET_FIELD(DPORT_IMMU_TABLE3_REG, DPORT_IMMU_TABLE3, 0x53); |
| REG_SET_FIELD(DPORT_IMMU_TABLE4_REG, DPORT_IMMU_TABLE4, 0x54); |
| REG_SET_FIELD(DPORT_IMMU_TABLE5_REG, DPORT_IMMU_TABLE5, 0x55); |
| REG_SET_FIELD(DPORT_IMMU_TABLE6_REG, DPORT_IMMU_TABLE6, 0x56); |
| REG_SET_FIELD(DPORT_IMMU_TABLE7_REG, DPORT_IMMU_TABLE7, 0x57); |
| REG_SET_FIELD(DPORT_IMMU_TABLE8_REG, DPORT_IMMU_TABLE8, 0x08); |
| REG_SET_FIELD(DPORT_IMMU_TABLE9_REG, DPORT_IMMU_TABLE9, 0x09); |
| REG_SET_FIELD(DPORT_IMMU_TABLE10_REG, DPORT_IMMU_TABLE10, 0x0a); |
| REG_SET_FIELD(DPORT_IMMU_TABLE11_REG, DPORT_IMMU_TABLE11, 0x0b); |
| REG_SET_FIELD(DPORT_IMMU_TABLE12_REG, DPORT_IMMU_TABLE12, 0x0c); |
| REG_SET_FIELD(DPORT_IMMU_TABLE13_REG, DPORT_IMMU_TABLE13, 0x0d); |
| REG_SET_FIELD(DPORT_IMMU_TABLE14_REG, DPORT_IMMU_TABLE14, 0x0e); |
| REG_SET_FIELD(DPORT_IMMU_TABLE15_REG, DPORT_IMMU_TABLE15, 0x0f); |
| |
| #ifdef CONFIG_ESP32_USER_DATA_EXTMEM |
| REG_SET_FIELD(DPORT_DMMU_TABLE0_REG, DPORT_DMMU_TABLE0, 0x00); |
| REG_SET_FIELD(DPORT_DMMU_TABLE1_REG, DPORT_DMMU_TABLE1, 0x01); |
| REG_SET_FIELD(DPORT_DMMU_TABLE2_REG, DPORT_DMMU_TABLE2, 0x02); |
| REG_SET_FIELD(DPORT_DMMU_TABLE3_REG, DPORT_DMMU_TABLE3, 0x03); |
| REG_SET_FIELD(DPORT_DMMU_TABLE4_REG, DPORT_DMMU_TABLE4, 0x04); |
| REG_SET_FIELD(DPORT_DMMU_TABLE5_REG, DPORT_DMMU_TABLE5, 0x05); |
| REG_SET_FIELD(DPORT_DMMU_TABLE6_REG, DPORT_DMMU_TABLE6, 0x06); |
| REG_SET_FIELD(DPORT_DMMU_TABLE7_REG, DPORT_DMMU_TABLE7, 0x07); |
| REG_SET_FIELD(DPORT_DMMU_TABLE8_REG, DPORT_DMMU_TABLE8, 0x08); |
| REG_SET_FIELD(DPORT_DMMU_TABLE9_REG, DPORT_DMMU_TABLE9, 0x09); |
| REG_SET_FIELD(DPORT_DMMU_TABLE10_REG, DPORT_DMMU_TABLE10, 0x0a); |
| REG_SET_FIELD(DPORT_DMMU_TABLE11_REG, DPORT_DMMU_TABLE11, 0x0b); |
| REG_SET_FIELD(DPORT_DMMU_TABLE12_REG, DPORT_DMMU_TABLE12, 0x0c); |
| REG_SET_FIELD(DPORT_DMMU_TABLE13_REG, DPORT_DMMU_TABLE13, 0x0d); |
| REG_SET_FIELD(DPORT_DMMU_TABLE14_REG, DPORT_DMMU_TABLE14, 0x0e); |
| REG_SET_FIELD(DPORT_DMMU_TABLE15_REG, DPORT_DMMU_TABLE15, 0x0f); |
| #else |
| REG_SET_FIELD(DPORT_DMMU_TABLE0_REG, DPORT_DMMU_TABLE0, 0x00); |
| REG_SET_FIELD(DPORT_DMMU_TABLE1_REG, DPORT_DMMU_TABLE1, 0x01); |
| REG_SET_FIELD(DPORT_DMMU_TABLE2_REG, DPORT_DMMU_TABLE2, 0x02); |
| REG_SET_FIELD(DPORT_DMMU_TABLE3_REG, DPORT_DMMU_TABLE3, 0x03); |
| REG_SET_FIELD(DPORT_DMMU_TABLE4_REG, DPORT_DMMU_TABLE4, 0x54); |
| REG_SET_FIELD(DPORT_DMMU_TABLE5_REG, DPORT_DMMU_TABLE5, 0x55); |
| REG_SET_FIELD(DPORT_DMMU_TABLE6_REG, DPORT_DMMU_TABLE6, 0x56); |
| REG_SET_FIELD(DPORT_DMMU_TABLE7_REG, DPORT_DMMU_TABLE7, 0x57); |
| REG_SET_FIELD(DPORT_DMMU_TABLE8_REG, DPORT_DMMU_TABLE8, 0x58); |
| REG_SET_FIELD(DPORT_DMMU_TABLE9_REG, DPORT_DMMU_TABLE9, 0x59); |
| REG_SET_FIELD(DPORT_DMMU_TABLE10_REG, DPORT_DMMU_TABLE10, 0x5a); |
| REG_SET_FIELD(DPORT_DMMU_TABLE11_REG, DPORT_DMMU_TABLE11, 0x5b); |
| REG_SET_FIELD(DPORT_DMMU_TABLE12_REG, DPORT_DMMU_TABLE12, 0x5c); |
| REG_SET_FIELD(DPORT_DMMU_TABLE13_REG, DPORT_DMMU_TABLE13, 0x5d); |
| REG_SET_FIELD(DPORT_DMMU_TABLE14_REG, DPORT_DMMU_TABLE14, 0x5e); |
| REG_SET_FIELD(DPORT_DMMU_TABLE15_REG, DPORT_DMMU_TABLE15, 0x5f); |
| #endif |
| |
| /* Configure interrupt vector addresses in PID Controller */ |
| |
| putreg32(PIDCTRL_INTERRUPT_ENABLE_M, PIDCTRL_INTERRUPT_ENABLE_REG); |
| |
| /* Configure the PID Controller to switch to privileged mode (PID 0) when |
| * the CPU fetches instruction from the following interrupt vectors: |
| * 1) Level 1 |
| * 2) Window Overflow 4 |
| * 3) Level 3 (SWINT) |
| * 4) Window Underflow 4 |
| * 5) Window Overflow 8 |
| * 6) Window Underflow 8 |
| * 7) NMI |
| */ |
| |
| putreg32(VECTORS_START + 0x340, PIDCTRL_INTERRUPT_ADDR_1_REG); |
| putreg32(VECTORS_START + 0x000, PIDCTRL_INTERRUPT_ADDR_2_REG); |
| putreg32(VECTORS_START + 0x1c0, PIDCTRL_INTERRUPT_ADDR_3_REG); |
| putreg32(VECTORS_START + 0x040, PIDCTRL_INTERRUPT_ADDR_4_REG); |
| putreg32(VECTORS_START + 0x080, PIDCTRL_INTERRUPT_ADDR_5_REG); |
| putreg32(VECTORS_START + 0x0c0, PIDCTRL_INTERRUPT_ADDR_6_REG); |
| putreg32(VECTORS_START + 0x2c0, PIDCTRL_INTERRUPT_ADDR_7_REG); |
| |
| putreg32(0, PIDCTRL_LEVEL_REG); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: esp32_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 esp32_userspace(void) |
| { |
| uint8_t *dest; |
| uint8_t *end; |
| |
| /* Load IROM and DROM information from image header */ |
| |
| load_header(); |
| |
| /* Configure the Flash MMU for enabling access to the userspace image */ |
| |
| configure_flash_mmu(); |
| |
| #ifdef CONFIG_ESP32_USER_DATA_EXTMEM |
| if (esp_spiram_init() != OK) |
| { |
| mwarn("SPI RAM initialization failed!\n"); |
| |
| PANIC(); |
| } |
| |
| /* Configure the SRAM MMU for enabling access to the userspace image */ |
| |
| configure_sram_mmu(); |
| #endif |
| |
| /* 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 to grant access to the userspace */ |
| |
| configure_mpu(); |
| } |
| |
| #endif /* CONFIG_BUILD_PROTECTED */ |