Ioremap

 

io 매핑

 

정규 ioremap 함수는 빈 vmalloc 영역을 찾아 매핑하는데 매핑 종류에 따라 5개의 함수를 사용한다. 아키텍처마다 지원되는 캐시의 종류가 다르므로 특정 모드가 지원되지  않는 경우 하향 호환을 시킨다. ioremap_cache() 함수는 캐시를 사용하지만 아키텍처에 따라 읽기 캐시외에 쓰기 캐시까지도 사용할 수 있다. 쓰기 캐시에 대한 옵션을 가능하면 선택할 수 있도록 하기 위해 특정 아키텍처에서는 ioremap_wc() 함수와 ioremap_wt() 함수를 구분하여 사용하기도 한다. 참고로 다음 표와 같이 arm 및 arm64 에서 원래 함수 의도와는 다르게 약간 상이한 매핑 타입을 확인할 수 있다.

 

 

32bit arm ioremap 함수 흐름도

arm64 ioremap 함수 흐름도

ioremap() 등

arch/arm/include/asm/io.h

/*
 * ioremap and friends.
 *
 * ioremap takes a PCI memory address, as specified in
 * Documentation/io-mapping.txt.
 *
 */
#define ioremap(cookie,size)            __arm_ioremap((cookie), (size), MT_DEVICE)
#define ioremap_nocache(cookie,size)    __arm_ioremap((cookie), (size), MT_DEVICE)
#define ioremap_cache(cookie,size)      __arm_ioremap((cookie), (size), MT_DEVICE_CACHED)
#define ioremap_wc(cookie,size)         __arm_ioremap((cookie), (size), MT_DEVICE_WC)

arm64에서 ioremap(), ioremap_nocache(), ioremap_wt() 함수는 PROT_DEVICE_nGnRE 매핑을 사용하여 캐시를 사용하지 디바이스 타입으로 매핑하고, ioremap_wc() 함수는 캐시를 사용하지 않는 메모리 타입으로 매핑한다.

 

__arm_ioremap()

arch/arm/mm/ioremap.c

void __iomem *
__arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype)
{
        return arch_ioremap_caller(phys_addr, size, mtype,
                __builtin_return_address(0));
}
EXPORT_SYMBOL(__arm_ioremap);

요청 물리주소 및 사이즈만큼 빈 vmalloc 공간을 찾아 mtype 속성으로 매핑하고 가상 주소를 반환한다.

 

전역 __arm_ioremap_caller 함수 포인터 변수

arch/arm/mm/ioremap.c

void __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t,
                                      unsigned int, void *) =
        __arm_ioremap_caller;

컴파일 타임에 전역 arch_ioremap_caller 함수 포인터 변수에 __arm_ioremap_caller() 함수 주소를 대입한다.

 

__arm_ioremap_caller()

arch/arm/mm/ioremap.c

void __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size,
        unsigned int mtype, void *caller)
{
        phys_addr_t last_addr;
        unsigned long offset = phys_addr & ~PAGE_MASK;
        unsigned long pfn = __phys_to_pfn(phys_addr);

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

        return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,
                        caller);
}

요청 물리주소 및 사이즈만큼 빈 vmalloc 공간을 찾아 mtype 속성으로 매핑하고 가상 주소를 반환한다.

  • 코드 라인 5~6에서 물리 주소에서 페이지 단위로 절삭하여 남은 offset 값과 pfn 값을 구한다.
  • 코드 라인 11에서 페이지 단위로 매핑할 페이지의 끝 주소를 구한다.
    • 예) phys_addr=0x1234_5000, size=0x2000
      • last_addr=0x1234_6fff
  • 코드 라인 12~13에서 size가 0이거나 끝 주소가 시스템 범위를 초과하는 경우 null을 반환한다.
  • 코드 라인 15에서 pfn에 대해 size 만큼 빈 vmalloc 공간을 찾아 mtype 속성으로 매핑하고 가상 주소를 반환한다.

 

__arm_ioremap_pfn_caller()

arch/arm/mm/ioremap.c

