21 /*
 22  * Must be called after early_fixmap_init
 23  */
 24 void __init early_ioremap_init(void)
 25 {
 26         early_ioremap_setup();
 27 }

 

 71 void __init early_ioremap_setup(void)
 72 {
 73         int i;
 74
 75         for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
 76                 if (WARN_ON(prev_map[i]))
 77                         break;
 78
 79         for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
 80                 slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i);
 81 }

 

위 그림의 BTMAPS 영역을 slot_virt[]에 할당

'linux' 카테고리의 다른 글

6.1/setup_arch(char **cmdline_p)  (0) 2024.04.02
6.1/fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)  (0) 2023.12.19
6.1/early_fixmap_init(void)  (0) 2023.12.18
6.1/early_fdt_map(u64 dt_phys)  (0) 2023.12.18
6.1/Head.S  (0) 2023.11.16

290 void __init __no_sanitize_address setup_arch(char **cmdline_p)
291 {
292         setup_initial_init_mm(_stext, _etext, _edata, _end);

더보기

 

 48 void setup_initial_init_mm(void *start_code, void *end_code,
 49                            void *end_data, void *brk)
 50 {
 51         init_mm.start_code = (unsigned long)start_code;
 52         init_mm.end_code = (unsigned long)end_code;
 53         init_mm.end_data = (unsigned long)end_data;
 54         init_mm.brk = (unsigned long)brk;
 55 }

 

init_mm에 _stext, _etext, _edata, _end 저장


293
294         *cmdline_p = boot_command_line;

boot_command_line을 cmdline_p 에 연결


295
296         /*
297          * If know now we are going to need KPTI then use non-global
298          * mappings from the start, avoiding the cost of rewriting
299          * everything later.
300          */
301         arm64_use_ng_mappings = kaslr_requires_kpti();

https://isun2501.tistory.com/62
302
303         early_fixmap_init();

https://isun2501.tistory.com/66
304         early_ioremap_init();

https://isun2501.tistory.com/71
305
306         setup_machine_fdt(__fdt_pointer);

__fdt_pointer 는 head.S에서 fdt의 위치를 보관함


307
308         /*
309          * Initialise the static keys early as they may be enabled by the
310          * cpufeature code and early parameters.
311          */
312         jump_label_init();
313         parse_early_param();
314
315         /*
316          * Unmask asynchronous aborts and fiq after bringing up possible
317          * earlycon. (Report possible System Errors once we can report this
318          * occurred).
319          */
320         local_daif_restore(DAIF_PROCCTX_NOIRQ);
321
322         /*
323          * TTBR0 is only used for the identity mapping at this stage. Make it
324          * point to zero page to avoid speculatively fetching new entries.
325          */
326         cpu_uninstall_idmap();
327
328         xen_early_init();
329         efi_init();
330

331         if (!efi_enabled(EFI_BOOT) && ((u64)_text % MIN_KIMG_ALIGN) != 0)
332              pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
333
334         arm64_memblock_init();
335
336         paging_init();
337

338         acpi_table_upgrade();
339
340         /* Parse the ACPI tables for possible boot-time configuration */
341         acpi_boot_table_init();
342
343         if (acpi_disabled)
344                 unflatten_device_tree();
345
346         bootmem_init();
347
348         kasan_init();
349
350         request_standard_resources();
351
352         early_ioremap_reset();
353
354         if (acpi_disabled)
355                 psci_dt_init();
356         else
357                 psci_acpi_init();
358
359         init_bootcpu_ops();
360         smp_init_cpus();
361         smp_build_mpidr_hash();
362
363         /* Init percpu seeds for random tags after cpus are set up. */
364         kasan_init_sw_tags();
365
366 #ifdef CONFIG_ARM64_SW_TTBR0_PAN
367         /*
368          * Make sure init_thread_info.ttbr0 always generates translation
369          * faults in case uaccess_enable() is inadvertently called by the init
370          * thread.
371          */
372         init_task.thread_info.ttbr0 = phys_to_ttbr(__pa_symbol(reserved_pg_dir));
373 #endif
374

375         if (boot_args[1] || boot_args[2] || boot_args[3]) {
376                 pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"
377                         "\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n"
378                         "This indicates a broken bootloader or old kernel\n",
379                         boot_args[1], boot_args[2], boot_args[3]);
380         }
381 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'linux' 카테고리의 다른 글

6.1/early_ioremap_init(void)  (0) 2025.03.29
6.1/fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)  (0) 2023.12.19
6.1/early_fixmap_init(void)  (0) 2023.12.18
6.1/early_fdt_map(u64 dt_phys)  (0) 2023.12.18
6.1/Head.S  (0) 2023.11.16

 

