rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame^] | 1 | #include <asm.h> |
| 2 | #include <arch/arm64/mmu.h> |
| 3 | #include <arch/asm_macros.h> |
| 4 | #include <arch/arch_ops.h> |
| 5 | #include <kernel/vm.h> |
| 6 | |
| 7 | /* |
| 8 | * Register use: |
| 9 | * x0-x3 Arguments |
| 10 | * x9-x15 Scratch |
| 11 | * x19-x28 Globals |
| 12 | */ |
| 13 | tmp .req x9 |
| 14 | tmp2 .req x10 |
| 15 | wtmp2 .req w10 |
| 16 | idx .req x11 |
| 17 | idx_shift .req x12 |
| 18 | page_table .req x13 |
| 19 | new_page_table .req x14 |
| 20 | phys_offset .req x15 |
| 21 | |
| 22 | cpuid .req x19 |
| 23 | page_table0 .req x20 |
| 24 | page_table1 .req x21 |
| 25 | mmu_initial_mapping .req x22 |
| 26 | vaddr .req x23 |
| 27 | paddr .req x24 |
| 28 | mapping_size .req x25 |
| 29 | size .req x26 |
| 30 | attr .req x27 |
| 31 | |
| 32 | .section .text.boot |
| 33 | FUNCTION(_start) |
| 34 | .globl arm_reset |
| 35 | arm_reset: |
| 36 | |
| 37 | bl setup_el2_or_el3_exception_base |
| 38 | |
| 39 | mrs tmp, CurrentEL |
| 40 | cmp tmp, #(0b11 << 2) |
| 41 | b.ne .Lsetup_el2_or_el3_stack |
| 42 | |
| 43 | /* el3 set secure timer */ |
| 44 | ldr tmp2, =13000000 |
| 45 | msr cntfrq_el0, tmp2 |
| 46 | |
| 47 | /* el3 enable smp bit */ |
| 48 | mrs tmp2, s3_1_c15_c2_1 |
| 49 | orr tmp2, tmp2, #(1<<6) |
| 50 | msr s3_1_c15_c2_1, tmp2 |
| 51 | |
| 52 | .Lsetup_el2_or_el3_stack: |
| 53 | /* set el2 or el3 stack pointer */ |
| 54 | ldr tmp2, = __stack_end |
| 55 | mov sp, tmp2 |
| 56 | |
| 57 | /* initialization required in EL3. weak symbol at asm.S */ |
| 58 | cmp tmp, #(0b11 << 2) |
| 59 | b.ne .LelX_to_el1 |
| 60 | bl platform_el3_init |
| 61 | |
| 62 | .LelX_to_el1: |
| 63 | /* change to el1 */ |
| 64 | bl arm64_elX_to_el1 |
| 65 | |
| 66 | #if WITH_KERNEL_VM |
| 67 | /* enable caches so atomics and spinlocks work */ |
| 68 | mrs tmp, sctlr_el1 |
| 69 | orr tmp, tmp, #(1<<12) /* Enable icache */ |
| 70 | orr tmp, tmp, #(1<<2) /* Enable dcache/ucache */ |
| 71 | bic tmp, tmp, #(1<<3) /* Disable Stack Alignment Check */ /* TODO: don't use unaligned stacks */ |
| 72 | msr sctlr_el1, tmp |
| 73 | |
| 74 | /* set up the mmu according to mmu_initial_mappings */ |
| 75 | |
| 76 | /* load the base of the translation table and clear the table */ |
| 77 | adrp page_table1, arm64_kernel_translation_table |
| 78 | add page_table1, page_table1, #:lo12:arm64_kernel_translation_table |
| 79 | |
| 80 | /* Prepare tt_trampoline page table */ |
| 81 | /* Calculate pagetable physical addresses */ |
| 82 | adrp page_table0, tt_trampoline |
| 83 | add page_table0, page_table0, #:lo12:tt_trampoline |
| 84 | |
| 85 | #if WITH_SMP |
| 86 | mrs cpuid, mpidr_el1 |
| 87 | ubfx cpuid, cpuid, #0, #SMP_CPU_ID_BITS |
| 88 | cbnz cpuid, .Lmmu_enable_secondary |
| 89 | #endif |
| 90 | |
| 91 | mov tmp, #0 |
| 92 | |
| 93 | /* walk through all the entries in the translation table, setting them up */ |
| 94 | .Lclear_top_page_table_loop: |
| 95 | str xzr, [page_table1, tmp, lsl #3] |
| 96 | add tmp, tmp, #1 |
| 97 | cmp tmp, #MMU_KERNEL_PAGE_TABLE_ENTRIES_TOP |
| 98 | bne .Lclear_top_page_table_loop |
| 99 | |
| 100 | /* load the address of the mmu_initial_mappings table and start processing */ |
| 101 | adrp mmu_initial_mapping, mmu_initial_mappings |
| 102 | add mmu_initial_mapping, mmu_initial_mapping, #:lo12:mmu_initial_mappings |
| 103 | |
| 104 | .Linitial_mapping_loop: |
| 105 | /* Read entry of mmu_initial_mappings (likely defined in platform.c) */ |
| 106 | ldp paddr, vaddr, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_PHYS_OFFSET] |
| 107 | ldp size, tmp, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_SIZE_OFFSET] |
| 108 | |
| 109 | tbzmask tmp, MMU_INITIAL_MAPPING_FLAG_DYNAMIC, .Lnot_dynamic |
| 110 | adr paddr, _start |
| 111 | mov size, x0 |
| 112 | str paddr, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_PHYS_OFFSET] |
| 113 | str size, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_SIZE_OFFSET] |
| 114 | |
| 115 | .Lnot_dynamic: |
| 116 | /* if size == 0, end of list, done with initial mapping */ |
| 117 | cbz size, .Linitial_mapping_done |
| 118 | mov mapping_size, size |
| 119 | |
| 120 | /* set up the flags */ |
| 121 | tbzmask tmp, MMU_INITIAL_MAPPING_FLAG_UNCACHED, .Lnot_uncached |
| 122 | ldr attr, =MMU_INITIAL_MAP_STRONGLY_ORDERED |
| 123 | b .Lmem_type_done |
| 124 | |
| 125 | .Lnot_uncached: |
| 126 | /* is this memory mapped to device/peripherals? */ |
| 127 | tbzmask tmp, MMU_INITIAL_MAPPING_FLAG_DEVICE, .Lnot_device |
| 128 | ldr attr, =MMU_INITIAL_MAP_DEVICE |
| 129 | b .Lmem_type_done |
| 130 | .Lnot_device: |
| 131 | |
| 132 | /* Determine the segment in which the memory resides and set appropriate |
| 133 | * attributes. In order to handle offset kernels, the following rules are |
| 134 | * implemented below: |
| 135 | * KERNEL_BASE to __code_start -read/write (see note below) |
| 136 | * __code_start to __rodata_start (.text) -read only |
| 137 | * __rodata_start to __data_start (.rodata) -read only, execute never |
| 138 | * __data_start to ..... (.data) -read/write |
| 139 | * |
| 140 | * The space below __code_start is presently left as read/write (same as .data) |
| 141 | * mainly as a workaround for the raspberry pi boot process. Boot vectors for |
| 142 | * secondary CPUs are in this area and need to be updated by cpu0 once the system |
| 143 | * is ready to boot the secondary processors. |
| 144 | * TODO: handle this via mmu_initial_mapping entries, which may need to be |
| 145 | * extended with additional flag types |
| 146 | */ |
| 147 | .Lmapping_size_loop: |
| 148 | ldr attr, =MMU_PTE_KERNEL_DATA_FLAGS |
| 149 | ldr tmp, =__code_start |
| 150 | subs size, tmp, vaddr |
| 151 | /* If page is below the entry point (_start) mark as kernel data */ |
| 152 | b.hi .Lmem_type_done |
| 153 | |
| 154 | ldr attr, =MMU_PTE_KERNEL_RO_FLAGS |
| 155 | ldr tmp, =__rodata_start |
| 156 | subs size, tmp, vaddr |
| 157 | b.hi .Lmem_type_done |
| 158 | orr attr, attr, #MMU_PTE_ATTR_PXN |
| 159 | ldr tmp, =__data_start |
| 160 | subs size, tmp, vaddr |
| 161 | b.hi .Lmem_type_done |
| 162 | ldr attr, =MMU_PTE_KERNEL_DATA_FLAGS |
| 163 | ldr tmp, =_end |
| 164 | subs size, tmp, vaddr |
| 165 | b.lo . /* Error: _end < vaddr */ |
| 166 | cmp mapping_size, size |
| 167 | b.lo . /* Error: mapping_size < size => RAM size too small for data/bss */ |
| 168 | mov size, mapping_size |
| 169 | |
| 170 | .Lmem_type_done: |
| 171 | subs mapping_size, mapping_size, size |
| 172 | b.lo . /* Error: mapping_size < size (RAM size too small for code/rodata?) */ |
| 173 | |
| 174 | /* Check that paddr, vaddr and size are page aligned */ |
| 175 | orr tmp, vaddr, paddr |
| 176 | orr tmp, tmp, size |
| 177 | tst tmp, #(1 << MMU_KERNEL_PAGE_SIZE_SHIFT) - 1 |
| 178 | bne . /* Error: not page aligned */ |
| 179 | |
| 180 | /* Clear top bits of virtual address (should be all set) */ |
| 181 | eor vaddr, vaddr, #(~0 << MMU_KERNEL_SIZE_SHIFT) |
| 182 | |
| 183 | /* Check that top bits were all set */ |
| 184 | tst vaddr, #(~0 << MMU_KERNEL_SIZE_SHIFT) |
| 185 | bne . /* Error: vaddr out of range */ |
| 186 | |
| 187 | .Lmap_range_top_loop: |
| 188 | /* Select top level page table */ |
| 189 | mov page_table, page_table1 |
| 190 | mov idx_shift, #MMU_KERNEL_TOP_SHIFT |
| 191 | |
| 192 | lsr idx, vaddr, idx_shift |
| 193 | |
| 194 | |
| 195 | /* determine the type of page table entry to use given alignment and size |
| 196 | * of the chunk of memory we are mapping |
| 197 | */ |
| 198 | .Lmap_range_one_table_loop: |
| 199 | /* Check if current level allow block descriptors */ |
| 200 | cmp idx_shift, #MMU_PTE_DESCRIPTOR_BLOCK_MAX_SHIFT |
| 201 | b.hi .Lmap_range_need_page_table |
| 202 | |
| 203 | /* Check if paddr and vaddr alignment allows a block descriptor */ |
| 204 | orr tmp2, vaddr, paddr |
| 205 | lsr tmp, tmp2, idx_shift |
| 206 | lsl tmp, tmp, idx_shift |
| 207 | cmp tmp, tmp2 |
| 208 | b.ne .Lmap_range_need_page_table |
| 209 | |
| 210 | /* Check if size is large enough for a block mapping */ |
| 211 | lsr tmp, size, idx_shift |
| 212 | cbz tmp, .Lmap_range_need_page_table |
| 213 | |
| 214 | /* Select descriptor type, page for level 3, block for level 0-2 */ |
| 215 | orr tmp, attr, #MMU_PTE_L3_DESCRIPTOR_PAGE |
| 216 | cmp idx_shift, MMU_KERNEL_PAGE_SIZE_SHIFT |
| 217 | beq .Lmap_range_l3 |
| 218 | orr tmp, attr, #MMU_PTE_L012_DESCRIPTOR_BLOCK |
| 219 | .Lmap_range_l3: |
| 220 | |
| 221 | /* Write page table entry */ |
| 222 | orr tmp, tmp, paddr |
| 223 | str tmp, [page_table, idx, lsl #3] |
| 224 | |
| 225 | /* Move to next page table entry */ |
| 226 | mov tmp, #1 |
| 227 | lsl tmp, tmp, idx_shift |
| 228 | add vaddr, vaddr, tmp |
| 229 | add paddr, paddr, tmp |
| 230 | subs size, size, tmp |
| 231 | /* TODO: add local loop if next entry is in the same page table */ |
| 232 | b.ne .Lmap_range_top_loop /* size != 0 */ |
| 233 | |
| 234 | /* Restore top bits of virtual address (should be all set) */ |
| 235 | eor vaddr, vaddr, #(~0 << MMU_KERNEL_SIZE_SHIFT) |
| 236 | /* Move to next subtype of ram mmu_initial_mappings entry */ |
| 237 | cbnz mapping_size, .Lmapping_size_loop |
| 238 | |
| 239 | /* Move to next mmu_initial_mappings entry */ |
| 240 | add mmu_initial_mapping, mmu_initial_mapping, __MMU_INITIAL_MAPPING_SIZE |
| 241 | b .Linitial_mapping_loop |
| 242 | |
| 243 | .Lmap_range_need_page_table: |
| 244 | /* Check if page table entry is unused */ |
| 245 | ldr new_page_table, [page_table, idx, lsl #3] |
| 246 | cbnz new_page_table, .Lmap_range_has_page_table |
| 247 | |
| 248 | /* Calculate phys offset (needed for memory allocation) */ |
| 249 | .Lphys_offset: |
| 250 | adr phys_offset, .Lphys_offset /* phys */ |
| 251 | ldr tmp, =.Lphys_offset /* virt */ |
| 252 | sub phys_offset, tmp, phys_offset |
| 253 | |
| 254 | /* Allocate new page table */ |
| 255 | calloc_bootmem_aligned new_page_table, tmp, tmp2, MMU_KERNEL_PAGE_SIZE_SHIFT, phys_offset |
| 256 | |
| 257 | /* Write page table entry (with allocated page table) */ |
| 258 | orr new_page_table, new_page_table, #MMU_PTE_L012_DESCRIPTOR_TABLE |
| 259 | str new_page_table, [page_table, idx, lsl #3] |
| 260 | |
| 261 | .Lmap_range_has_page_table: |
| 262 | /* Check descriptor type */ |
| 263 | and tmp, new_page_table, #MMU_PTE_DESCRIPTOR_MASK |
| 264 | cmp tmp, #MMU_PTE_L012_DESCRIPTOR_TABLE |
| 265 | b.ne . /* Error: entry already in use (as a block entry) */ |
| 266 | |
| 267 | /* switch to next page table level */ |
| 268 | bic page_table, new_page_table, #MMU_PTE_DESCRIPTOR_MASK |
| 269 | mov tmp, #~0 |
| 270 | lsl tmp, tmp, idx_shift |
| 271 | bic tmp, vaddr, tmp |
| 272 | sub idx_shift, idx_shift, #(MMU_KERNEL_PAGE_SIZE_SHIFT - 3) |
| 273 | lsr idx, tmp, idx_shift |
| 274 | |
| 275 | b .Lmap_range_one_table_loop |
| 276 | |
| 277 | .Linitial_mapping_done: |
| 278 | |
| 279 | /* Prepare tt_trampoline page table */ |
| 280 | |
| 281 | /* Zero tt_trampoline translation tables */ |
| 282 | mov tmp, #0 |
| 283 | .Lclear_tt_trampoline: |
| 284 | str xzr, [page_table0, tmp, lsl#3] |
| 285 | add tmp, tmp, #1 |
| 286 | cmp tmp, #MMU_PAGE_TABLE_ENTRIES_IDENT |
| 287 | blt .Lclear_tt_trampoline |
| 288 | |
| 289 | /* Setup mapping at phys -> phys */ |
| 290 | adr tmp, .Lmmu_on_pc |
| 291 | lsr tmp, tmp, #MMU_IDENT_TOP_SHIFT /* tmp = paddr index */ |
| 292 | ldr tmp2, =MMU_PTE_IDENT_FLAGS |
| 293 | add tmp2, tmp2, tmp, lsl #MMU_IDENT_TOP_SHIFT /* tmp2 = pt entry */ |
| 294 | |
| 295 | str tmp2, [page_table0, tmp, lsl #3] /* tt_trampoline[paddr index] = pt entry */ |
| 296 | |
| 297 | #if WITH_SMP |
| 298 | adrp tmp, page_tables_not_ready |
| 299 | add tmp, tmp, #:lo12:page_tables_not_ready |
| 300 | str wzr, [tmp] |
| 301 | b .Lpage_tables_ready |
| 302 | |
| 303 | .Lmmu_enable_secondary: |
| 304 | adrp tmp, page_tables_not_ready |
| 305 | add tmp, tmp, #:lo12:page_tables_not_ready |
| 306 | .Lpage_tables_not_ready: |
| 307 | ldr wtmp2, [tmp] |
| 308 | cbnz wtmp2, .Lpage_tables_not_ready |
| 309 | .Lpage_tables_ready: |
| 310 | #endif |
| 311 | |
| 312 | /* set up the mmu */ |
| 313 | |
| 314 | /* Invalidate TLB */ |
| 315 | tlbi vmalle1is |
| 316 | isb |
| 317 | dsb sy |
| 318 | |
| 319 | /* Initialize Memory Attribute Indirection Register */ |
| 320 | ldr tmp, =MMU_MAIR_VAL |
| 321 | msr mair_el1, tmp |
| 322 | |
| 323 | /* Initialize TCR_EL1 */ |
| 324 | /* set cacheable attributes on translation walk */ |
| 325 | /* (SMP extensions) non-shareable, inner write-back write-allocate */ |
| 326 | ldr tmp, =MMU_TCR_FLAGS_IDENT |
| 327 | msr tcr_el1, tmp |
| 328 | |
| 329 | isb |
| 330 | |
| 331 | /* Write ttbr with phys addr of the translation table */ |
| 332 | msr ttbr0_el1, page_table0 |
| 333 | msr ttbr1_el1, page_table1 |
| 334 | isb |
| 335 | |
| 336 | /* Read SCTLR */ |
| 337 | mrs tmp, sctlr_el1 |
| 338 | |
| 339 | /* Turn on the MMU */ |
| 340 | orr tmp, tmp, #0x1 |
| 341 | |
| 342 | /* Write back SCTLR */ |
| 343 | msr sctlr_el1, tmp |
| 344 | .Lmmu_on_pc: |
| 345 | isb |
| 346 | |
| 347 | /* Jump to virtual code address */ |
| 348 | ldr tmp, =.Lmmu_on_vaddr |
| 349 | br tmp |
| 350 | |
| 351 | .Lmmu_on_vaddr: |
| 352 | |
| 353 | /* Disable trampoline page-table in ttbr0 */ |
| 354 | ldr tmp, =MMU_TCR_FLAGS_KERNEL |
| 355 | msr tcr_el1, tmp |
| 356 | isb |
| 357 | |
| 358 | |
| 359 | /* Invalidate TLB */ |
| 360 | tlbi vmalle1 |
| 361 | isb |
| 362 | |
| 363 | #if WITH_SMP |
| 364 | cbnz cpuid, .Lsecondary_boot |
| 365 | #endif |
| 366 | #endif /* WITH_KERNEL_VM */ |
| 367 | |
| 368 | ldr tmp, =__stack_end |
| 369 | mov sp, tmp |
| 370 | |
| 371 | /* clear bss */ |
| 372 | .L__do_bss: |
| 373 | /* clear out the bss excluding the stack and kernel translation table */ |
| 374 | /* NOTE: relies on __post_prebss_bss_start and __bss_end being 8 byte aligned */ |
| 375 | ldr tmp, =__post_prebss_bss_start |
| 376 | ldr tmp2, =__bss_end |
| 377 | sub tmp2, tmp2, tmp |
| 378 | cbz tmp2, .L__bss_loop_done |
| 379 | .L__bss_loop: |
| 380 | sub tmp2, tmp2, #8 |
| 381 | str xzr, [tmp], #8 |
| 382 | cbnz tmp2, .L__bss_loop |
| 383 | .L__bss_loop_done: |
| 384 | |
| 385 | bl lk_main |
| 386 | b . |
| 387 | |
| 388 | #if WITH_SMP |
| 389 | .Lsecondary_boot: |
| 390 | and tmp, cpuid, #0xff |
| 391 | cmp tmp, #(1 << SMP_CPU_CLUSTER_SHIFT) |
| 392 | bge .Lunsupported_cpu_trap |
| 393 | bic cpuid, cpuid, #0xff |
| 394 | orr cpuid, tmp, cpuid, LSR #(8 - SMP_CPU_CLUSTER_SHIFT) |
| 395 | adrp tmp, linear_cpuid_map |
| 396 | add tmp, tmp, #:lo12:linear_cpuid_map |
| 397 | ldr tmp, [tmp] |
| 398 | cbz tmp, .Lno_cpuid_remap |
| 399 | add tmp, tmp, cpuid |
| 400 | ldrb wtmp2, [tmp] |
| 401 | ubfx cpuid, tmp2, #0, #31 |
| 402 | |
| 403 | .Lno_cpuid_remap: |
| 404 | cmp cpuid, #SMP_MAX_CPUS |
| 405 | bge .Lunsupported_cpu_trap |
| 406 | |
| 407 | /* Set up the stack */ |
| 408 | ldr tmp, =__stack_end |
| 409 | mov tmp2, #ARCH_DEFAULT_STACK_SIZE |
| 410 | mul tmp2, tmp2, cpuid |
| 411 | sub sp, tmp, tmp2 |
| 412 | |
| 413 | mov x0, cpuid |
| 414 | bl arm64_secondary_entry |
| 415 | |
| 416 | .Lunsupported_cpu_trap: |
| 417 | wfe |
| 418 | b .Lunsupported_cpu_trap |
| 419 | #endif |
| 420 | |
| 421 | .ltorg |
| 422 | |
| 423 | #if WITH_SMP |
| 424 | .data |
| 425 | DATA(page_tables_not_ready) |
| 426 | .long 1 |
| 427 | #endif |
| 428 | |
| 429 | .section .bss.prebss.stack |
| 430 | .align 4 |
| 431 | DATA(__stack) |
| 432 | .skip ARCH_DEFAULT_STACK_SIZE * SMP_MAX_CPUS |
| 433 | DATA(__stack_end) |
| 434 | |
| 435 | #if WITH_KERNEL_VM |
| 436 | .section ".bss.prebss.translation_table" |
| 437 | .align 3 + MMU_PAGE_TABLE_ENTRIES_IDENT_SHIFT |
| 438 | DATA(tt_trampoline) |
| 439 | .skip 8 * MMU_PAGE_TABLE_ENTRIES_IDENT |
| 440 | #endif |