void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,
        unsigned long offset, size_t size, unsigned int mtype, void *caller)
{
        const struct mem_type *type;
        int err;
        unsigned long addr;
        struct vm_struct *area;
        phys_addr_t paddr = __pfn_to_phys(pfn);

#ifndef CONFIG_ARM_LPAE
        /*
         * High mappings must be supersection aligned
         */
        if (pfn >= 0x100000 && (paddr & ~SUPERSECTION_MASK))
                return NULL;
#endif

        type = get_mem_type(mtype);
        if (!type)
                return NULL;

        /*
         * Page align the mapping size, taking account of any offset.
         */
        size = PAGE_ALIGN(offset + size);

        /*
         * Try to reuse one of the static mapping whenever possible.
         */
        if (size && !(sizeof(phys_addr_t) == 4 && pfn >= 0x100000)) {
                struct static_vm *svm;

                svm = find_static_vm_paddr(paddr, size, mtype);
                if (svm) {
                        addr = (unsigned long)svm->vm.addr;
                        addr += paddr - svm->vm.phys_addr;
                        return (void __iomem *) (offset + addr);
                }
        }

        /*
         * Don't allow RAM to be mapped - this causes problems with ARMv6+
         */
        if (WARN_ON(pfn_valid(pfn)))
                return NULL;

        area = get_vm_area_caller(size, VM_IOREMAP, caller);
        if (!area)
                return NULL;
        addr = (unsigned long)area->addr;
        area->phys_addr = paddr;

#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
        if (DOMAIN_IO == 0 &&
            (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
               cpu_is_xsc3()) && pfn >= 0x100000 &&
               !((paddr | size | addr) & ~SUPERSECTION_MASK)) {
                area->flags |= VM_ARM_SECTION_MAPPING;
                err = remap_area_supersections(addr, pfn, size, type);
        } else if (!((paddr | size | addr) & ~PMD_MASK)) {
                area->flags |= VM_ARM_SECTION_MAPPING;
                err = remap_area_sections(addr, pfn, size, type);
        } else
#endif
                err = ioremap_page_range(addr, addr + size, paddr,
                                         __pgprot(type->prot_pte));

        if (err) {
                vunmap((void *)addr);
                return NULL;
        }

        flush_cache_vmap(addr, addr + size);
        return (void __iomem *) (offset + addr);
}

요청 물리주소 및 사이즈만큼 빈 vmalloc 공간을 찾아 mtype 속성으로 매핑하고 가상 주소를 반환한다.

  • 코드 라인 18~20에서 요청한 mtype에 해당하는 mem_type 구조체를 알아오는데 없는 경우 null을 반환한다.
  • 코드 라인 25에서 매핑에 필요한 페이지 사이즈를 구하기 위해 size에 offset을 더한 값을 페이지 단위로 정렬한다.
    • 예) offset=0x678, size=0x1000, PAGE_SIZE=4K
      • size=0x2000 (2개 페이지)
  • 코드 라인 30에서  물리 주소가 4이면서 pfn이 0x10000(4G)를 초과하는 경우만 아니라면
  • 코드 라인 33~38에서 vmalloc 공간에 이미 동일한 타입으로 static 매핑 영역이내에 포함된 경우 매핑 없이 가상 주소만 찾아 반환한다.
  • 코드 라인 47~49에서 VM_IOREMAP 플래그를 사용하여 vmalloc 공간에서 빈 영역을 찾아 영역 정보를 vm_struct 구조체 형태로 알아오고 null인 경우 함수를 빠져나간다.
  • 코드 라인 50~51에서 찾은 시작 가상 주소를 addr에 대입하고 영역의 시작 물리 주소에 paddr을 대입한다.
  • 코드 라인 53~64에서 CONFIG_SMP 커널 옵션을 사용하지 않고 LPAE도 아닌 경우 수퍼 섹션 또는 섹션 매핑을 지원한다.
  • 코드 라인 65~66에서 가상 주소 범위만큼 물리 주소 paddr부터 매핑한다.
  • 코드 라인 68~71 매핑이 실패하는 경우 매핑해제를 시도한다.
  • 코드 라인 73에서 매핑된 가상 주소 영역을 flush한다. 단 armv7 및 armv8 아키텍처와 같이 데이터 캐시가 PIPT 또는 VIPT non-aliasing을 사용하는 경우에는 flush할 필요없다

 

find_static_vm_paddr()

arch/arm/mm/ioremap.c

