RTEMS 6.1-rc1
aarch64-mmu.h
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-2-Clause */
2
11/*
12 * Copyright (C) 2021 On-Line Applications Research Corporation (OAR)
13 * Written by Kinsey Moore <kinsey.moore@oarcorp.com>
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#ifndef LIBBSP_AARCH64_SHARED_AARCH64_MMU_H
38#define LIBBSP_AARCH64_SHARED_AARCH64_MMU_H
39
40#include <bsp/fatal.h>
41#include <bsp/linker-symbols.h>
42#include <bsp/start.h>
43#include <bsp/utility.h>
44#include <bspopts.h>
47
48#ifdef __cplusplus
49extern "C" {
50#endif /* __cplusplus */
51
52/* AArch64 uses levels 0, 1, 2, and 3 */
53#define MMU_MAX_SUBTABLE_PAGE_BITS ( 3 * MMU_BITS_PER_LEVEL + MMU_PAGE_BITS )
54
55typedef struct {
56 uintptr_t begin;
57 uintptr_t end;
58 uint64_t flags;
60
61#define AARCH64_MMU_DEFAULT_SECTIONS \
62 { \
63 .begin = (uintptr_t) bsp_section_fast_text_begin, \
64 .end = (uintptr_t) bsp_section_fast_text_end, \
65 .flags = AARCH64_MMU_CODE_CACHED \
66 }, { \
67 .begin = (uintptr_t) bsp_section_fast_data_begin, \
68 .end = (uintptr_t) bsp_section_fast_data_end, \
69 .flags = AARCH64_MMU_DATA_RW_CACHED \
70 }, { \
71 .begin = (uintptr_t) bsp_section_start_begin, \
72 .end = (uintptr_t) bsp_section_start_end, \
73 .flags = AARCH64_MMU_CODE_CACHED \
74 }, { \
75 .begin = (uintptr_t) bsp_section_vector_begin, \
76 .end = (uintptr_t) bsp_section_vector_end, \
77 .flags = AARCH64_MMU_DATA_RW_CACHED \
78 }, { \
79 .begin = (uintptr_t) bsp_section_text_begin, \
80 .end = (uintptr_t) bsp_section_text_end, \
81 .flags = AARCH64_MMU_CODE_CACHED \
82 }, { \
83 .begin = (uintptr_t) bsp_section_rodata_begin, \
84 .end = (uintptr_t) bsp_section_rodata_end, \
85 .flags = AARCH64_MMU_DATA_RO_CACHED \
86 }, { \
87 .begin = (uintptr_t) bsp_section_data_begin, \
88 .end = (uintptr_t) bsp_section_data_end, \
89 .flags = AARCH64_MMU_DATA_RW_CACHED \
90 }, { \
91 .begin = (uintptr_t) bsp_section_bss_begin, \
92 .end = (uintptr_t) bsp_section_bss_end, \
93 .flags = AARCH64_MMU_DATA_RW_CACHED \
94 }, { \
95 .begin = (uintptr_t) bsp_section_rtemsstack_begin, \
96 .end = (uintptr_t) bsp_section_rtemsstack_end, \
97 .flags = AARCH64_MMU_DATA_RW_CACHED \
98 }, { \
99 .begin = (uintptr_t) bsp_section_noinit_begin, \
100 .end = (uintptr_t) bsp_section_noinit_end, \
101 .flags = AARCH64_MMU_DATA_RW_CACHED \
102 }, { \
103 .begin = (uintptr_t) bsp_section_work_begin, \
104 .end = (uintptr_t) bsp_section_work_end, \
105 .flags = AARCH64_MMU_DATA_RW_CACHED \
106 }, { \
107 .begin = (uintptr_t) bsp_section_stack_begin, \
108 .end = (uintptr_t) bsp_section_stack_end, \
109 .flags = AARCH64_MMU_DATA_RW_CACHED \
110 }, { \
111 .begin = (uintptr_t) bsp_section_nocache_begin, \
112 .end = (uintptr_t) bsp_section_nocache_end, \
113 .flags = AARCH64_MMU_DEVICE \
114 }, { \
115 .begin = (uintptr_t) bsp_section_nocachenoload_begin, \
116 .end = (uintptr_t) bsp_section_nocachenoload_end, \
117 .flags = AARCH64_MMU_DEVICE \
118 }, { \
119 .begin = (uintptr_t) bsp_translation_table_base, \
120 .end = (uintptr_t) bsp_translation_table_end, \
121 .flags = AARCH64_MMU_DATA_RW_CACHED \
122 }, { \
123/*
124 * The vector table must be in writable and executable memory as it stores both
125 * exception code and the mutable pointer to which it jumps
126 */ \
127 .begin = (uintptr_t) bsp_start_vector_table_begin, \
128 .end = (uintptr_t) bsp_start_vector_table_end, \
129 .flags = AARCH64_MMU_CODE_RW_CACHED \
130 }
131
132/* setup straight mapped block entries */
133BSP_START_TEXT_SECTION static inline void aarch64_mmu_page_table_set_blocks(
134 uint64_t *page_table,
135 uint64_t base,
136 uint32_t bits_offset,
137 uint64_t default_attr
138)
139{
140 uint64_t page_flag = 0;
141
142 if ( bits_offset == MMU_PAGE_BITS ) {
143 page_flag = MMU_DESC_TYPE_PAGE;
144 }
145
146 for ( uint64_t i = 0; i < ( 1 << MMU_BITS_PER_LEVEL ); i++ ) {
147 page_table[i] = base | ( i << bits_offset );
148 page_table[i] |= default_attr | page_flag;
149 }
150}
151
152BSP_START_TEXT_SECTION static inline rtems_status_code
153aarch64_mmu_page_table_alloc( uint64_t **page_table )
154{
155 /* First page table is already in use as TTB0 */
156 static uintptr_t current_page_table =
157 (uintptr_t) bsp_translation_table_base;
158
159 current_page_table += MMU_PAGE_SIZE;
160 *page_table = (uint64_t *) current_page_table;
161
162 /* Out of linker-allocated page tables? */
163 uintptr_t consumed_pages = (uintptr_t) current_page_table;
164 consumed_pages -= (uintptr_t) bsp_translation_table_base;
165 consumed_pages /= MMU_PAGE_SIZE;
166
167 if ( consumed_pages > AARCH64_MMU_TRANSLATION_TABLE_PAGES ) {
168 *page_table = NULL;
169 return RTEMS_NO_MEMORY;
170 }
171
172 return RTEMS_SUCCESSFUL;
173}
174
175BSP_START_TEXT_SECTION static inline uintptr_t aarch64_mmu_get_index(
176 uintptr_t root_address,
177 uintptr_t vaddr,
178 uint32_t shift
179)
180{
181 uintptr_t mask = ( 1 << ( MMU_BITS_PER_LEVEL + 1 ) ) - 1;
182
183 return ( ( vaddr - root_address ) >> shift ) & mask;
184}
185
186BSP_START_TEXT_SECTION static inline rtems_status_code
187aarch64_mmu_get_sub_table(
188 uint64_t *page_table_entry,
189 uint64_t **sub_table,
190 uintptr_t physical_root_address,
191 uint32_t shift
192)
193{
194 /* check if the index already has a page table */
195 if ( ( *page_table_entry & MMU_DESC_TYPE_TABLE ) == MMU_DESC_TYPE_TABLE ) {
196 /* extract page table address */
197 uint64_t table_pointer = *page_table_entry & MMU_DESC_PAGE_TABLE_MASK;
198 /* This cast should be safe since the address was inserted in this mode */
199 *sub_table = (uint64_t *) (uintptr_t) table_pointer;
200 } else {
201 /* allocate new page table and set block */
202 rtems_status_code sc = aarch64_mmu_page_table_alloc( sub_table );
203
204 if ( sc != RTEMS_SUCCESSFUL ) {
205 return sc;
206 }
207
208 aarch64_mmu_page_table_set_blocks(
209 *sub_table,
210 physical_root_address,
211 shift - MMU_BITS_PER_LEVEL,
212 *page_table_entry & ~MMU_DESC_PAGE_TABLE_MASK
213 );
214 *page_table_entry = (uintptr_t) *sub_table;
215 *page_table_entry |= MMU_DESC_TYPE_TABLE | MMU_DESC_VALID;
216 }
217
218 return RTEMS_SUCCESSFUL;
219}
220
221BSP_START_TEXT_SECTION static inline rtems_status_code aarch64_mmu_map_block(
222 uint64_t *page_table,
223 uint64_t root_address,
224 uint64_t addr,
225 uint64_t size,
226 int8_t level,
227 uint64_t flags
228)
229{
230 uint32_t shift = ( 2 - level ) * MMU_BITS_PER_LEVEL + MMU_PAGE_BITS;
231 uint64_t granularity = 1LLU << shift;
232 uint64_t page_flag = 0;
233
234 if ( level == 2 ) {
235 page_flag = MMU_DESC_TYPE_PAGE;
236 }
237
238 while ( size > 0 ) {
239 uintptr_t index = aarch64_mmu_get_index( root_address, addr, shift );
240 uint64_t block_bottom = RTEMS_ALIGN_DOWN( addr, granularity );
241 uint64_t chunk_size = granularity;
242
243 /* check for perfect block match */
244 if ( block_bottom == addr ) {
245 if ( size >= chunk_size ) {
246 /* level -1 can't contain block descriptors, fall through to subtable */
247 if ( level != -1 ) {
248 /* when page_flag is set the last level must be a page descriptor */
249 if ( page_flag || ( page_table[index] & MMU_DESC_TYPE_TABLE ) != MMU_DESC_TYPE_TABLE ) {
250 /* no sub-table, apply block properties */
251 page_table[index] = addr | flags | page_flag;
252 size -= chunk_size;
253 addr += chunk_size;
254 continue;
255 }
256 }
257 } else {
258 /* block starts on a boundary, but is short */
259 chunk_size = size;
260
261 /* it isn't possible to go beyond page table level 2 */
262 if ( page_flag ) {
263 /* no sub-table, apply block properties */
264 page_table[index] = addr | flags | page_flag;
265 size -= chunk_size;
266 addr += chunk_size;
267 continue;
268 }
269 }
270 } else {
271 uintptr_t block_top = RTEMS_ALIGN_UP( addr, granularity );
272 chunk_size = block_top - addr;
273
274 if ( chunk_size > size ) {
275 chunk_size = size;
276 }
277 }
278
279 /* Deal with any subtable modification */
280 uint64_t new_root_address = root_address + index * granularity;
281 uint64_t *sub_table = NULL;
283
284 sc = aarch64_mmu_get_sub_table(
285 &page_table[index],
286 &sub_table,
287 new_root_address,
288 shift
289 );
290
291 if ( sc != RTEMS_SUCCESSFUL ) {
292 return sc;
293 }
294
295 sc = aarch64_mmu_map_block(
296 sub_table,
297 new_root_address,
298 addr,
299 chunk_size,
300 level + 1,
301 flags
302 );
303
304 if ( sc != RTEMS_SUCCESSFUL ) {
305 return sc;
306 }
307
308 size -= chunk_size;
309 addr += chunk_size;
310 }
311
312 return RTEMS_SUCCESSFUL;
313}
314
315BSP_START_DATA_SECTION extern const aarch64_mmu_config_entry
316 aarch64_mmu_config_table[];
317
318BSP_START_DATA_SECTION extern const size_t
319 aarch64_mmu_config_table_size;
320
321/* Get the maximum number of bits supported by this hardware */
322BSP_START_TEXT_SECTION static inline uint64_t
323aarch64_mmu_get_cpu_pa_bits( void )
324{
325 uint64_t id_reg = _AArch64_Read_id_aa64mmfr0_el1();
326
327 switch ( AARCH64_ID_AA64MMFR0_EL1_PARANGE_GET( id_reg ) ) {
328 case 0:
329 return 32;
330 case 1:
331 return 36;
332 case 2:
333 return 40;
334 case 3:
335 return 42;
336 case 4:
337 return 44;
338 case 5:
339 return 48;
340 case 6:
341 return 52;
342 default:
343 return 48;
344 }
345 return 48;
346}
347
348BSP_START_TEXT_SECTION static inline void
349aarch64_mmu_set_translation_table_entries(
350 uint64_t *ttb,
352)
353{
354 /* Force alignemnt to 4k page size */
355 uintptr_t begin = RTEMS_ALIGN_DOWN( config->begin, MMU_PAGE_SIZE );
356 uintptr_t end = RTEMS_ALIGN_UP( config->end, MMU_PAGE_SIZE );
357 uint64_t max_mappable = 1LLU << aarch64_mmu_get_cpu_pa_bits();
359
360 if ( begin >= max_mappable || end > max_mappable ) {
361 bsp_fatal( BSP_FATAL_MMU_ADDRESS_INVALID );
362 }
363
364 sc = aarch64_mmu_map_block(
365 ttb,
366 0x0,
367 begin,
368 end - begin,
369 -1,
370 config->flags
371 );
372
373 if ( sc != RTEMS_SUCCESSFUL ) {
375 }
376}
377
378BSP_START_TEXT_SECTION static inline void aarch64_mmu_setup_translation_table(
379 const aarch64_mmu_config_entry *config_table,
380 size_t config_count
381)
382{
383 size_t i;
384 uint64_t *ttb = (uint64_t *) bsp_translation_table_base;
385
386 aarch64_mmu_page_table_set_blocks(
387 ttb,
388 (uintptr_t) NULL,
389 MMU_MAX_SUBTABLE_PAGE_BITS,
390 0
391 );
392
393 _AArch64_Write_ttbr0_el1( (uintptr_t) ttb );
394
395 /* Configure entries required for each memory section */
396 for ( i = 0; i < config_count; ++i ) {
397 aarch64_mmu_set_translation_table_entries( ttb, &config_table[i] );
398 }
399}
400
401BSP_START_TEXT_SECTION static inline void
402aarch64_mmu_enable( void )
403{
404 uint64_t sctlr;
405
406 /* CPUECTLR_EL1.SMPEN is already set on ZynqMP and is not writable */
407
408 /* Flush and invalidate cache */
411
412 /* Enable MMU and cache */
413 sctlr = _AArch64_Read_sctlr_el1();
414 sctlr |= AARCH64_SCTLR_EL1_I | AARCH64_SCTLR_EL1_C | AARCH64_SCTLR_EL1_M;
415 _AArch64_Write_sctlr_el1( sctlr );
416}
417
418BSP_START_TEXT_SECTION static inline void
419aarch64_mmu_disable( void )
420{
421 uint64_t sctlr;
422
423 /*
424 * Flush data cache before disabling the MMU. While the MMU is disabled, all
425 * accesses are treated as uncached device memory.
426 */
428
429 /* Disable MMU */
430 sctlr = _AArch64_Read_sctlr_el1();
431 sctlr &= ~(AARCH64_SCTLR_EL1_M);
432 _AArch64_Write_sctlr_el1( sctlr );
433}
434
435BSP_START_TEXT_SECTION static inline void aarch64_mmu_setup( void )
436{
437 /* Set TCR */
438 /* 256TB/48 bits mappable (64-0x10) */
439 _AArch64_Write_tcr_el1(
440 AARCH64_TCR_EL1_T0SZ( 0x10 ) | AARCH64_TCR_EL1_IRGN0( 0x1 ) |
441 AARCH64_TCR_EL1_ORGN0( 0x1 ) | AARCH64_TCR_EL1_SH0( 0x3 ) |
442 AARCH64_TCR_EL1_TG0( 0x0 ) | AARCH64_TCR_EL1_IPS( 0x5ULL ) |
443 AARCH64_TCR_EL1_EPD1
444 );
445
446 /* Set MAIR */
447 _AArch64_Write_mair_el1(
448 AARCH64_MAIR_EL1_ATTR0( 0x0 ) | AARCH64_MAIR_EL1_ATTR1( 0x4 ) |
449 AARCH64_MAIR_EL1_ATTR2( 0x44 ) | AARCH64_MAIR_EL1_ATTR3( 0xFF )
450 );
451}
452
453#ifdef __cplusplus
454}
455#endif /* __cplusplus */
456
457#endif /* LIBBSP_AARCH64_SHARED_AARCH64_MMU_H */
This header file provides the API to read and write the AArch64 system registers.
This header file provides fatal codes for RTEMS_FATAL_SOURCE_BSP.
#define RTEMS_ALIGN_UP(_value, _alignment)
Aligns up the value to the alignment.
Definition: basedefs.h:141
#define RTEMS_ALIGN_DOWN(_value, _alignment)
Aligns down the value to the alignment.
Definition: basedefs.h:123
void rtems_cache_invalidate_entire_data(void)
Invalidates the entire data cache.
Definition: cacheimpl.h:228
void rtems_cache_flush_entire_data(void)
Flushes the entire data cache.
Definition: cacheimpl.h:213
RTEMS_NO_RETURN void rtems_fatal_error_occurred(uint32_t fatal_code)
Invokes the fatal error handler.
Definition: fatal.c:47
rtems_status_code
This enumeration provides status codes for directives of the Classic API.
Definition: status.h:85
@ RTEMS_SUCCESSFUL
This status code indicates successful completion of a requested operation.
Definition: status.h:90
@ RTEMS_NO_MEMORY
This status code indicates that the directive attempted to allocate memory but was unable to do so.
Definition: status.h:236
#define NULL
Requests a GPIO pin group configuration.
Definition: xil_types.h:54
This header file provides utility macros for BSPs.
Definitions used in MMU setup.
Definition: aarch64-mmu.h:55
Definition: deflate.c:114
Definition: i386.h:673
unsigned size
Definition: tte.h:1
Definition: i386.h:649