fdt와 FIX_FDT의 주소변환 테이블 구성

 

 

1354 void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
1355 {
1356         const u64 dt_virt_base = __fix_to_virt(FIX_FDT);

dt_virt_base = FIX_FDT의 가상주소


1357         int offset;
1358         void *dt_virt;
1359
1360         /*
1361          * Check whether the physical FDT address is set and meets the minimum
1362          * alignment requirement. Since we are relying on MIN_FDT_ALIGN to be
1363          * at least 8 bytes so that we can always access the magic and size
1364          * fields of the FDT header after mapping the first chunk, double check
1365          * here if that is indeed the case.
1366          */

물리적 FDT 주소가 설정되어 있고 최소 정렬 요구 사항을 충족하는지 확인하세요. 첫 번째 청크를 매핑한 후 FDT 헤더의 Magic 및 Size 필드에 항상 액세스할 수 있도록 MIN_FDT_ALIGN이 최소 8바이트가 되도록 의존하고 있으므로 여기에서 실제로 그런 경우인지 다시 확인하세요.


1367         BUILD_BUG_ON(MIN_FDT_ALIGN < 8);
1368         if (!dt_phys || dt_phys % MIN_FDT_ALIGN)
1369                 return NULL;
1370
1371         /*
1372          * Make sure that the FDT region can be mapped without the need to
1373          * allocate additional translation table pages, so that it is safe
1374          * to call create_mapping_noalloc() this early.
1375          *
1376          * On 64k pages, the FDT will be mapped using PTEs, so we need to
1377          * be in the same PMD as the rest of the fixmap.
1378          * On 4k pages, we'll use section mappings for the FDT so we only
1379          * have to be in the same PUD.
1380          */

추가 변환 테이블 페이지를 할당할 필요 없이 FDT 영역을 매핑할 수 있는지 확인하여 일찍 create_mapping_noalloc()을 호출하는 것이 안전합니다.
64k 페이지에서 FDT는 PTE를 사용하여 매핑되므로 나머지 fixmap과 동일한 PMD에 있어야 합니다.
4k 페이지에서는 FDT에 섹션 매핑을 사용하므로 동일한 PUD에만 있으면 됩니다.


1381         BUILD_BUG_ON(dt_virt_base % SZ_2M);

dt_virt_base 의 2MB 정렬확인


1382
1383         BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> SWAPPER_TABLE_SHIFT !=
1384                      __fix_to_virt(FIX_BTMAP_BEGIN) >> SWAPPER_TABLE_SHIFT);

FIX_FDT_END ~ FIX_BTMAP_BEGIN 범위가 SWAPPER_TABLE_SHIFT 안에 있는지 확인


1385
1386         offset = dt_phys % SWAPPER_BLOCK_SIZE;
1387         dt_virt = (void *)dt_virt_base + offset;

(dt_virt)FIX_FDT의 가상주소와 (dt_phys) fdt의 물리주소를 SWAPPER_BLOCK_SIZE 단위로 끝자리 조정


1388
1389         /* map the first chunk so we can read the size from the header */
1390         create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE),
1391                         dt_virt_base, SWAPPER_BLOCK_SIZE, prot);
1392
1393         if (fdt_magic(dt_virt) != FDT_MAGIC)
1394                 return NULL;

정상적인 FDT_MAGIC 코드인지 확인


1395

1396         *size = fdt_totalsize(dt_virt);
1397         if (*size > MAX_FDT_SIZE)
1398                 return NULL;

fdt 최대크기 초과 확인


1399
1400         if (offset + *size > SWAPPER_BLOCK_SIZE)
1401                 create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), dt_virt_base,
1402                                round_up(offset + *size, SWAPPER_BLOCK_SIZE), prot);
1403
1404         return dt_virt;
1405 }


 444 /*
 445  * This function can only be used to modify existing table entries,
 446  * without allocating new levels of table. Note that this permits the
 447  * creation of new section or page entries.
 448  */