static struct static_vm *find_static_vm_paddr(phys_addr_t paddr,
                        size_t size, unsigned int mtype)
{
        struct static_vm *svm;
        struct vm_struct *vm;

        list_for_each_entry(svm, &static_vmlist, list) {
                vm = &svm->vm;
                if (!(vm->flags & VM_ARM_STATIC_MAPPING))
                        continue;
                if ((vm->flags & VM_ARM_MTYPE_MASK) != VM_ARM_MTYPE(mtype))
                        continue;

                if (vm->phys_addr > paddr ||
                        paddr + size - 1 > vm->phys_addr + vm->size - 1)
                        continue;

                return svm;
        }

        return NULL;
}

요청 영역이 vmalloc 공간에 매핑된 영역 엔트리 중 하나에 포함되고 같은 매핑 타입으로 이미 static 매핑된 경우 해당 vm_struct 정보를 반환한다. 그 외의 경우 null을 반환한다.

  • 코드 라인 7에서 전역 static_vmlist의 모든 엔트리를 대상으로 루프를 돈다.
  • 코드 라인 9~10에서 해당 영역이 VM_ARM_STATIC_MAPPING 플래그를 사용하지 않으면 skip 한다.
  • 코드 라인 11~12에서 해당 영역에 매핑된 타입과 요청 매핑 타입이 다른 경우 skip 한다.
  • 코드 라인 14~16에서 기존 영역의 범위내에 요청 범위가 포함되지 않은 경우 skip 한다.

 

remap_area_sections()

arch/arm/mm/ioremap.c

static int
remap_area_sections(unsigned long virt, unsigned long pfn,
                    size_t size, const struct mem_type *type)
{
        unsigned long addr = virt, end = virt + size;
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;

        /*
         * Remove and free any PTE-based mapping, and
         * sync the current kernel mapping.
         */
        unmap_area_sections(virt, size);

        pgd = pgd_offset_k(addr);
        pud = pud_offset(pgd, addr);
        pmd = pmd_offset(pud, addr);
        do {
                pmd[0] = __pmd(__pfn_to_phys(pfn) | type->prot_sect);
                pfn += SZ_1M >> PAGE_SHIFT;
                pmd[1] = __pmd(__pfn_to_phys(pfn) | type->prot_sect);
                pfn += SZ_1M >> PAGE_SHIFT;
                flush_pmd_entry(pmd);

                addr += PMD_SIZE;
                pmd += 2;
        } while (addr < end);

        return 0;
}

1M 단위 섹션 매핑이 가능한 경우 해당 pmd 엔트리를 섹션 페이지에 매핑한다.

  • arm에서 pmd 엔트리는 pmd[0] 및 pmd[1] 2개로 구성되어 있다.

 

unmap_area_sections()

lib/ioremap.c

/*
 * Section support is unsafe on SMP - If you iounmap and ioremap a region,
 * the other CPUs will not see this change until their next context switch.
 * Meanwhile, (eg) if an interrupt comes in on one of those other CPUs
 * which requires the new ioremap'd region to be referenced, the CPU will
 * reference the _old_ region.
 *
 * Note that get_vm_area_caller() allocates a guard 4K page, so we need to
 * mask the size back to 1MB aligned or we will overflow in the loop below.
 */
static void unmap_area_sections(unsigned long virt, unsigned long size)
{
        unsigned long addr = virt, end = virt + (size & ~(SZ_1M - 1));
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmdp;

        flush_cache_vunmap(addr, end);
        pgd = pgd_offset_k(addr);
        pud = pud_offset(pgd, addr);
        pmdp = pmd_offset(pud, addr);
        do {
                pmd_t pmd = *pmdp;

                if (!pmd_none(pmd)) {
                        /*
                         * Clear the PMD from the page table, and
                         * increment the vmalloc sequence so others
                         * notice this change.
                         *
                         * Note: this is still racy on SMP machines.
                         */
                        pmd_clear(pmdp);
                        init_mm.context.vmalloc_seq++;

                        /*
                         * Free the page table, if there was one.
                         */
                        if ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_TABLE)
                                pte_free_kernel(&init_mm, pmd_page_vaddr(pmd));
                }

                addr += PMD_SIZE;
                pmdp += 2;
        } while (addr < end);

        /*
         * Ensure that the active_mm is up to date - we want to
         * catch any use-after-iounmap cases.
         */
        if (current->active_mm->context.vmalloc_seq != init_mm.context.vmalloc_seq)
                __check_vmalloc_seq(current->active_mm);

        flush_tlb_kernel_range(virt, end);
}

