40 #if (PAGE_OFFSET & 0x1fffff) != 0
41 #error PAGE_OFFSET must be at least 2MB aligned
42 #endif
PAGE_OFFSET 이 2MB 정렬이 아니면 오류
43
44 /*
45 * Kernel startup entry point.
46 * ---------------------------
47 *
48 * The requirements are:
49 * MMU = off, D-cache = off, I-cache = on or off,
50 * x0 = physical address to the FDT blob.
MMU = off, D-cache = off, I-cache = on or off
x0 = FDT 의 물리주소
시작시 MMU가 off 되어야 하는 이유?
MMU는 CPU가 명령어를 수행할 때 메모리에 접근할 vaddr(Virtual Address)를 paddr(Physical Address)로 변환하는 작업을 수행합니다. 그리고 이때 사용되는 자료구조가 주소 매핑 테이블인데 이는 커널이 생성합니다. Startup 코드가 수행되는 시점에서는 매핑 테이블이 존재하지 않는데 MMU가 on 되어 있다면 메모리 주소가 vaddr로 해석됩니다. 결국 MMU가 수행되고 이는 paddr로 변환되어 의도치 않은 동작이 수행됩니다. 이와 같은 이유로 매핑 테이블이 생성되기 이전에는 MMU를 off 해야합니다.
D-Cashe 가 off 되어야 하는 이유?
D-Cache는 CPU와 메인 메모리 사이에서 데이터를 빠르게 접근하기 위한 저장소 역할을 수행합니다. 대부분의 시스템 프로그래머들은 알다시피 종종 캐시와 메모리간 데이터 싱크(Sync) 문제들을 마주하게 되는데 startup 시점에서는 paddr에서 데이터를 직접 읽거나 써야 합니다. 그러나 D-Cache가 on 되어 있다면 paddr에서 데이터를 읽는 대신 캐시 메모리에서 읽으므로 의도치 않은 동작을 수행하게 됩니다. 이와 같은 이유로 D-Cache도 off 해야합니다.
https://www.ooseel.net/2021/07/why-mmu-and-d-cache-must-be-off-at-startup-point-in-arm64/
51 *
52 * Note that the callee-saved registers are used for storing variables
53 * that are useful before the MMU is enabled. The allocations are described
54 * in the entry routines.
55 */
56 __HEAD
95 #define __HEAD .section ".head.text","ax"
.head.text 섹션 a(allocation) 및 x(execution) 속성으로 시작
a (SHF_ALLOC) : 실행시 메모리에 차지한다. (.comment 등 "a" 속성이 없으면 실행시 메모리에 올라 오지 않음)
x (SHF_EXECINSTR) : 실행가능한 기계어
https://developer.arm.com/documentation/dui0774/i/armclang-Integrated-Assembler-Directives/Section-directives
https://man7.org/linux/man-pages/man5/elf.5.html
57 /*
58 * DO NOT MODIFY. Image header expected by Linux boot-loaders.
59 */
60 efi_signature_nop // special NOP to identity as PE/COFF executable
uefi인경우 "MZ" 서명, 아니면 nop
61 b primary_entry // branch to kernel start, magic
primary_entry 로 분기
62 .quad 0 // Image load offset from start of RAM, little-endian
63 le64sym _kernel_size_le // Effective size of kernel image, little-endian
64 le64sym _kernel_flags_le // Informative flags, little-endian
65 .quad 0 // reserved
66 .quad 0 // reserved
67 .quad 0 // reserved
68 .ascii ARM64_IMAGE_MAGIC // Magic number
69 .long .Lpe_header_offset // Offset to the PE header.
70
71 __EFI_PE_HEADER
uefi 인경우 uefi 헤더위치
72
73 __INIT
96 #define __INIT .section ".init.text","ax"
.init.text 섹션 a(allocation) 및 x(execution) 속성으로 시작
74
75 /*
76 * The following callee saved general purpose registers are used on the
77 * primary lowlevel boot path:
78 *
79 * Register Scope Purpose
80 * x20 primary_entry() .. __primary_switch() CPU boot mode
81 * x21 primary_entry() .. start_kernel() FDT pointer passed at boot in x0
82 * x22 create_idmap() .. start_kernel() ID map VA of the DT blob
83 * x23 primary_entry() .. start_kernel() physical misalignment/KASLR offset
84 * x24 __primary_switch() linear map KASLR seed
85 * x25 primary_entry() .. start_kernel() supported VA size
86 * x28 create_idmap() callee preserved temp register
87 */
88 SYM_CODE_START(primary_entry)
89 bl preserve_boot_args
84 u64 __cacheline_aligned boot_args[4];
boot_args 에 x0 ~ x3 보관
90 bl init_kernel_el // w0=cpu_boot_mode
현재 EL을 확인 후 EL2인경우 하이퍼바이져 관련 설정
91 mov x20, x0
92 bl create_idmap
현재 kernel Image 의 물리 주소와 가상 주소을 동일하게 매핑
93
94 /*
95 * The following calls CPU setup code, see arch/arm64/mm/proc.S for
96 * details.
97 * On return, the CPU will be ready for the MMU to be turned on and
98 * the TCR will have been set.
99 */
100 #if VA_BITS > 48
101 mrs_s x0, SYS_ID_AA64MMFR2_EL1
102 tst x0, #0xf << ID_AA64MMFR2_EL1_VARange_SHIFT
x0 = 0b0000 이면 48bit VA 지원
x0 = 0b0001 이면 52bit VA 지원
103 mov x0, #VA_BITS
104 mov x25, #VA_BITS_MIN
105 csel x25, x25, x0, eq
eq => 102 행의 tst 결과가 0이면 즉 48bit VA지원 이면, x25 는 48아니면 VA_BITS (52)
106 mov x0, x25
107 #endif
x0 : 48bit 지원이면 48, 52bit 지원이면 52
108 bl __cpu_setup // initialise processor
109 b __primary_switch
110 SYM_CODE_END(primary_entry)
111
796 SYM_FUNC_START_LOCAL(__primary_switch)
797 adrp x1, reserved_pg_dir
798 adrp x2, init_idmap_pg_dir
799 bl __enable_mmu
reserved_pg_dir 을 ttbr0 (유저영역), init_idmap_pg_dir ttbr1 (커널영역)에 연결후 mmu enable
800 #ifdef CONFIG_RELOCATABLE
801 adrp x23, KERNEL_START
802 and x23, x23, MIN_KIMG_ALIGN - 1
KERNEL_START (_text) 의 2MB 정렬시 버려지는 부분 (x23 mod 2MB) 을 x23에
803 #ifdef CONFIG_RANDOMIZE_BASE
804 mov x0, x22
x0, x22 : FDT 주소를 PMD 크기로 내림 정렬
805 adrp x1, init_pg_end
806 mov sp, x1
x1, sp(스택 포인터)를 init_pg_end로 함.
807 mov x29, xzr
808 bl __pi_kaslr_early_init
OK, so we are proceeding with KASLR enabled. Calculate a suitable kernel image offset from the seed. Let's place the kernel in the middle half of the VMALLOC area (VA_BITS_MIN - 2), and stay clear of the lower and upper quarters to avoid colliding with other allocations.
KASLR을 활성화한 상태로 진행하겠습니다. 시드에서 적절한 커널 이미지 오프셋을 계산합니다. VMALLOC 영역(VA_BITS_MIN - 2)의 중간 부분에 커널을 배치하고 다른 할당과 충돌하지 않도록 하위 및 상위 분기를 피하도록 하겠습니다.
#define BIT(x) (1UL << (x)),
#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long ) * 8 - 1 - (h))))
BIT(VA_BITS_MIN - 3) + (seed & GENMASK(VA_BITS_MIN - 3, 0))
arch/arm64/kernel/pi/kaslr_early.c
The Makefile which arch/arm64/kernel/pi/kaslr_early.c uses, which kaslr_early_init resides in, prepends all symbols with __pi_ , therefore the bl __pi_kaslr_early_init call gets directed here.
kaslr_early_init가 속한 arch/arm64/kernel/pi/kaslr_early.c가 사용하는 Makefile은 모든 기호 앞에 __pi_를 붙이므로 bl__pi_kaslr_early_init 호출은 여기로 향합니다.
https://rhythm16.github.io/kaslr_en/#kaslr-early-init
809 and x24, x0, #SZ_2M - 1 // capture memstart offset seed
x0에 커널이 위치할 주소에 반영할 임의의 값(seed)을 받아 옮
x24 = x0 mod 2M
810 bic x0, x0, #SZ_2M - 1
811 orr x23, x23, x0 // record kernel offset
x23 커널 주소의 offset 값
x23 = x23(커널 주소의 하위 2MB) | x0 (랜덤값 2MB 초과 값)
예) x23 = 0000mmmm | hhhh0000 = hhhhmmmm
812 #endif
813 #endif
814 bl clear_page_tables
init_pg_dir에서 init_pg_end까지 clear
815 bl create_kernel_mapping
init_pg_dir 을 PGD 로, VA를 KIMAGE_VADDR 로 매핑함
816
817 adrp x1, init_pg_dir
818 load_ttbr1 x1, x1, x2
init_pg_dir을 ttbr1 레지스터에 등록
819 #ifdef CONFIG_RELOCATABLE
820 bl __relocate_kernel
relocation table (__rela_start ~ __rela_end) 의 data (name + addend)에 KIMAGE_VADDR + 랜덤 offset 값을 더하여 다시 저장
relocation table 형식 : Offset, Info, Name + Addend
http://jake.dothome.co.kr/a64-elf-relocations/
821 #endif
822 ldr x8, =__primary_switched
823 adrp x0, KERNEL_START // __pa(KERNEL_START)
824 br x8
825 SYM_FUNC_END(__primary_switch)
415 /*
416 * The following fragment of code is executed with the MMU enabled.
417 *
418 * x0 = __pa(KERNEL_START)
419 */
420 SYM_FUNC_START_LOCAL(__primary_switched)
421 adr_l x4, init_task
422 init_cpu_task x4, x5, x6
sp_el0와 현재 sp를 설정하고 per_cpu offset 값을 tpidr 레지스터에 보관
423
424 adr_l x8, vectors // load VBAR_EL1 with virtual
425 msr vbar_el1, x8 // vector table address
426 isb
vectors table 주소를 vbar_el1에 보관
vectors table 은 arch/arm64/kernel/entry.S에 있음
https://developer.arm.com/documentation/100933/0100/AArch64-exception-vector-table
https://s-matyukevich.github.io/raspberry-pi-os/docs/lesson03/linux/low_level-exception_handling.html
http://jake.dothome.co.kr/arm64-vector/
427
428 stp x29, x30, [sp, #-16]!
429 mov x29, sp
x29 ( frame pointer (FP)), x30( link register (LR)) 스택 보관
https://m.blog.naver.com/crushhh/222222656563
https://johannst.github.io/notes/arch/arm64.html
430
431 str_l x21, __fdt_pointer, x5 // Save FDT pointer
__fdt_pointer 에 fdt 시작물리주소 보관
432
433 ldr_l x4, kimage_vaddr // Save the offset between
434 sub x4, x4, x0 // the kernel virtual and
435 str_l x4, kimage_voffset, x5 // physical mappings
kimage_voffset 에 커널이미지의 가상주소 - 물리주소 보관
436
437 mov x0, x20
438 bl set_cpu_boot_mode_flag
u32 __boot_cpu_mode[2]에 현재 boot mode가
el1이면 {0xe11, 0xe11 }
el2이면 {0xe12, 0xe12 } 보관
439
440 // Clear BSS
441 adr_l x0, __bss_start
442 mov x1, xzr
443 adr_l x2, __bss_stop
444 sub x2, x2, x0
445 bl __pi_memset
446 dsb ishst // Make zero page visible to PTW
BSS 영역 초기화
447
448 #if VA_BITS > 48
449 adr_l x8, vabits_actual // Set this early so KASAN early init
450 str x25, [x8] // ... observes the correct value
451 dc civac, x8 // Make visible to booting secondaries
452 #endif
VA_BITS > 48 경우 vabits_actual 에 VA_BITS ( = 52) 보관
453
454 #ifdef CONFIG_RANDOMIZE_BASE
455 adrp x5, memstart_offset_seed // Save KASLR linear map seed
456 strh w24, [x5, :lo12:memstart_offset_seed]
457 #endif
KASLR 을 위한 seed 값 mod 2MB 값 보관
458 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
459 bl kasan_early_init
460 #endif
kasan 관련 초기화
461 mov x0, x21 // pass FDT address in x0
462 bl early_fdt_map // Try mapping the FDT early
463 mov x0, x20 // pass the full boot status
464 bl init_feature_override // Parse cpu feature overrides
465 mov x0, x20
466 bl finalise_el2 // Prefer VHE if possible
467 ldp x29, x30, [sp], #16
468 bl start_kernel
469 ASM_BUG()
470 SYM_FUNC_END(__primary_switched)
282
283 SYM_FUNC_START_LOCAL(create_idmap)
284 mov x28, lr
복귀주소(lr)를 x28에 보관
285 /*
286 * The ID map carries a 1:1 mapping of the physical address range
287 * covered by the loaded image, which could be anywhere in DRAM. This
288 * means that the required size of the VA (== PA) space is decided at
289 * boot time, and could be more than the configured size of the VA
290 * space for ordinary kernel and user space mappings.
291 *
292 * There are three cases to consider here:
293 * - 39 <= VA_BITS < 48, and the ID map needs up to 48 VA bits to cover
294 * the placement of the image. In this case, we configure one extra
295 * level of translation on the fly for the ID map only. (This case
296 * also covers 42-bit VA/52-bit PA on 64k pages).
297 *
298 * - VA_BITS == 48, and the ID map needs more than 48 VA bits. This can
299 * only happen when using 64k pages, in which case we need to extend
300 * the root level table rather than add a level. Note that we can
301 * treat this case as 'always extended' as long as we take care not
302 * to program an unsupported T0SZ value into the TCR register.
303 *
304 * - Combinations that would require two additional levels of
305 * translation are not supported, e.g., VA_BITS==36 on 16k pages, or
306 * VA_BITS==39/4k pages with 5-level paging, where the input address
307 * requires more than 47 or 48 bits, respectively.
308 */
ID 맵은 로드된 이미지가 포함하는 물리적 주소 범위(DRAM의 어느 위치에나 있을 수 있음)를 1:1로 매핑합니다. 즉, 필요한 VA(== PA) 공간 크기는 부팅 시 결정되며 일반 커널 및 사용자 공간 매핑을 위해 구성된 VA 공간 크기보다 클 수 있습니다.
여기서 고려해야 할 세 가지 경우가 있습니다.
- 39 <= VA_BITS < 48, ID 맵은 이미지 배치를 처리하기 위해 최대 48 VA 비트가 필요합니다. 이 경우 ID 맵에 대해서만 즉석에서 하나의 추가 변환 수준을 구성합니다. (이 사례는 64k 페이지의 42비트 VA/52비트 PA에도 적용됩니다.)
- VA_BITS == 48, ID 맵에는 48VA 비트 이상이 필요합니다. 이는 64k 페이지를 사용할 때만 발생할 수 있으며, 이 경우 레벨을 추가하는 대신 루트 레벨 테이블을 확장해야 합니다. 지원되지 않는 T0SZ 값을 TCR 레지스터에 프로그래밍하지 않도록 주의하는 한 이 경우를 '항상 확장'으로 처리할 수 있습니다.
- 두 가지 추가 변환 수준이 필요한 조합은 지원되지 않습니다. 예를 들어 16k 페이지의 VA_BITS==36 또는 입력 주소에 각각 47비트 또는 48비트 이상이 필요한 5레벨 페이징의 VA_BITS==39/4k 페이지 .
PGTABLE_LEVELS에 따른 Page 크기와 VA_BITS 수의 관계
레벨 2 => ARM64_16K_PAGES && ARM64_VA_BITS_36
레벨 2 => ARM64_64K_PAGES && ARM64_VA_BITS_42
레벨 3 => ARM64_64K_PAGES && (ARM64_VA_BITS_48 || ARM64_VA_BITS_52)
레벨 3 => ARM64_4K_PAGES && ARM64_VA_BITS_39
레벨 3 => ARM64_16K_PAGES && ARM64_VA_BITS_47
레벨 4 => !ARM64_64K_PAGES && ARM64_VA_BITS_48
VA_BITS = (PAGE_SHIFT - 3) * 레벨 + PAGE_SHIFT 공식에 의거 만들어짐
4K(3레벨)의 경우 39
64K인 경우 42(2레벨)
16K인 경우 47(3개 레벨)
4K인 경우 48 || 16K || 64K(페이지 크기에 따라 4/4/3 레벨)
https://linux-arm-kernel.infradead.narkive.com/JgUN8nTp/patch-v7-04-11-arm64-introduce-va-bits-and-translation-level-options
https://www.spinics.net/lists/arm-kernel/msg319552.html
309 #if (VA_BITS < 48)
310 #define IDMAP_PGD_ORDER (VA_BITS - PGDIR_SHIFT)
ex) VA_BITS - PGDIR_SHIFT = 39 - 30 = 9
init_idmap_pg_dir 에 들어갈수 있는 최대 항목수 (2^9)
311 #define EXTRA_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3)
ex) 30 + 12 - 3 = 39
312
313 /*
314 * If VA_BITS < 48, we have to configure an additional table level.
315 * First, we have to verify our assumption that the current value of
316 * VA_BITS was chosen such that all translation levels are fully
317 * utilised, and that lowering T0SZ will always result in an additional
318 * translation level to be configured.
319 */
VA_BITS < 48인 경우 추가 테이블 수준을 구성해야 합니다.
먼저, 모든 변환 수준이 완전히 활용되도록 VA_BITS의 현재 값이 선택되었으며 T0SZ를 낮추면 항상 추가 변환 수준이 구성된다는 가정을 확인해야 합니다.
320 #if VA_BITS != EXTRA_SHIFT
321 #error "Mismatch between VA_BITS and page size/number of translation levels"
322 #endif
323 #else
324 #define IDMAP_PGD_ORDER (PHYS_MASK_SHIFT - PGDIR_SHIFT)
ex) 48 - 39 = 9
init_idmap_pg_dir 에 들어갈수 있는 최대 항목수 (2^9)
325 #define EXTRA_SHIFT
326 /*
327 * If VA_BITS == 48, we don't have to configure an additional
328 * translation level, but the top-level table has more entries.
329 */
330 #endif
331 adrp x0, init_idmap_pg_dir
332 adrp x3, _text
333 adrp x6, _end + MAX_FDT_SIZE + SWAPPER_BLOCK_SIZE
334 mov x7, SWAPPER_RX_MMUFLAGS
335
336 map_memory x0, x1, x3, x6, x7, x3, IDMAP_PGD_ORDER, x10, x11, x12, x13, x14, EXTRA_SHIFT
337
-. init_idmap_pg_dir을 PGD로 하고 범위를 _text 부터 _end + MAX_FDT_SIZE + SWAPPER_BLOCK_SIZE 까지
(Kernel Image 전체 + FDT 크기 + swapper 크기) SWAPPER_RX_MMUFLAGS (읽기전용, 실행가능) 상태로 하는 맵핑
-. SWAPPER_BLOCK_SIZE = 2 ^ 21 (PMD 사용) or 1 page (PMD 미사용)
338 /* Remap the kernel page tables r/w in the ID map */
339 adrp x1, _text
340 adrp x2, init_pg_dir
341 adrp x3, init_pg_end
342 bic x4, x2, #SWAPPER_BLOCK_SIZE - 1
343 mov x5, SWAPPER_RW_MMUFLAGS
344 mov x6, #SWAPPER_BLOCK_SHIFT
345 bl remap_region
물리주소 init_pg_dir 부터 init_pg_end 까지 RW 가능하게 remapping
346
347 /* Remap the FDT after the kernel image */
348 adrp x1, _text
349 adrp x22, _end + SWAPPER_BLOCK_SIZE
350 bic x2, x22, #SWAPPER_BLOCK_SIZE - 1
351 bfi x22, x21, #0, #SWAPPER_BLOCK_SHIFT // remapped FDT address
352 add x3, x2, #MAX_FDT_SIZE + SWAPPER_BLOCK_SIZE
353 bic x4, x21, #SWAPPER_BLOCK_SIZE - 1
354 mov x5, SWAPPER_RW_MMUFLAGS
355 mov x6, #SWAPPER_BLOCK_SHIFT
356 bl remap_region
x21 (FDT 시작 주소) 부터 MAX_FDT_SIZE (2MB) + @ 까지 RW 가능하게 remapping
357
358 /*
359 * Since the page tables have been populated with non-cacheable
360 * accesses (MMU disabled), invalidate those tables again to
361 * remove any speculatively loaded cache lines.
362 */
363 dmb sy
364
365 adrp x0, init_idmap_pg_dir
366 adrp x1, init_idmap_pg_end
367 bl dcache_inval_poc
init_idmap_pg_dir 부터 init_idmap_pg_end 까지 Dcache invalidate
368 ret x28
호출한곳으로 return
369 SYM_FUNC_END(create_idmap)
399 /*
400 * __cpu_setup
401 *
402 * Initialise the processor for turning the MMU on.
403 *
404 * Input:
405 * x0 - actual number of VA bits (ignored unless VA_BITS > 48)
406 * Output:
407 * Return in x0 the value of the SCTLR_EL1 register.
408 */
409 .pushsection ".idmap.text", "awx"
410 SYM_FUNC_START(__cpu_setup)
411 tlbi vmalle1 // Invalidate local TLB
tlb로 모두 invalidate 한다.
412 dsb nsh
tlb가 모두 invalidate 완료 시까지 대기
413
414 mov x1, #3 << 20
415 msr cpacr_el1, x1 // Enable FP/ASIMD
FP/ASIMD 명령사용시 kernel 로 trap 금지
416 mov x1, #1 << 12 // Reset mdscr_el1 and disable
417 msr mdscr_el1, x1 // access to the DCC from EL0
418 isb // Unmask debug exceptions now,
DCC (Debug Communications Channel)활성화로 유저모드에서 디버깅 가능
https://developer.arm.com/documentation/ddi0406/c/Debug-Architecture/The-Debug-Communications-Channel-and-Instruction-Transfer-Register/About-the-DCC-and-DBGITR?lang=en
419 enable_dbg // since this is per-cpu
daifclr에 #8 값을 기록하여 D 플래그를 unmask 하여 debug 가능상태로
420 reset_pmuserenr_el0 x1 // Disable PMU access from EL0
421 reset_amuserenr_el0 x1 // Disable AMU access from EL0
유저모드(EL0)에서 PMU와 AMU 사용금지
-. PMU를 사용하는 목적은 성능 분석 및 디버깅입니다.PMU는 성능 프로파일링 및 디버깅을 위해 캐시 미스, TLB 미스, CPU 사이클, 실행된 명령 등 다양한 이벤트를 제공합니다.PMU는 Linux Perf, arm DS-5 및 arm DS와 같은 성능 분석 도구에서 사용할 수 있습니다.
-. AMU를 사용하는 목적은 시스템 관리 및 모니터링, 특히 전원 및 성능 관리를 위한 것입니다.예를 들어 AMU 카운터를 동적 전압 및 주파수 스케일링(DVFS)을 위한 추가 CPU 프로세서 작업 정보로 사용할 수 있습니다.
https://developer.arm.com/documentation/ka004659/latest/
https://developer.arm.com/documentation/ddi0487/latest/
422
423 /*
424 * Default values for VMSA control registers. These will be adjusted
425 * below depending on detected CPU features.
426 */
427 mair .req x17
428 tcr .req x16
x17을 mair, x16을 tcr 로 명명
429 mov_q mair, MAIR_EL1_SET
430 mov_q tcr, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
431 TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
432 TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS
MAIR 레지스터와 TCR 레지스터의 초기값 설정
433
434 tcr_clear_errata_bits tcr, x9, x5
Fujitsu-A64FX errata 보정
435
436 #ifdef CONFIG_ARM64_VA_BITS_52
437 sub x9, xzr, x0
438 add x9, x9, #64
439 tcr_set_t1sz tcr, x9
VA_BITS = 52 인경우 t1sz 값을 64 - 52 = 12 로 설정
440 #else
441 idmap_get_t0sz x9
442 #endif
443 tcr_set_t0sz tcr, x9
t0sz의 크기를 64 - VA_BITS 로 설정
444
445 /*
446 * Set the IPS bits in TCR_EL1.
447 */
448 tcr_compute_pa_size tcr, #TCR_IPS_SHIFT, x5, x6
실제 PA(물리주소의 크기)값과 커널에 설정된 PA값중 작은 값으로 tcr.ips 설정
449 #ifdef CONFIG_ARM64_HW_AFDBM
450 /*
451 * Enable hardware update of the Access Flags bit.
452 * Hardware dirty bit management is enabled later,
453 * via capabilities.
454 */
455 mrs x9, ID_AA64MMFR1_EL1
456 and x9, x9, #0xf
457 cbz x9, 1f
458 orr tcr, tcr, #TCR_HA // hardware Access flag update
459 1:
460 #endif /* CONFIG_ARM64_HW_AFDBM */
H/W에서 (MMU 말하는 듯) Page 변환테이블(메모리에 있는)에 Access flag 와 Dirty state 를 갱신할수있다면
TCR.HA 설정
https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/ID-AA64MMFR1-EL1--AArch64-Memory-Model-Feature-Register-1
461 msr mair_el1, mair
462 msr tcr_el1, tcr
지금까지 만들어진 mair레지스터, tcr 레지스터 설정값을 반영
463 /*
464 * Prepare SCTLR
465 */
466 mov_q x0, INIT_SCTLR_EL1_MMU_ON
MMU 을 ON 하기위한 SCTLR_EL1 값을 x0에
467 ret // return to head.S
468
469 .unreq mair
470 .unreq tcr
복귀 및 mair, tcr 명명 설정 해제
471 SYM_FUNC_END(__cpu_setup)
646 /*
647 * Enable the MMU.
648 *
649 * x0 = SCTLR_EL1 value for turning on the MMU.
650 * x1 = TTBR1_EL1 value
651 * x2 = ID map root table address
652 *
653 * Returns to the caller via x30/lr. This requires the caller to be covered
654 * by the .idmap.text section.
655 *
656 * Checks if the selected granule size is supported by the CPU.
657 * If it isn't, park the CPU
658 */
659 SYM_FUNC_START(__enable_mmu)
660 mrs x3, ID_AA64MMFR0_EL1
661 ubfx x3, x3, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4
662 cmp x3, #ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN
663 b.lt __no_granule_support
664 cmp x3, #ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX
665 b.gt __no_granule_support
커널설정에 지정된 Page (4k, 16k, 64k 중 1)을 지원하는지 확인
666 phys_to_ttbr x2, x2
667 msr ttbr0_el1, x2 // load TTBR0
668 load_ttbr1 x1, x1, x3
ttrb0와 ttrb1에 해당 PGD을 연결
669
670 set_sctlr_el1 x0
MMU ON
671
672 ret
673 SYM_FUNC_END(__enable_mmu)
695 SYM_FUNC_START_LOCAL(__no_granule_support)
696 /* Indicate that this CPU can't boot and is stuck in the kernel */
697 update_early_cpu_boot_status \
698 CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_NO_GRAN, x1, x2
699 1:
700 wfe
701 wfi
702 b 1b
해당 cpu 에서 kernel에 동작 할수 없음을 표시후 대기
703 SYM_FUNC_END(__no_granule_support)
대부분의 시계가 게이팅된 저전력 대기 상태에 진입하기 위한 WFI 및 WFE의 두 가지 지침이 있습니다.
주요 차이점은 WFE가 이벤트 레지스터, SEV 지침 및 EVENTI, EVENTO 신호를 사용한다는 점이며, 엔트리 및 웨이크업 조건에서 약간의 차이가 있습니다.
WFI는 프로세서를 깨우는 데 인터럽트가 필요한 대기 모드, 휴면 모드 또는 종료 모드로 전환하는 것을 목표로 합니다.
WFE의 용도는 스핀록 루프에 넣는 것입니다. CPU가 공유 메모리와 같은 공유 리소스에 액세스하려는 경우 전용 로드 및 저장소 액세스로 관리되는 세마포어 플래그 위치를 사용할 수 있습니다. 여러 CPU가 리소스에 액세스하려고 하면 한 CPU가 액세스하여 리소스를 사용하기 시작하고 다른 CPU는 스핀록 루프에 고정됩니다. 전력을 절약하려면 WFE 명령을 루프에 삽입하여 연속적으로 루프를 하는 대신 CPU가 STANBYWFE로 들어가도록 할 수 있습니다. 그러면 자원을 사용하고 있던 CPU는 자원 사용이 끝난 후에 SEV 명령을 실행해야 합니다. 그러면 STANBYWFE에서 다른 모든 CPU가 웨이크업되고 다른 CPU가 공유 리소스에 액세스할 수 있습니다.
EVENTI와 EVENTO가 있는 이유는 CPU 중 하나에 의해 SEV 명령이 실행될 때 EVENTO에 펄스를 내보내기 위해서입니다. 이 신호는 두 번째 Cortex-A5 MPCore 클러스터의 EVENTI에 연결되며 STANBYWFE 상태의 CPU는 모두 대기 상태를 유지합니다. 따라서 이러한 신호는 여러 클러스터에 걸쳐 WFE 모드의 사용을 확장할 수 있습니다. 단일 클러스터가 있으면 클러스터를 사용할 필요가 없습니다.
371 SYM_FUNC_START_LOCAL(create_kernel_mapping)
372 adrp x0, init_pg_dir
373 mov_q x5, KIMAGE_VADDR // compile time __va(_text)
x5 = 커널이 위치할 가상주소 ( - (1 << ( VA_BITS_MIN - 1)) + 128MB)
374 #ifdef CONFIG_RELOCATABLE
375 add x5, x5, x23 // add KASLR displacement
커널 재배치 offset 값을 더하여 재배치 랜덤화 실행
376 #endif
377 adrp x6, _end // runtime __pa(_end)
378 adrp x3, _text // runtime __pa(_text)
379 sub x6, x6, x3 // _end - _text
380 add x6, x6, x5 // runtime __va(_end)
381 mov x7, SWAPPER_RW_MMUFLAGS
382
383 map_memory x0, x1, x5, x6, x7, x3, (VA_BITS - PGDIR_SHIFT), x10, x11, x12, x13, x14
384
385 dsb ishst // sync with page table walker
386 ret
387 SYM_FUNC_END(create_kernel_mapping)
x0 : location of page table (init_pg_dir)
x1 : address to be used for first level page table entry (typically tbl + PAGE_SIZE)
x5 : virtual address of start of range
x6 : virtual address of end of range - we map [vstart, vend - 1]
x7 : flags to use to map last level entries
x3 : physical address corresponding to vstart - physical memory is contiguous
VA_BITS - PGDIR_SHIFT : #imm 2log(number of entries in PGD table)
x10, x11, x12, x13, x14 : istart, iend, tmp, count, sv
389 /*
390 * Initialize CPU registers with task-specific and cpu-specific context.
391 *
392 * Create a final frame record at task_pt_regs(current)->stackframe, so
393 * that the unwinder can identify the final frame record of any task by
394 * its location in the task stack. We reserve the entire pt_regs space
395 * for consistency with user tasks and kthreads.
396 */
작업별 및 CPU별 컨텍스트를 사용하여 CPU 레지스터를 초기화합니다.
unwinder가 작업 스택의 위치에 따라 모든 작업의 최종 프레임 레코드를 식별할 수 있도록 task_pt_regs(current)->stackframe에서 최종 프레임 레코드를 만듭니다. 사용자 작업 및 kthread와의 일관성을 위해 전체 pt_regs 공간을 예약합니다.
https://www.codeblueprint.co.uk/2017/07/31/the-orc-unwinder.html
397 .macro init_cpu_task tsk, tmp1, tmp2
398 msr sp_el0, \tsk
sp_el0에 tsk 의 주소 저장
399
400 ldr \tmp1, [\tsk, #TSK_STACK]
401 add sp, \tmp1, #THREAD_SIZE
402 sub sp, sp, #PT_REGS_SIZE
현재 sp에 tsk의 stack 을 지정 (init_task.init_stack)하고 PT_REGS_SIZE 공간 확보
403
404 stp xzr, xzr, [sp, #S_STACKFRAME]
405 add x29, sp, #S_STACKFRAME
pt_regs.stackframe[]을 초기화하고 x29에 위치 보관
406
407 scs_load_current
shadow 스택 사용시 x18레지스터에 tsk->thread_info.scs_sp 값 보관
https://source.android.com/docs/security/test/shadow-call-stack?hl=ko
408
409 adr_l \tmp1, __per_cpu_offset
410 ldr w\tmp2, [\tsk, #TSK_TI_CPU]
411 ldr \tmp1, [\tmp1, \tmp2, lsl #3]
412 set_this_cpu_offset \tmp1
현재 CPU의 per_cpu offset 값( 0 )을 tpidr 레지스터에 보관
413 .endm
573 /*
574 * Secondary entry point that jumps straight into the kernel. Only to
575 * be used where CPUs are brought online dynamically by the kernel.
576 */
577 SYM_FUNC_START(secondary_entry)
578 bl init_kernel_el // w0=cpu_boot_mode
579 b secondary_startup
580 SYM_FUNC_END(secondary_entry)
581
582 SYM_FUNC_START_LOCAL(secondary_startup)
583 /*
584 * Common entry point for secondary CPUs.
585 */
586 mov x20, x0 // preserve boot mode
587 bl finalise_el2
588 bl __cpu_secondary_check52bitva
589 #if VA_BITS > 48
590 ldr_l x0, vabits_actual
591 #endif
592 bl __cpu_setup // initialise processor
593 adrp x1, swapper_pg_dir
594 adrp x2, idmap_pg_dir
595 bl __enable_mmu
596 ldr x8, =__secondary_switched
597 br x8
598 SYM_FUNC_END(secondary_startup)
599
600 SYM_FUNC_START_LOCAL(__secondary_switched)
601 mov x0, x20
602 bl set_cpu_boot_mode_flag
603 str_l xzr, __early_cpu_boot_status, x3
604 adr_l x5, vectors
605 msr vbar_el1, x5
606 isb
607
608 adr_l x0, secondary_data
609 ldr x2, [x0, #CPU_BOOT_TASK]
610 cbz x2, __secondary_too_slow
611
612 init_cpu_task x2, x1, x3
613
614 #ifdef CONFIG_ARM64_PTR_AUTH
615 ptrauth_keys_init_cpu x2, x3, x4, x5
616 #endif
https://velog.io/@pensieveview/Pointer-authentication
https://velog.io/@pensieveview/Pointer-authentication-Security
https://staraube.tistory.com/31
617
618 bl secondary_start_kernel
619 ASM_BUG()
620 SYM_FUNC_END(__secondary_switched)