이 함수는 새로운 테이블 레벨을 할당하지 않고 기존 테이블 항목을 수정하는 데에만 사용할 수 있습니다. 이를 통해 새 섹션이나 페이지 항목을 생성할 수 있습니다.


 449 static void __init create_mapping_noalloc(phys_addr_t phys, unsigned long virt,
 450                                   phys_addr_t size, pgprot_t prot)
 451 {
 452         if ((virt >= PAGE_END) && (virt < VMALLOC_START)) {
 453                 pr_warn("BUG: not creating mapping for %pa at 0x%016lx - outside kernel range\n",
 454                         &phys, virt);
 455                 return;
 456         }
 457         __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
 458                              NO_CONT_MAPPINGS);
 459 }


 395 static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
 396                                  unsigned long virt, phys_addr_t size,
 397                                  pgprot_t prot,
 398                                  phys_addr_t (*pgtable_alloc)(int),
 399                                  int flags)
 400 {
 401         mutex_lock(&fixmap_lock);
 402         __create_pgd_mapping_locked(pgdir, phys, virt, size, prot,
 403                                     pgtable_alloc, flags);
 404         mutex_unlock(&fixmap_lock);
 405 }


 367 static void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys,
 368                                         unsigned long virt, phys_addr_t size,
 369                                         pgprot_t prot,
 370                                         phys_addr_t (*pgtable_alloc)(int),
 371                                         int flags)
 372 {
 373         unsigned long addr, end, next;
 374         pgd_t *pgdp = pgd_offset_pgd(pgdir, virt);

init_mm.init_pg_dir 을 PGD로 FIX_FDT를 가상주소로 하는 pgdp 확득.


 375
 376         /*
 377          * If the virtual and physical address don't have the same offset
 378          * within a page, we cannot map the region as the caller expects.
 379          */

페이지 내에서 가상 주소와 실제 주소의 오프셋이 동일하지 않으면 호출자가 예상하는 대로 지역을 매핑할 수 없습니다.


 380         if (WARN_ON((phys ^ virt) & ~PAGE_MASK))
 381                 return;
 382
 383         phys &= PAGE_MASK;
 384         addr = virt & PAGE_MASK;
 385         end = PAGE_ALIGN(virt + size);

물리주소와 가상주소를 Page 단위 내림정렬, end 주소 획득


 386
 387         do {
 388                 next = pgd_addr_end(addr, end);

다음 Mapping 주소 획득


 389                 alloc_init_pud(pgdp, addr, next, phys, prot, pgtable_alloc,
 390                                flags);
 391                 phys += next - addr;
 392         } while (pgdp++, addr = next, addr != end);
 393 }
 394


 

 311 static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
 312                            phys_addr_t phys, pgprot_t prot,
 313                            phys_addr_t (*pgtable_alloc)(int),
 314                            int flags)
 315 {
 316         unsigned long next;
 317         pud_t *pudp;
 318         p4d_t *p4dp = p4d_offset(pgdp, addr);
 319         p4d_t p4d = READ_ONCE(*p4dp);

FIX_FDT의 가상주소의 p4dp와 p4d 획득


 320
 321         if (p4d_none(p4d)) {
 322                 p4dval_t p4dval = P4D_TYPE_TABLE | P4D_TABLE_UXN;
 323                 phys_addr_t pud_phys;
 324
 325                 if (flags & NO_EXEC_MAPPINGS)
 326                         p4dval |= P4D_TABLE_PXN;

해당 page의 실행 금지를 위해 PXN 속성 설정


 327                 BUG_ON(!pgtable_alloc);
 328                 pud_phys = pgtable_alloc(PUD_SHIFT);
 329                 __p4d_populate(p4dp, pud_phys, p4dval);

p4dp에 배정받은 PUD의 물리주소 pud_phys를 연결


 330                 p4d = READ_ONCE(*p4dp);
 331         }
 332         BUG_ON(p4d_bad(p4d));

p4d에 table flag가 설정되어있지 않으면 BUG_ON


 333
 334         pudp = pud_set_fixmap_offset(p4dp, addr);

FIX_PUD에 addr(FIX_FDT)의 PUD를 만들고 pudp 획득


 335         do {
 336                 pud_t old_pud = READ_ONCE(*pudp);
 337
 338                 next = pud_addr_end(addr, end);

addr의 다음 pudp값 획득


 339
 340                 /*
 341                  * For 4K granule only, attempt to put down a 1GB block
 342                  */

Page 크기가 4KB인경우 1GB 블록 사용가능


 343                 if (pud_sect_supported() &&
 344                    ((addr | next | phys) & ~PUD_MASK) == 0 &&
 345                     (flags & NO_BLOCK_MAPPINGS) == 0) {
 346                         pud_set_huge(pudp, phys, prot);

Page 크기가 4KB이고, addr, next, phys 가 PUD offset 이 0이고, BLOCK Mapping 이 허락된경우, BLOCK Mapping 진행


 347
 348                         /*
 349                          * After the PUD entry has been populated once, we
 350                          * only allow updates to the permission attributes.
 351                          */
 352                         BUG_ON(!pgattr_change_is_safe(pud_val(old_pud),
 353                                                       READ_ONCE(pud_val(*pudp))));

old_pud (huge page 할당전 pud)와 pudp (huge page 할당후)의 mapping attributes를 비교하여 변경가능 범위인지 검사


 354                 } else {

 355                         alloc_init_cont_pmd(pudp, addr, next, phys, prot,
 356                                             pgtable_alloc, flags);
 357
 358                         BUG_ON(pud_val(old_pud) != 0 &&
 359                                pud_val(old_pud) != READ_ONCE(pud_val(*pudp)));
 360                 }
 361                 phys += next - addr;
 362         } while (pudp++, addr = next, addr != end);
 363
 364         pud_clear_fixmap();

bm_pte에서 FIX_PUD 연결 해제


 365 }