vmalloc 공간에서 pmd 매핑된 엔트리를 클리어한다.

  • 코드 라인 18에서 요청 영역을 pmd 섹션 단위만큼  데이터 캐시를 flush한다. 단 armv7 및 armv8 아키텍처와 같이 데이터 캐시가 PIPT 또는 VIPT non-aliasing을 사용하는 경우에는 flush할 필요없다
  • 코드 라인 19~21에서 커널 페이지 테이블을 통해 요청 가상 주소에 해당하는 pgd, pud, pmd 엔트리 주소를 알아온다.
  • 코드 라인 25~33에서 이미 pmd 매핑된 경우 매핑을 클리어한다.
  • 코드 라인 34에서 커널에 메모리 context에서 vmalloc 시퀀스 값을 증가시켜 커널의 vmalloc 등록 정보가 갱신되었음을 나타내게 한다.
  • 코드 라인 39~40에서 기존 pmd 엔트리가 테이블을 가리킨 경우 그 테이블을 할당 해제 시킨다.
  • 코드 라인 43~45에서 끝 주소까지 pmd 단위를 증가시키며 루프를 돈다.
  • 코드 라인 51~52에서 커널의 vmalloc 정보와 현재 태스크의 vmalloc 정보를 비교하여 갱신된 경우 커널의 pgd 매핑된 vmalloc 엔트리들을 현재 태스크 메모리 디스크립터의 pgd 테이블에 복사한다.
  • 코드 라인 54에서 해당 커널 영역에 대한 TLB cache를 flush 한다

 

ioremap_page_range()

lib/ioremap.c

int ioremap_page_range(unsigned long addr, 
                       unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
{
        pgd_t *pgd;
        unsigned long start;
        unsigned long next;
        int err;

        BUG_ON(addr >= end);

        start = addr;
        phys_addr -= addr;
        pgd = pgd_offset_k(addr);
        do {
                next = pgd_addr_end(addr, end);
                err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);
                if (err)
                        break;
        } while (pgd++, addr = next, addr != end);

        flush_cache_vmap(start, end);

        return err;
}
EXPORT_SYMBOL_GPL(ioremap_page_range);

주어진 가상 주소 영역에 물리 주소를 prot 속성으로 매핑한다.

  • 코드 라인 11~12에서 요청 가상 주소를 start에 잠시 보관하고, 물리 주소에서 요청 가상 주소를 뺀다.
    • 예) addr=0x1000_0000, phys_addr=0x1234_5000
      • phys_addr=0x0234_5000
  • 코드 라인 13에서 가상 주소 addr에 해당하는 커널 pgd 엔트리 주소를 알아온다.
  • 코드 라인 15에서 다음 pgd 엔트리가 관리하는 가상 주소를 가져오되 마지막인 경우 end 값을 가져온다.
  • 코드 라인 16~18에서 pgd 엔트리 하나 범위내 addr ~ next 가상 주소 범위의 pud 엔트리들을 매핑하고 에러인 경우 루프를 탈출한다.
  • 코드 라인 19에서 다음 pgd 엔트리 및 다음 가상 주소를 선택하고  end까지 루프를 돌며 반복한다.
  • 코드 라인 21에서 매핑된 가상 주소 영역을 flush한다. 단 armv7 및 armv8 아키텍처와 같이 데이터 캐시가 PIPT 또는 VIPT non-aliasing을 사용하는 경우에는 flush할 필요없다.

 

ioremap_pud_range()

lib/ioremap.c

static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr,
                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
{
        pud_t *pud;
        unsigned long next;

        phys_addr -= addr;
        pud = pud_alloc(&init_mm, pgd, addr);
        if (!pud)
                return -ENOMEM;
        do {
                next = pud_addr_end(addr, end);
                if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot))
                        return -ENOMEM;
        } while (pud++, addr = next, addr != end);
        return 0;          
}

