Early ioremap

Early IO 매핑

paging_init()이 호출되기 전, 즉 페이지 테이블이 준비되지 않은 이른(early) 시간에 io 매핑이 필요한 경우 256K씩 최대 7개의 fixmap 매핑 공간을 사용하여 매핑을 할 수 있게 한다.

 

 

Early IO 매핑 초기화

early_ioremap_init()

/*
 * Must be called after early_fixmap_init
 */
void __init early_ioremap_init(void)
{
        early_ioremap_setup();
}

paging_init()이 완료되지 않아 정규 ioremap() 함수를 사용하지 못하지만 이른(early) 시간에 임시적으로 매핑이 필요한  경우를 위해 준비한다.

  • arm에서는 준비되지 않았고 arm64나 x86 아키텍처에서 동작한다.
  • 7개까지 fixmap을 이용하므로 early_fixmap_init() 함수가 먼저 호출되어야한다.

 

early_ioremap_setup()

mm/early_ioremap.c

void __init early_ioremap_setup(void)
{
        int i;

        for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
                if (WARN_ON(prev_map[i]))
                        break;

        for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
                slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i);
}

7개 배열로 이루어진 slot_virt[] 배열에 fixmap에서 early ioremap 용도로 배정된 가상 주소를 배정한다.

  • 7개 각 주소 공간은 최대 256K를 사용할 수 있다.

 

 

early_ioremap()

mm/early_ioremap.c

/* Remap an IO device */
void __init __iomem *
early_ioremap(resource_size_t phys_addr, unsigned long size)
{
        return __early_ioremap(phys_addr, size, FIXMAP_PAGE_IO);
}

최대 7개 까지 io 용도로 early io 매핑한다. 그리고 매핑된 가상 주소를 반환한다.

 

early_memremap()

mm/early_ioremap.c

/* Remap memory */ 
void __init *
early_memremap(resource_size_t phys_addr, unsigned long size)
{
        return (__force void *)__early_ioremap(phys_addr, size,
                                               FIXMAP_PAGE_NORMAL);
}

최대 7개 까지 메모리 용도로 early 매핑한다. 그리고 매핑된 가상 주소를 반환한다.

 

__early_ioremap()

mm/early_ioremap.c

static void __init __iomem *
__early_ioremap(resource_size_t phys_addr, unsigned long size, pgprot_t prot)
{
        unsigned long offset;
        resource_size_t last_addr;
        unsigned int nrpages;
        enum fixed_addresses idx;
        int i, slot;

        WARN_ON(system_state != SYSTEM_BOOTING);

        slot = -1;
        for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
                if (!prev_map[i]) {
                        slot = i;
                        break;
                }
        }

        if (WARN(slot < 0, "%s(%08llx, %08lx) not found slot\n",
                 __func__, (u64)phys_addr, size))
                return NULL;

        /* Don't allow wraparound or zero size */
        last_addr = phys_addr + size - 1;
        if (WARN_ON(!size || last_addr < phys_addr))
                return NULL;

        prev_size[slot] = size;
        /*
         * Mappings have to be page-aligned
         */
        offset = phys_addr & ~PAGE_MASK;
        phys_addr &= PAGE_MASK;
        size = PAGE_ALIGN(last_addr + 1) - phys_addr;

        /*
         * Mappings have to fit in the FIX_BTMAP area.
         */
        nrpages = size >> PAGE_SHIFT;
        if (WARN_ON(nrpages > NR_FIX_BTMAPS))
                return NULL;

        /*
         * Ok, go for it..
         */
        idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
        while (nrpages > 0) {
                if (after_paging_init)
                        __late_set_fixmap(idx, phys_addr, prot);
                else
                        __early_set_fixmap(idx, phys_addr, prot);
                phys_addr += PAGE_SIZE;
                --idx;
                --nrpages;
        }
        WARN(early_ioremap_debug, "%s(%08llx, %08lx) [%d] => %08lx + %08lx\n",
             __func__, (u64)phys_addr, size, slot, offset, slot_virt[slot]);

        prev_map[slot] = (void __iomem *)(offset + slot_virt[slot]);
        return prev_map[slot];
}

