Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:42

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup aarch64_start
0007  *
0008  * @brief AArch64 MMU configuration.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2021 On-Line Applications Research Corporation (OAR)
0013  * Written by Kinsey Moore <kinsey.moore@oarcorp.com>
0014  *
0015  * Redistribution and use in source and binary forms, with or without
0016  * modification, are permitted provided that the following conditions
0017  * are met:
0018  * 1. Redistributions of source code must retain the above copyright
0019  *    notice, this list of conditions and the following disclaimer.
0020  * 2. Redistributions in binary form must reproduce the above copyright
0021  *    notice, this list of conditions and the following disclaimer in the
0022  *    documentation and/or other materials provided with the distribution.
0023  *
0024  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0025  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0027  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0028  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0029  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0030  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0031  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0032  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0033  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0034  * POSSIBILITY OF SUCH DAMAGE.
0035  */
0036 
0037 #include <bsp/aarch64-mmu.h>
0038 
0039 #include <bsp/fatal.h>
0040 #include <bsp/linker-symbols.h>
0041 #include <bsp/start.h>
0042 #include <rtems/score/aarch64-system-registers.h>
0043 #include <rtems/score/assert.h>
0044 
0045 aarch64_mmu_control aarch64_mmu_instance = {
0046   .ttb = (uint64_t *) bsp_translation_table_base,
0047 
0048   /* One page table is used for the initial setup at the base */
0049   .used_page_tables = 1
0050 };
0051 
0052 /* AArch64 uses levels 0, 1, 2, and 3 */
0053 #define MMU_MAX_SUBTABLE_PAGE_BITS ( 3 * MMU_BITS_PER_LEVEL + MMU_PAGE_BITS )
0054 
0055 /* setup straight mapped block entries */
0056 BSP_START_TEXT_SECTION static inline void aarch64_mmu_page_table_set_blocks(
0057   uint64_t *page_table,
0058   uint64_t base,
0059   uint32_t bits_offset,
0060   uint64_t default_attr
0061 )
0062 {
0063   uint64_t page_flag = 0;
0064 
0065   if ( bits_offset == MMU_PAGE_BITS ) {
0066     page_flag = MMU_DESC_TYPE_PAGE;
0067   }
0068 
0069   for ( uint64_t i = 0; i < ( 1 << MMU_BITS_PER_LEVEL ); i++ ) {
0070     page_table[i] = base | ( i << bits_offset );
0071     page_table[i] |= default_attr | page_flag;
0072   }
0073 }
0074 
0075 BSP_START_TEXT_SECTION static inline uint64_t *
0076 aarch64_mmu_page_table_alloc( aarch64_mmu_control *control )
0077 {
0078   size_t used_page_tables = control->used_page_tables;
0079 
0080   if ( used_page_tables >= AARCH64_MMU_TRANSLATION_TABLE_PAGES ) {
0081     return NULL;
0082   }
0083 
0084   control->used_page_tables = used_page_tables + 1;
0085   return (uint64_t *)
0086     ( (uintptr_t) control->ttb + ( used_page_tables << MMU_PAGE_BITS ) );
0087 }
0088 
0089 BSP_START_TEXT_SECTION static inline uintptr_t aarch64_mmu_get_index(
0090   uintptr_t root_address,
0091   uintptr_t vaddr,
0092   uint32_t shift
0093 )
0094 {
0095   uintptr_t mask = ( 1 << ( MMU_BITS_PER_LEVEL + 1 ) ) - 1;
0096 
0097   return ( ( vaddr - root_address ) >> shift ) & mask;
0098 }
0099 
0100 BSP_START_TEXT_SECTION static uint64_t *
0101 aarch64_mmu_get_sub_table(
0102   aarch64_mmu_control *control,
0103   uint64_t *page_table_entry,
0104   uintptr_t physical_root_address,
0105   uint32_t shift
0106 )
0107 {
0108   /* check if the index already has a page table */
0109   if ( ( *page_table_entry & MMU_DESC_TYPE_TABLE ) == MMU_DESC_TYPE_TABLE ) {
0110     /* extract page table address */
0111     uint64_t table_pointer = *page_table_entry & MMU_DESC_PAGE_TABLE_MASK;
0112     /* This cast should be safe since the address was inserted in this mode */
0113     return (uint64_t *) (uintptr_t) table_pointer;
0114   }
0115 
0116   /* allocate new page table and set block */
0117   uint64_t *sub_table = aarch64_mmu_page_table_alloc( control );
0118 
0119   if ( sub_table == NULL ) {
0120     return NULL;
0121   }
0122 
0123   aarch64_mmu_page_table_set_blocks(
0124     sub_table,
0125     physical_root_address,
0126     shift - MMU_BITS_PER_LEVEL,
0127     *page_table_entry & ~MMU_DESC_PAGE_TABLE_MASK
0128   );
0129   *page_table_entry = (uintptr_t) sub_table;
0130   *page_table_entry |= MMU_DESC_TYPE_TABLE | MMU_DESC_VALID;
0131 
0132   return sub_table;
0133 }
0134 
0135 BSP_START_TEXT_SECTION static inline rtems_status_code aarch64_mmu_map_block(
0136   aarch64_mmu_control *control,
0137   uint64_t *page_table,
0138   uint64_t root_address,
0139   uint64_t addr,
0140   uint64_t size,
0141   int8_t level,
0142   uint64_t flags
0143 )
0144 {
0145   uint32_t shift = ( 2 - level ) * MMU_BITS_PER_LEVEL + MMU_PAGE_BITS;
0146   uint64_t granularity = 1LLU << shift;
0147 
0148   do {
0149     uintptr_t index = aarch64_mmu_get_index( root_address, addr, shift );
0150     uint64_t block_bottom = RTEMS_ALIGN_DOWN( addr, granularity );
0151     uint64_t chunk_size = granularity;
0152 
0153     /* check for perfect block match */
0154     if ( block_bottom == addr ) {
0155       if ( size >= chunk_size ) {
0156         /* level -1 can't contain block descriptors, fall through to subtable */
0157         if ( level != -1 ) {
0158           uint64_t page_flag = 0;
0159 
0160           if ( level == 2 ) {
0161             page_flag = MMU_DESC_TYPE_PAGE;
0162           }
0163 
0164           /* when page_flag is set the last level must be a page descriptor */
0165           if ( page_flag || ( page_table[index] & MMU_DESC_TYPE_TABLE ) != MMU_DESC_TYPE_TABLE ) {
0166             /* no sub-table, apply block properties */
0167             page_table[index] = addr | flags | page_flag;
0168             size -= chunk_size;
0169             addr += chunk_size;
0170             continue;
0171           }
0172         }
0173       } else {
0174         /*
0175          * Block starts on a boundary, but is short.
0176          *
0177          * The size is >= MMU_PAGE_SIZE since
0178          * aarch64_mmu_set_translation_table_entries() aligns the memory region
0179          * to page boundaries.  The minimum chunk_size is MMU_PAGE_SIZE.
0180          */
0181         _Assert( level < 2 );
0182         chunk_size = size;
0183       }
0184     } else {
0185       uintptr_t block_top = RTEMS_ALIGN_UP( addr, granularity );
0186       chunk_size = block_top - addr;
0187 
0188       if ( chunk_size > size ) {
0189         chunk_size = size;
0190       }
0191     }
0192 
0193     /* Deal with any subtable modification  */
0194     uint64_t new_root_address = root_address + index * granularity;
0195     rtems_status_code sc;
0196 
0197     uint64_t *sub_table = aarch64_mmu_get_sub_table(
0198       control,
0199       &page_table[index],
0200       new_root_address,
0201       shift
0202     );
0203 
0204     if ( sub_table == NULL ) {
0205       return RTEMS_TOO_MANY;
0206     }
0207 
0208     sc = aarch64_mmu_map_block(
0209       control,
0210       sub_table,
0211       new_root_address,
0212       addr,
0213       chunk_size,
0214       level + 1,
0215       flags
0216     );
0217 
0218     if ( sc != RTEMS_SUCCESSFUL ) {
0219       return sc;
0220     }
0221 
0222     size -= chunk_size;
0223     addr += chunk_size;
0224   } while ( size > 0 );
0225 
0226   return RTEMS_SUCCESSFUL;
0227 }
0228 /* Get the maximum number of bits supported by this hardware */
0229 BSP_START_TEXT_SECTION static inline uint64_t
0230 aarch64_mmu_get_cpu_pa_bits( void )
0231 {
0232 #ifdef AARCH64_MMU_PHYSICAL_ADDRESS_RANGE_BITS
0233   return AARCH64_MMU_PHYSICAL_ADDRESS_RANGE_BITS;
0234 #else
0235   uint64_t id_reg = _AArch64_Read_id_aa64mmfr0_el1();
0236 
0237   switch ( AARCH64_ID_AA64MMFR0_EL1_PARANGE_GET( id_reg ) ) {
0238   case 0:
0239       return 32;
0240   case 1:
0241       return 36;
0242   case 2:
0243       return 40;
0244   case 3:
0245       return 42;
0246   case 4:
0247       return 44;
0248   case 5:
0249       return 48;
0250   case 6:
0251       return 52;
0252   default:
0253       return 48;
0254   }
0255   return 48;
0256 #endif
0257 }
0258 
0259 BSP_START_TEXT_SECTION rtems_status_code
0260 aarch64_mmu_set_translation_table_entries(
0261   aarch64_mmu_control *control,
0262   const aarch64_mmu_config_entry *config
0263 )
0264 {
0265   uint64_t max_mappable = 1LLU << aarch64_mmu_get_cpu_pa_bits();
0266   /* Align to page boundaries */
0267   uintptr_t begin = RTEMS_ALIGN_DOWN( config->begin, MMU_PAGE_SIZE );
0268   uintptr_t end = RTEMS_ALIGN_UP( config->end, MMU_PAGE_SIZE );
0269   uintptr_t size = end - begin;
0270 
0271   if ( config->begin == config->end ) {
0272     return RTEMS_SUCCESSFUL;
0273   }
0274 
0275   if ( begin >= max_mappable ) {
0276     return RTEMS_INVALID_ADDRESS;
0277   }
0278 
0279   if ( size > max_mappable - begin ) {
0280     return RTEMS_INVALID_SIZE;
0281   }
0282 
0283   return aarch64_mmu_map_block(
0284     control,
0285     control->ttb,
0286     0x0,
0287     begin,
0288     size,
0289     -1,
0290     config->flags
0291   );
0292 }
0293 
0294 BSP_START_TEXT_SECTION void aarch64_mmu_setup_translation_table(
0295   aarch64_mmu_control *control,
0296   const aarch64_mmu_config_entry *config_table,
0297   size_t config_count
0298 )
0299 {
0300   size_t i;
0301 
0302   aarch64_mmu_page_table_set_blocks(
0303     control->ttb,
0304     (uintptr_t) NULL,
0305     MMU_MAX_SUBTABLE_PAGE_BITS,
0306     0
0307   );
0308 
0309   /* Configure entries required for each memory section */
0310   for ( i = 0; i < config_count; ++i ) {
0311     rtems_status_code sc;
0312 
0313     sc = aarch64_mmu_set_translation_table_entries( control, &config_table[i] );
0314 
0315     if ( sc != RTEMS_SUCCESSFUL ) {
0316       bsp_fatal( AARCH64_FATAL_MMU_CANNOT_MAP_BLOCK );
0317     }
0318   }
0319 }