pud 단위로 주어진 가상 주소 영역에 물리 주소를 prot 속성으로 매핑한다.

  • 코드 라인 7에서 물리 주소에서 요청 가상 주소를 미리 뺀다.
    • 예) addr=0x1000_0000, phys_addr=0x1_1234_5000
      • phys_addr=0x1_0234_5000
  • 코드 라인 8~10에서 pud용 테이블을 하나 할당 받아온다. 만일 실패하는 경우 -ENOMEM 에러로 함수를 빠져나간다.
  • 코드 라인 12에서 다음 pud 엔트리가 관리하는 가상 주소를 가져오되 마지막인 경우 end 값을 가져온다.
  • 코드 라인 13~14에서 pud 엔트리 하나 범위내 addr ~ next 가상 주소 범위의 pmd 엔트리들을 매핑한다. 실패하는 경우 -ENOMEM 결과를 가지고 함수를 빠져나간다.
  • 코드 섹션 15에서 다음 pud 엔트리 및 다음 가상 주소를 선택하고  end까지 루프를 돌며 반복한다.

 

ioremap_pmd_range()

lib/ioremap.c

static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
{
        pmd_t *pmd;
        unsigned long next;

        phys_addr -= addr;
        pmd = pmd_alloc(&init_mm, pud, addr);
        if (!pmd)
                return -ENOMEM;
        do {
                next = pmd_addr_end(addr, end);
                if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot))
                        return -ENOMEM;
        } while (pmd++, addr = next, addr != end); 
        return 0;              
}

pmd 단위로 주어진 가상 주소 영역에 물리 주소를 prot 속성으로 매핑한다.

  • 코드 라인 7에서 물리 주소에서 요청 가상 주소를 미리 뺀다.
  • 코드 라인 8~10에서 pmd용 테이블을 하나 할당 받아온다. 만일 실패하는 경우 -ENOMEM 에러로 함수를 빠져나간다.
  • 코드 라인 12에서 다음 pmd 엔트리가 관리하는 가상 주소를 가져오되 마지막인 경우 end 값을 가져온다.
  • 코드 라인 13~14에서 pmd 엔트리 하나 범위내 addr ~ next 가상 주소 범위의 pte 엔트리들을 매핑한다. 실패하는 경우 -ENOMEM 결과를 가지고 함수를 빠져나간다.
  • 코드 섹션 15에서 다음 pmd 엔트리 및 다음 가상 주소를 선택하고  end까지 루프를 돌며 반복한다.

 

ioremap_pte_range()

lib/ioremap.c

static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
{
        pte_t *pte;
        u64 pfn;

        pfn = phys_addr >> PAGE_SHIFT;
        pte = pte_alloc_kernel(pmd, addr);
        if (!pte)
                return -ENOMEM;
        do {
                BUG_ON(!pte_none(*pte));
                set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
                pfn++;
        } while (pte++, addr += PAGE_SIZE, addr != end);
        return 0;
}

pte 단위로 주어진 가상 주소 영역에 물리 주소 페이지를 prot 속성으로 매핑한다.

  • 코드 라인 7에서 pfn을 알아온다.
  • 코드 라인 8~10에서 pte용 테이블을 하나 할당 받아온다. 만일 실패하는 경우 -ENOMEM 에러로 함수를 빠져나간다.
  • 코드 라인 13~14에서 가상 주소 addr에 해당하는 pte 엔트리를 pfn 물리페이지에 prot 타입으로 매핑시키고 pfn을 증가시킨다.
  • 코드 라인 15에서 다음 pte 엔트리를 증가시키고, 가상 주소도 다음 페이지만큼 증가시키며 end까지 루프를 돌며 반복한다.

 

io 언매핑

iounmap()

arch/arm/include/asm/io.h

#define iounmap                         __arm_iounmap

요청 물리 주소의 io 매핑을 해제한다.

 

__arm_iounmap()

arch/arm/mm/ioremap.c

void __arm_iounmap(volatile void __iomem *io_addr)
{
        arch_iounmap(io_addr);
}
EXPORT_SYMBOL(__arm_iounmap);

요청 물리 주소의 io 매핑을 해제한다.

 

arch_iounmap()

arch/arm/mm/ioremap.c

void (*arch_iounmap)(volatile void __iomem *) = __iounmap;

컴파일 타임에 전역 arch_iounmap 함수 포인터 변수에는 __iounmap() 함수의 주소가 담긴다.

 

__iounmap()

arch/arm/mm/ioremap.c

void __iounmap(volatile void __iomem *io_addr)
{       
        void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr);
        struct static_vm *svm;

        /* If this is a static mapping, we must leave it alone */
        svm = find_static_vm_vaddr(addr);
        if (svm)
                return;

