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

+ Recent posts