1407 int pud_set_huge(pud_t *pudp, phys_addr_t phys, pgprot_t prot)
1408 {
1409         pud_t new_pud = pfn_pud(__phys_to_pfn(phys), mk_pud_sect_prot(prot));
1410
1411         /* Only allow permission changes for now */
1412         if (!pgattr_change_is_safe(READ_ONCE(pud_val(*pudp)),
1413                                    pud_val(new_pud)))
1414                 return 0;

이전 상태에서 새로 바꾸려는 상태로 바꾸는 것이 오류를 발생 시키지 않는지 검사


1415
1416         VM_BUG_ON(phys & ~PUD_MASK);

물리 주소가 PUD 정렬이 되었는지 검사
1417         set_pud(pudp, new_pud);
1418         return 1;
1419 }


 

 270 static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
 271                                 unsigned long end, phys_addr_t phys,
 272                                 pgprot_t prot,
 273                                 phys_addr_t (*pgtable_alloc)(int), int flags)
 274 {
 275         unsigned long next;
 276         pud_t pud = READ_ONCE(*pudp);
 277
 278         /*
 279          * Check for initial section mappings in the pgd/pud.
 280          */
 281         BUG_ON(pud_sect(pud));

CONT Mapping 을 하려는 곳이라 Section Mapping 설정이면 오류


 282         if (pud_none(pud)) {
 283                 pudval_t pudval = PUD_TYPE_TABLE | PUD_TABLE_UXN;

커널 영역 관리에 사용되기에 UXN 설정


 284                 phys_addr_t pmd_phys;
 285
 286                 if (flags & NO_EXEC_MAPPINGS)
 287                         pudval |= PUD_TABLE_PXN;
 288                 BUG_ON(!pgtable_alloc);
 289                 pmd_phys = pgtable_alloc(PMD_SHIFT);
 290                 __pud_populate(pudp, pmd_phys, pudval);
 291                 pud = READ_ONCE(*pudp);
 292         }
 293         BUG_ON(pud_bad(pud));
 294
 295         do {
 296                 pgprot_t __prot = prot;
 297
 298                 next = pmd_cont_addr_end(addr, end);
 299
 300                 /* use a contiguous mapping if the range is suitably aligned */
 301                 if ((((addr | next | phys) & ~CONT_PMD_MASK) == 0) &&
 302                     (flags & NO_CONT_MAPPINGS) == 0)
 303                         __prot = __pgprot(pgprot_val(prot) | PTE_CONT);

addr, next, phys가 PMD상 CONT Mapping이 가능하고 CONT Mapping 불가 설정이 아니면  CONT Mapping


 304
 305                 init_pmd(pudp, addr, next, phys, __prot, pgtable_alloc, flags);
 306
 307                 phys += next - addr;
 308         } while (addr = next, addr != end);
 309 }


 233 static void init_pmd(pud_t *pudp, unsigned long addr, unsigned long end,
 234                      phys_addr_t phys, pgprot_t prot,
 235                      phys_addr_t (*pgtable_alloc)(int), int flags)
 236 {
 237         unsigned long next;
 238         pmd_t *pmdp;
 239
 240         pmdp = pmd_set_fixmap_offset(pudp, addr);

FIX_PMD사용하기 위해 bm_pte에 연결하고 


 241         do {
 242                 pmd_t old_pmd = READ_ONCE(*pmdp);
 243
 244                 next = pmd_addr_end(addr, end);
 245
 246                 /* try section mapping first */
 247                 if (((addr | next | phys) & ~PMD_MASK) == 0 &&
 248                     (flags & NO_BLOCK_MAPPINGS) == 0) {
 249                         pmd_set_huge(pmdp, phys, prot);

PMD가 BLOCK Mapping 가능한 경우


 250
 251                         /*
 252                          * After the PMD entry has been populated once, we
 253                          * only allow updates to the permission attributes.
 254                          */
 255                         BUG_ON(!pgattr_change_is_safe(pmd_val(old_pmd),
 256                                                       READ_ONCE(pmd_val(*pmdp))));
 257                 } else {
 258                         alloc_init_cont_pte(pmdp, addr, next, phys, prot,
 259                                             pgtable_alloc, flags);
 260
 261                         BUG_ON(pmd_val(old_pmd) != 0 &&
 262                                pmd_val(old_pmd) != READ_ONCE(pmd_val(*pmdp)));

pte 할당후 pmd 값이 변경된경우 BUG_ON


 263                 }
 264                 phys += next - addr;
 265         } while (pmdp++, addr = next, addr != end);
 266
 267         pmd_clear_fixmap();

FIX_PMD 을 bm_pte 에서 연결해제


 268 }

 


 194 static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
 195                                 unsigned long end, phys_addr_t phys,
 196                                 pgprot_t prot,
 197                                 phys_addr_t (*pgtable_alloc)(int),
 198                                 int flags)
 199 {
 200         unsigned long next;
 201         pmd_t pmd = READ_ONCE(*pmdp);
 202
 203         BUG_ON(pmd_sect(pmd));

section mapping 인경우 BUG_ON


 204         if (pmd_none(pmd)) {
 205                 pmdval_t pmdval = PMD_TYPE_TABLE | PMD_TABLE_UXN;
 206                 phys_addr_t pte_phys;
 207
 208                 if (flags & NO_EXEC_MAPPINGS)
 209                         pmdval |= PMD_TABLE_PXN;

실행금지 인경우  PXN설정


 210                 BUG_ON(!pgtable_alloc);
 211                 pte_phys = pgtable_alloc(PAGE_SHIFT);
 212                 __pmd_populate(pmdp, pte_phys, pmdval);
 213                 pmd = READ_ONCE(*pmdp);
 214         }
 215         BUG_ON(pmd_bad(pmd));
 216
 217         do {
 218                 pgprot_t __prot = prot;
 219
 220                 next = pte_cont_addr_end(addr, end);
 221
 222                 /* use a contiguous mapping if the range is suitably aligned */
 223                 if ((((addr | next | phys) & ~CONT_PTE_MASK) == 0) &&
 224                     (flags & NO_CONT_MAPPINGS) == 0)

addr, next, phys 가 PTE CONT (16page) 만큼 정렬되어 있고, CONT Mapping 금지가 아니라면 CONT Mapping


 225                         __prot = __pgprot(pgprot_val(prot) | PTE_CONT);
 226
 227                 init_pte(pmdp, addr, next, phys, __prot);
 228
 229                 phys += next - addr;
 230         } while (addr = next, addr != end);
 231 }

 


 

 170 static void init_pte(pmd_t *pmdp, unsigned long addr, unsigned long end,
 171                      phys_addr_t phys, pgprot_t prot)
 172 {
 173         pte_t *ptep;
 174
 175         ptep = pte_set_fixmap_offset(pmdp, addr);
 176         do {
 177                 pte_t old_pte = READ_ONCE(*ptep);
 178
 179                 set_pte(ptep, pfn_pte(__phys_to_pfn(phys), prot));

FIX_PTE(가상주소) 와 fdt (물리주소)를 연결


 180
 181                 /*
 182                  * After the PTE entry has been populated once, we
 183                  * only allow updates to the permission attributes.
 184                  */
 185                 BUG_ON(!pgattr_change_is_safe(pte_val(old_pte),
 186                                               READ_ONCE(pte_val(*ptep))));

pte 상태값 변경이 오류를 이르키지 않는지 검사

 

 187
 188                 phys += PAGE_SIZE;
 189         } while (ptep++, addr += PAGE_SIZE, addr != end);
 190
 191         pte_clear_fixmap();

FIX_PTE 를 bm_pte에서 해제


 192 }

 

 