#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
        {
                struct vm_struct *vm;
                       
                vm = find_vm_area(addr);
        
                /*
                 * If this is a section based mapping we need to handle it
                 * specially as the VM subsystem does not know how to handle
                 * such a beast.
                 */
                if (vm && (vm->flags & VM_ARM_SECTION_MAPPING))
                        unmap_area_sections((unsigned long)vm->addr, vm->size);
        }
#endif          

        vunmap(addr);
}

요청 물리 주소의 io 매핑을 해제한다.

  • 코드 라인 7~9에서 vmalloc 공간에 요청 주소에 대한 static 매핑이 있는 경우 그냥 함수를 빠져나간다.
  • 코드 라인 11~25에서 시스템(빌드된 커널)이 SMP 및 LPAE를 지원하지 않는 경우  해당 주소로 섹션 매핑이 된 경우 해제한다.
  • 코드 라인 27에서 vmalloc 공간에 매핑된 io 물리 주소를 vunmap() 함수를 호출하여 매핑을 해제한다.

 

vmalloc 공간의 매핑 상태 확인

다음과 같이 어떤 api를 통해 물리주소가 vmalloc 가상 주소 공간에 매핑되었는지 확인할 수 있다.

$ sudo cat /proc/vmallocinfo
0xba800000-0xbb000000 8388608 iotable_init+0x0/0xb8 phys=3a800000 ioremap
0xbb804000-0xbb806000    8192 raw_init+0x50/0x148 pages=1 vmalloc
0xbb806000-0xbb809000   12288 pcpu_mem_zalloc+0x44/0x80 pages=2 vmalloc
0xbb899000-0xbb89c000   12288 pcpu_mem_zalloc+0x44/0x80 pages=2 vmalloc
0xbb89c000-0xbb89e000    8192 dwc_otg_driver_probe+0x650/0x7a8 phys=3f006000 ioremap
0xbb89e000-0xbb8a0000    8192 devm_ioremap_nocache+0x40/0x7c phys=3f300000 ioremap
0xbbc00000-0xbbc83000  536576 bcm2708_fb_set_par+0x108/0x13c phys=3db79000 ioremap
0xbc9f7000-0xbc9ff000   32768 SyS_swapon+0x618/0xf6c pages=7 vmalloc
0xbc9ff000-0xbca01000    8192 SyS_swapon+0x850/0xf6c pages=1 vmalloc
0xbcc39000-0xbcc3b000    8192 SyS_swapon+0xaa0/0xf6c pages=1 vmalloc
0xbce7b000-0xbce7f000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbce83000-0xbce87000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbce8b000-0xbce8f000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbce93000-0xbce97000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbce9b000-0xbce9f000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbcea7000-0xbceab000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbceab000-0xbceaf000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbceb7000-0xbceb9000    8192 bpf_prog_alloc+0x44/0xb0 pages=1 vmalloc
0xbf509000-0xbf50d000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xbf50d000-0xbf511000   16384 n_tty_open+0x20/0xe4 pages=3 vmalloc
0xf3000000-0xf3001000    4096 iotable_init+0x0/0xb8 phys=3f000000 ioremap
0xf3003000-0xf3004000    4096 iotable_init+0x0/0xb8 phys=3f003000 ioremap
0xf3007000-0xf3008000    4096 iotable_init+0x0/0xb8 phys=3f007000 ioremap
0xf300b000-0xf300c000    4096 iotable_init+0x0/0xb8 phys=3f00b000 ioremap
0xf3100000-0xf3101000    4096 iotable_init+0x0/0xb8 phys=3f100000 ioremap
0xf3200000-0xf3201000    4096 iotable_init+0x0/0xb8 phys=3f200000 ioremap
0xf3201000-0xf3202000    4096 iotable_init+0x0/0xb8 phys=3f201000 ioremap
0xf3215000-0xf3216000    4096 iotable_init+0x0/0xb8 phys=3f215000 ioremap
0xf3980000-0xf39a0000  131072 iotable_init+0x0/0xb8 phys=3f980000 ioremap
0xf4000000-0xf4001000    4096 iotable_init+0x0/0xb8 phys=40000000 ioremap
0xfea50000-0xff000000 5963776 pcpu_get_vm_areas+0x0/0x5e0 vmalloc

 

참고

답글 남기기

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