요청한 물리 주소와 사이즈를 fixmap의 BTMAPS 슬롯에 해당하는 가상주소에 prot 속성으로 매핑한다. 실패하는 경우 null을 반환한다. arm64에서 early 하게 io 매핑할 수 있는 최대 수는 FIX_BTMAPS_SLOTS(7)개이다.

  • fixmap용 BTMAPS 슬롯 관리 정보
    • slot_virt[]: 부트업 타임에 초기화된 각 슬롯에 해당하는 fixmap 가상 주소 (256K 단위)
    • prev_map[]: fixmap에 매핑된 가상 주소
    • prev_size[]: 매핑된 사이즈

 

  • 코드 라인 12-18에서 사용할 빈 슬롯을 선택한다. 루프를 도는 동안 빈 슬롯을 못 찾은 경우 경고 메시지를 출력하고 null 값을 반환한다
    • FIX_BTMAPS_SLOTS(7) 수 만큼 루프를 돌며 전역 prev_map[] 값이 설정되지 않은 경우 현재 카운터 i 값을 slot에 대입하여 slot을 선택한다.
  • 코드 라인 26에서 사이즈가 0이거나 끝 주소가 시스템 주소 범위를 벗어나는 경우 경고 메시지를 출력하고 null 값을 반환한다.
  • 코드 라인 29~34에서 prev_size[]에 요청 사이즈를 담아둔다. offset는 요청 물리 주소에서 페이지 단위의 나머지 offset 만을 산출하고, 물리 주소는 페이지 단위로 절삭한다.
  • 코드 라인 35에서 페이지 단위로 정렬된 사이즈 값을 산출한다.
    • 처음 요청한 물리 시작 주소 + 사이즈값을 페이지 단위로 정렬시키고 페이지 단위로 절삭된 물리 주소를 뺀다. (최소 값은 페이지 단위가 된다)
  • 코드 라인 40에서 사이즈 값으로 페이지 수를 산출하고 한다. 사이즈가 256K를 초과하는 경우 null을 반환한다.
  • 코드 라인 48에서 산출된 페이지 수만큼 루프를 돈다.
  • 코드 라인 49~50에서 전역 after_pagiong_init이 설정된 경우 __late_clear_fixmap() 매크로 함수를 호출한다.
    • 예) arm64 및 x86의 경우 별도의 late 처리 함수 없이 그냥 __set_fixmap() 함수를 호출한다.
  • 코드 섹션 51~52에서 반대로 after_pagiong_init이 설정되지 않은 경우 __early_set_fixmap() 매크로 함수를 호출한다.
    • 예) arm64의 경우 별도의 early 처리 함수 없이 그냥 __set_fixmap() 함수를 호출한다.
    • 예) x64의 경우 별도의 early 처리 함수인 __early_set_fixmap() 함수를 호출한다.
  • 코드 섹션 60에서 prev_map[slot]에 슬롯에 해당하는 fixmap 가상 주소 + offset을 더해 대입한다.

 

__early_set_fixmap()

arm64/include/asm/fixmap.h

#define __early_set_fixmap __set_fixmap

paging_init()이 완료되기 전에는 early_ioremap() 함수가 호출되는 경우에는 fixmap에 매핑한다.

 

__late_set_fixmap()

mm/early_ioremap.c

/*
 * Generally, ioremap() is available after paging_init() has been called.
 * Architectures wanting to allow early_ioremap after paging_init() can
 * define __late_set_fixmap and __late_clear_fixmap to do the right thing.
 */                  
#ifndef __late_set_fixmap
static inline void __init __late_set_fixmap(enum fixed_addresses idx,
                                            phys_addr_t phys, pgprot_t prot)
{       
        BUG();          
}       
#endif

paging_init()이 완료된 후 early_ioremap()  함수를 호출하는 경우 버그를 출력한다.

  • x86 아키텍처에서와 같이 버그 출력 없이 그냥 fixmap에 매핑시키도록 허용하기도 한다.

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.