'linux' 카테고리의 다른 글

6.1/early_ioremap_init(void)  (0) 2025.03.29
6.1/setup_arch(char **cmdline_p)  (0) 2024.04.02
6.1/early_fixmap_init(void)  (0) 2023.12.18
6.1/early_fdt_map(u64 dt_phys)  (0) 2023.12.18
6.1/Head.S  (0) 2023.11.16

fdt와 FIX_FDT의 주소변환 테이블 구성

 

 

1273 /*
1274  * The p*d_populate functions call virt_to_phys implicitly so they can't be used
1275  * directly on kernel symbols (bm_p*d). This function is called too early to use
1276  * lm_alias so __p*d_populate functions must be used to populate with the
1277  * physical address from __pa_symbol.
1278  */

p*d_populate 함수는 virt_to_phys를 암시적으로 호출하므로 커널 기호(bm_p*d)에서 직접 사용할 수 없습니다.
이 함수는 lm_alias를 사용하기에는 너무 일찍 호출되므로 __p*d_populate 함수를 사용하여 __pa_symbol의 물리적 주소를 채워야 합니다.


1279 void __init early_fixmap_init(void)
1280 {
1281         pgd_t *pgdp;
1282         p4d_t *p4dp, p4d;
1283         pud_t *pudp;
1284         pmd_t *pmdp;
1285         unsigned long addr = FIXADDR_START;

pgdp : addr의 pud의 시작주소(물리주소)를 담고있는 PGD의 위치

pudp : addr의  pmd의 시작주소(물리주소)를 담고있는 PUD의 위치

pmdp : addr의  ptd의 시작주소(물리주소)를 담고있는 PMD의 위치


1286
1287         pgdp = pgd_offset_k(addr);

addr 의 init_mm.init_pg_dir 을 PGD 로하는 pgdp

 

1288         p4dp = p4d_offset(pgdp, addr);

1289         p4d = READ_ONCE(*p4dp);

arm64는 최대 4단계 주소변환을 지원하여 P4D 사용 안함.

p4dp = (p4d_t *)pgdp;

 

1290         if (CONFIG_PGTABLE_LEVELS > 3 &&
1291             !(p4d_none(p4d) || p4d_page_paddr(p4d) == __pa_symbol(bm_pud))) {
1292                 /*
1293                  * We only end up here if the kernel mapping and the fixmap
1294                  * share the top level pgd entry, which should only happen on
1295                  * 16k/4 levels configurations.
1296                  */

커널 매핑과 Fixmap이 최상위 레벨 pgd 항목을 공유하는 경우에만 여기서 끝나게 됩니다. 이는 16k/4 레벨 구성에서만 발생해야 합니다.


1297                 BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));
1298                 pudp = pud_offset_kimg(p4dp, addr);

