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