addr의 pudp(가상주소)값 얻어옮


1299         } else {
1300                 if (p4d_none(p4d))
1301                         __p4d_populate(p4dp, __pa_symbol(bm_pud), P4D_TYPE_TABLE);

p4d(pgd)에 PUD가 미연결인경우 bm_pud로 연결

 

1302                 pudp = fixmap_pud(addr);

addr 의 pudp(가상주소)값 얻어옮


1303         }
1304         if (pud_none(READ_ONCE(*pudp)))
1305                 __pud_populate(pudp, __pa_symbol(bm_pmd), PUD_TYPE_TABLE);

pudp에 PMD가 미연결인경우 bm_pmd로 연결


1306         pmdp = fixmap_pmd(addr);

addr 의 pmdp(가상주소)값 얻어옮


1307         __pmd_populate(pmdp, __pa_symbol(bm_pte), PMD_TYPE_TABLE);

pmdp에 PTE로 bm_pte 연결


1308

1309         /*
1310          * The boot-ioremap range spans multiple pmds, for which
1311          * we are not prepared:
1312          */
1313         BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
1314                      != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));

FIX_BTMAP_BEGIN ~ FIX_BTMAP_END 까지 같은 PMD 값을 갖아야 한다.


1315
1316         if ((pmdp != fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)))
1317              || pmdp != fixmap_pmd(fix_to_virt(FIX_BTMAP_END))) {

FIXADDR_START, FIX_BTMAP_BEGIN, FIX_BTMAP_END는 같은 pmdp를 갖는다.

즉 2MB 범위안에  존재해야한다. (Fixmap API는 원래 early_ioremap 목적으로 arm64에 추가되었습니다.
다른 목적으로도 사용될 수 있으므로 초기화를 ioremap에서 좀 더 일반적인 곳으로 옮깁니다.
이렇게 하면 Fixmap이 설정되는 위치가 명확해지고 __set_fixmap을 더 깔끔하게 구현할 수 있습니다.)

https://github.com/torvalds/linux/commit/af86e5974d3069bd26ebcf7c046c6e59726acaaa


1318                 WARN_ON(1);
1319                 pr_warn("pmdp %p != %p, %p\n",
1320                         pmdp, fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)),
1321                         fixmap_pmd(fix_to_virt(FIX_BTMAP_END)));
1322                 pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
1323                         fix_to_virt(FIX_BTMAP_BEGIN));
1324                 pr_warn("fix_to_virt(FIX_BTMAP_END):   %08lx\n",
1325                         fix_to_virt(FIX_BTMAP_END));
1326
1327                 pr_warn("FIX_BTMAP_END:       %d\n", FIX_BTMAP_END);
1328                 pr_warn("FIX_BTMAP_BEGIN:     %d\n", FIX_BTMAP_BEGIN);
1329         }
1330 }
1331


 

'linux' 카테고리의 다른 글

6.1/setup_arch(char **cmdline_p)  (0) 2024.04.02
6.1/fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)  (0) 2023.12.19
6.1/early_fdt_map(u64 dt_phys)  (0) 2023.12.18
6.1/Head.S  (0) 2023.11.16
6.1/kaslr_requires_kpti(void)  (0) 2023.11.10

173 asmlinkage void __init early_fdt_map(u64 dt_phys)
174 {
175         int fdt_size;
176
177         early_fixmap_init();

가상주소 FIXADDR_START를  init_pg_dir을 PGD로 bm_pud, bm_pmd, bm_pte로 하는 mapping table 작성

(연결된 물리 주소 없음)

FIXMAP 영역 mapping 용


178         early_fdt_ptr = fixmap_remap_fdt(dt_phys, &fdt_size, PAGE_KERNEL);
179 }

 

dt_phys (fdt의 물리주소)와 FIX_FDT (가상주소)를 mapping

 

'linux' 카테고리의 다른 글

6.1/fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)  (0) 2023.12.19
6.1/early_fixmap_init(void)  (0) 2023.12.18
6.1/Head.S  (0) 2023.11.16
6.1/kaslr_requires_kpti(void)  (0) 2023.11.10
6.1/smp_setup_processor_id()  (0) 2023.02.23

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)

 

'linux' 카테고리의 다른 글

6.1/early_fixmap_init(void)  (0) 2023.12.18
6.1/early_fdt_map(u64 dt_phys)  (0) 2023.12.18
6.1/kaslr_requires_kpti(void)  (0) 2023.11.10
6.1/smp_setup_processor_id()  (0) 2023.02.23
6.1/set_task_stack_end_magic()  (0) 2023.02.09

 

1587 /*
1588  * This check is triggered during the early boot before the cpufeature
1589  * is initialised. Checking the status on the local CPU allows the boot
1590  * CPU to detect the need for non-global mappings and thus avoiding a
1591  * pagetable re-write after all the CPUs are booted. This check will be
1592  * anyway run on individual CPUs, allowing us to get the consistent
1593  * state once the SMP CPUs are up and thus make the switch to non-global
1594  * mappings if required.
1595  */

 

이 검사는 CPU 기능이 초기화되기 전 초기 부팅 중에 트리거됩니다.
로컬 CPU의 상태를 확인하면 부팅 CPU가 비전역 매핑의 필요성을 감지할 수 있으므로 모든 CPU가 부팅된 후 페이지 테이블 다시 쓰기를 방지할 수 있습니다.
이 검사는 어쨌든 개별 CPU에서 실행되므로 SMP CPU가 가동되면 일관된 상태를 얻을 수 있으므로 필요한 경우 비전역 매핑으로 전환할 수 있습니다.


1596 bool kaslr_requires_kpti(void)
1597 {
1598         if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE))
1599                 return false;

Kernel 이 임의의 주소에 위치할 수 있는지 여부


1600
1601         /*
1602          * E0PD does a similar job to KPTI so can be used instead
1603          * where available.
1604          */

 

KPTI : user 모드와 kernel 모드 별로 Page Table을 별도로 관리

https://hyeyoo.com/120

 

[Linux Kernel] KPTI: Kernel Page-Table Isolation

예전부터 "커널의 페이지 테이블 엔트리는 어디에 있을까?" 가 궁금했는데 최근에 공부하다 궁금증이 해결되었다. 이 질문에 대한 답변은 KPTI를 적용하냐 아니냐에 따라 나뉜다. 우선 적용하지

hyeyoo.com

 

E0PD는 KPTI의 기능을 대신할수 있음. 하드웨어 지원으로 KPTI와 달리 성능상의 하락 없음

E0PD가 있는 CPU에서는 KPTI의 기능 끄기

 

 

E0PD, bits [63:60]

https://developer.arm.com/documentation/ddi0601/2023-09/AArch64-Registers/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2?lang=en

 

https://developer.arm.com/documentation/101800/0201/AArch64-registers/AArch64-Identification-register-summary/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2?lang=en

 

 

1605         if (IS_ENABLED(CONFIG_ARM64_E0PD)) {
1606                 u64 mmfr2 = read_sysreg_s(SYS_ID_AA64MMFR2_EL1);
1607                 if (cpuid_feature_extract_unsigned_field(mmfr2,
1608                                                 ID_AA64MMFR2_EL1_E0PD_SHIFT))
1609                         return false;
1610         }

E0PD기능이 있는 경우 KPTI 기능 필요없음(false)

 

1611

1612         /*
1613          * Systems affected by Cavium erratum 24756 are incompatible
1614          * with KPTI.
1615          */
1616         if (IS_ENABLED(CONFIG_CAVIUM_ERRATUM_27456)) {
1617                 extern const struct midr_range cavium_erratum_27456_cpus[];
1618
1619                 if (is_midr_in_range_list(read_cpuid_id(),
1620                                           cavium_erratum_27456_cpus))
1621                         return false;
1622         }

Cavium erratum 27456

Cavium   ThunderX에 대한 errta 

 

On ThunderX T88 pass 1.x through 2.1 parts, broadcast TLBI instructions may cause the icache to become corrupted if it contains data for a non-current ASID.  
The fix is to invalidate the icache when changing the mm context.

 

ThunderX T88 패스 1.x에서 2.1 부분까지, 브로드캐스트 TLBI 명령으로 인해 현재가 아닌 ASID에 대한 데이터가 포함된 경우 icache가 손상될 수 있습니다.
해결 방법은 mm 컨텍스트를 변경할 때 icache를 무효화하는 것입니다.

 

TLB (Translation Lookaside Buffer) : 가상주소와 물리주소를 Mapping하는 Page Table 의 Cache

 

https://wpaud16.tistory.com/304

 

TLB란? ( page table, 48bit 가상 공간, virtual memory, ASID , TTBR, arm )

TLB (Translation Lookaside Buffer) 요놈을 알기 위해선 page table이라는 개념을 알고 있어야 한다. 알아보자 Page Table page table이란 간단히 말해 가상 주소와 실제 주소를 mapping 하는 table이다. CPU가 가상 주

wpaud16.tistory.com

 

1623
1624         return kaslr_offset() > 0;
1625 }

 


 

 

201 static inline unsigned long kaslr_offset(void)
202 {
203     return kimage_vaddr KIMAGE_VADDR;

==> 0xFFFF-8000-0800-0000 - 0xFFFF-8000-00F8-0000 = 0x708 0000
204 }

 

 53 u64 kimage_vaddr __ro_after_init = (u64)&_text;

==> System.map (VA_BITS = 48 인 경우)

0xFFFF-8000-0800-0000 T _text

 

 46 #define KIMAGE_VADDR        (MODULES_END)

==> 0xFFFF-8000-00F8-0000


 47 #define MODULES_END     (MODULES_VADDR + MODULES_VSIZE)
 48 #define MODULES_VADDR       (_PAGE_END(VA_BITS_MIN))

==> VA_BITS_MIN = 48 인 경우 0xFFFF-8000-0000-0000

 

 49 #define MODULES_VSIZE       (SZ_128M)

==> 0x00F8-0000

 

 62 #define _PAGE_END(va)       (-(UL(1) << ((va) - 1)))

 

 

 

 

'linux' 카테고리의 다른 글

6.1/early_fdt_map(u64 dt_phys)  (0) 2023.12.18
6.1/Head.S  (0) 2023.11.16
6.1/smp_setup_processor_id()  (0) 2023.02.23
6.1/set_task_stack_end_magic()  (0) 2023.02.09
Linux Kernel 6.1/start_kernel(void)  (0) 2023.02.09

arch/arm64/kernel/setup.c

 86 void __init smp_setup_processor_id(void)
 87 {
 88         u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;

 89         set_cpu_logical_map(0, mpidr);

현재 커널이 동작 중인 CPU 번호를 논리 CPU 번호 "0"으로 할당


 90
 91         pr_info("Booting Linux on physical CPU 0x%010lx [0x%08x]\n",
 92                 (unsigned long)mpidr, read_cpuid_id());
 93 }


arch/arm64/include/asm/cputype.h

251 static inline u64 __attribute_const__ read_cpuid_mpidr(void)
252 {
253     return read_cpuid(MPIDR_EL1);
현재 커널이 실행중인 CPU번호 반환

 

254 }

 

MPIDR_EL1 : https://developer.arm.com/documentation/ddi0601/2024-09/AArch64-Registers/MPIDR-EL1--Multiprocessor-Affinity-Register?lang=en

 


arch/arm64/include/asm/smp.h

 51 static inline void set_cpu_logical_map(unsigned int cpu, u64 hwid)
 52 {
 53     __cpu_logical_map[cpu] = hwid;
 54 }


246 static inline u32 __attribute_const__ read_cpuid_id(void)
247 {
248     return read_cpuid(MIDR_EL1);

CPU 밴더, 버전등 정보 반환
249 }

 

MIDR_EL1 : https://developer.arm.com/documentation/ddi0601/2024-09/AArch64-Registers/MIDR-EL1--Main-ID-Register?lang=en

 

'linux' 카테고리의 다른 글

6.1/Head.S  (0) 2023.11.16
6.1/kaslr_requires_kpti(void)  (0) 2023.11.10
6.1/set_task_stack_end_magic()  (0) 2023.02.09
Linux Kernel 6.1/start_kernel(void)  (0) 2023.02.09
Linux kernel 5.0 ARM64 Compile  (0) 2019.03.12

+ Recent posts