2014년 11월 1일 토요일

[Linux Kernel] 76주차(2014.11.01)

ARM10C 76주차 후기

일시 : 2014.11.01 (76주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 3명

진도

  • init_IRQ();
    • gic_of_init()->of_iomap()

지난시간에 이어 init_IRQ() 을 계속 분석 진행합니다. 

main.c::start_kernel()

  • start_kernel()->init_IRQ()
asmlinkage void __init start_kernel(void)
{

...

    boot_cpu_init();
    // 현재 cpu(core id)를 얻어서 cpu_XXX_bits[] 의 cpu를 셋한다.

...

    setup_arch(&command_line);

...

    mm_init();
    // buddy와 slab 을 활성화 하고 기존 할당 받은 bootmem 은 buddy,
    // pcpu 메모리, vmlist 는 slab으로 이관

    sched_init();
    // scheduler가 사용하는 자료 구조 초기화, idle_threads를 init_task로 세팅

    preempt_disable();
    // preempt count를 증가시켜 preemption 못하도록 막음

...

    rcu_init();
    // rcu 자료구조 bh, sched, preempt 를 각각 초기화 수행함

...

    radix_tree_init();
    // radix tree로 사용하는 radix_tree_node_cachep에 kmem_cache#20을 생성 및 초기화 후 할당하고
    // height_to_maxindex을 초기화 수행

    /* init some links before init_ISA_irqs() */
    early_irq_init();
    // irq_desc 0 ~ 15 까지의 object을 할당 받고 초기화를 수행
    // allocated_irqs에 bit를 1로 세팅하고 radix tree에 각 irq_desc를 노트로 추가

    init_IRQ();

irq.c::init_IRQ()

  • start_kernel()->init_IRQ()->irqchip_init()
// ARM10C 20141004
void __init init_IRQ(void)
{
    // CONFIG_OF=y, machine_desc->init_irq: __mach_desc_EXYNOS5_DT.init_irq: 0
    if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
        irqchip_init();
    else
        machine_desc->init_irq();
}

irqchip.h::irqchip_init()

  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()
// ARM10C 20141004
void __init irqchip_init(void)
{
    // exynos-combiner.c 에 정의된 함수를 사용하여 초기화 수행
    // __irqchip_begin: irqchip_of_match_exynos4210_combiner
    of_irq_init(__irqchip_begin);
}

irqchip.h::irqchip_init()

  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()
void __init of_irq_init(const struct of_device_id *matches)
{
    struct device_node *np, *parent = NULL;
    // parent: NULL
    struct intc_desc *desc, *temp_desc;
    struct list_head intc_desc_list, intc_parent_list;

    INIT_LIST_HEAD(&intc_desc_list);
    // intc_desc_list 리스트 초기화 수행

    INIT_LIST_HEAD(&intc_parent_list);
    // intc_parent_list 리스트 초기화 수행

    // matches: irqchip_of_match_exynos4210_combiner
    for_each_matching_node(np, matches) {
    // for (np = of_find_matching_node(NULL, matches); np; np = of_find_matching_node(np, matches))

        // np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // of_find_property(devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소, "interrupt-controller", NULL):
        // combiner node의 "interrupt-controller" property의 주소
        // np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        // of_find_property(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, "interrupt-controller", NULL):
        // gic node의 "interrupt-controller" property의 주소
        if (!of_find_property(np, "interrupt-controller", NULL))
            continue;
        /*
         * Here, we allocate and populate an intc_desc with the node
         * pointer, interrupt-parent device_node etc.
         */
        // sizeof(struct intc_desc): 16 bytes, GFP_KERNEL: 0xD0
        // kzalloc(16, GFP_KERNEL: 0xD0): kmem_cache#30-o10
        // sizeof(struct intc_desc): 16 bytes, GFP_KERNEL: 0xD0
        // kzalloc(16, GFP_KERNEL: 0xD0): kmem_cache#30-o11
        desc = kzalloc(sizeof(*desc), GFP_KERNEL);
        // desc: kmem_cache#30-o10
        // desc: kmem_cache#30-o11

        // desc: kmem_cache#30-o10
        // desc: kmem_cache#30-o11
        if (WARN_ON(!desc))
            goto err;

        // desc->dev: (kmem_cache#30-o10)->dev, np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // desc->dev: (kmem_cache#30-o11)->dev, np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        desc->dev = np;
        // desc->dev: (kmem_cache#30-o10)->dev: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소

// 2014/10/04 종료
// 2014/10/11 시작

        // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent, np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // of_irq_find_parent(devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소): gic node 주소
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent, np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        // of_irq_find_parent(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소): gic node 주소
        desc->interrupt_parent = of_irq_find_parent(np);
        // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: gic node 주소
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: gic node 주소

        // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: gic node 주소
        // np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: gic node 주소
        // np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        if (desc->interrupt_parent == np)
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: gic node 주소
            desc->interrupt_parent = NULL;
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL

        // &desc->list: &(kmem_cache#30-o10)->list
        // &desc->list: &(kmem_cache#30-o11)->list
        list_add_tail(&desc->list, &intc_desc_list);
        // intc_desc_list에 (kmem_cache#30-o10)->list를 tail에 추가
        // intc_desc_list에 (kmem_cache#30-o11)->list를 tail에 추가
    }

    // irqchip_of_match_exynos4210_combiner, irqchip_of_match_cortex_a15_gic 의
    // struct intc_desc 메모리 할당, intc_desc 맴버가 초기화 된 값이 intc_desc_list list의 tail로 추가됨

    /*
     * The root irq controller is the one without an interrupt-parent.
     * That one goes first, followed by the controllers that reference it,
     * followed by the ones that reference the 2nd level controllers, etc.
     */
    // list_empty(&intc_desc_list): 0
    while (!list_empty(&intc_desc_list)) {
        /*
         * Process all controllers with the current 'parent'.
         * First pass will be looking for NULL as the parent.
         * The assumption is that NULL parent means a root controller.
         */
        list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
        // for (desc = list_first_entry(&intc_desc_list, typeof(*desc), list),
        //  temp_desc = list_next_entry(desc, list);
        //      &desc->list != (&intc_desc_list);
        //      desc = temp_desc, temp_desc = list_next_entry(temp_desc, list))

            // desc: kmem_cache#30-o10 (exynos4210_combiner), temp_desc: kmem_cache#30-o11 (cortex_a15_gic)
            // desc: kmem_cache#30-o11 (cortex_a15_gic), temp_desc: NULL

            const struct of_device_id *match;
            int ret;
            of_irq_init_cb_t irq_init_cb;

            // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: gic node 주소, parent: NULL
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL, parent: NULL
            if (desc->interrupt_parent != parent)
                continue;
                // continue 수행 (exynos4210_combiner)

            // &desc->list: (kmem_cache#30-o11)->list
            list_del(&desc->list);
            // intc_desc_list에서 (kmem_cache#30-o11)->list를 삭제

            // matches: irqchip_of_match_cortex_a15_gic,
            // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
            // of_match_node(cortex_a15_gic, devtree에서 allnext로 순회 하면서 찾은 gic node의 주소):
            // irqchip_of_match_cortex_a15_gic
            match = of_match_node(matches, desc->dev);
            // match: irqchip_of_match_cortex_a15_gic

            // match->data; irqchip_of_match_cortex_a15_gic.data: gic_of_init
            if (WARN(!match->data,
                "of_irq_init: no init function for %s\n",
                match->compatible)) {
                kfree(desc);
                continue;
            }

            // match->compatible: irqchip_of_match_cortex_a15_gic.compatible: "arm,cortex-a15-gic",
            // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
            pr_debug("of_irq_init: init %s @ %p, parent %p\n",
                 match->compatible,
                 desc->dev, desc->interrupt_parent);
            // "of_irq_init: init arm,cortex-a15-gic @ 0x(gic node의 주소), parent 0\n"

// 2014/10/11 종료
// 2014/10/18 시작

            // match->data; irqchip_of_match_cortex_a15_gic.data: gic_of_init
            irq_init_cb = (of_irq_init_cb_t)match->data;
            // irq_init_cb: gic_of_init

            // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소,
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
            // gic_of_init(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, NULL):
            ret = irq_init_cb(desc->dev, desc->interrupt_parent);
            if (ret) {
                kfree(desc);
                continue;
            }

            /*
             * This one is now set up; add it to the parent list so
             * its children can get processed in a subsequent pass.
             */
            list_add_tail(&desc->list, &intc_parent_list);
        }

        /* Get the next pending parent that might have children */
        desc = list_first_entry_or_null(&intc_parent_list,
                        typeof(*desc), list);
        if (!desc) {
            pr_err("of_irq_init: children remain, but no parents\n");
            break;
        }
        list_del(&desc->list);
        parent = desc->dev;
        kfree(desc);
    }

    list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
        list_del(&desc->list);
        kfree(desc);
    }
err:
    list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
        list_del(&desc->list);
        kfree(desc);
    }
}
  • ret = irq_init_cb(desc->dev, desc->interrupt_parent);
  • irq_init_cb = (of_irq_init_cb_t)match->data;
    • 여기서 irq_init_cb: gic_of_init 로 되어 gic_of_init()를 실행한다.

irq_gic.c::gic_of_init()

  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()
  • of_irq_init()->gic_of_init()
// ARM10C 20141018
// desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소,
// desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
    void __iomem *cpu_base;
    void __iomem *dist_base;
    u32 percpu_offset;
    int irq;

    // node: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
    if (WARN_ON(!node))
        return -ENODEV;

    // node: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
    dist_base = of_iomap(node, 0);
    WARN(!dist_base, "unable to map gic dist registers\n");

    cpu_base = of_iomap(node, 1);
    WARN(!cpu_base, "unable to map gic cpu registers\n");

    if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
        percpu_offset = 0;

    gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
    if (!gic_cnt)
        gic_init_physaddr(node);

    if (parent) {
        irq = irq_of_parse_and_map(node, 0);
        gic_cascade_irq(gic_cnt, irq);
    }
    gic_cnt++;
    return 0;
}
  • dist_base = of_iomap(node, 0);
  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()
    • gic_of_init()->of_iomap()

address.c::of_iomap()

  • dist_base = of_iomap(node, 0);
  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()
    • node: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
// ARM10C 20141018
// node: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, 0
void __iomem *of_iomap(struct device_node *np, int index)
{
    struct resource res;

    // np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, index: 0
    // of_address_to_resource(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, 0, &res): 0
    if (of_address_to_resource(np, index, &res))
        return NULL;

    // of_address_to_resource에서 한일:
    // (&res)->start: 0x10481000
    // (&res)->end: 0x10481fff
    // (&res)->flags: IORESOURCE_MEM: 0x00000200
    // (&res)->name: "/interrupt-controller@10481000"

    // res.start: 0x10481000, resource_size(&res): 0x1000
    return ioremap(res.start, resource_size(&res));
}
EXPORT_SYMBOL(of_iomap);
  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()->ioremap()
  • return ioremap(res.start, resource_size(&res));
    • res.start: 0x10481000, resource_size(&res): 0x1000

io.h::ioremap()

  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()->ioremap()
#define ioremap(cookie,size)        __arm_ioremap((cookie), (size), MT_DEVICE)
  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()->ioremap()
    • ioremap()->__arm_ioremap()

ioremap.c::__arm_ioremap()

  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()->ioremap()
    • ioremap()->__arm_ioremap()
// ARM10C 20141018
// res.start: 0x10481000, resource_size(&res): 0x1000, MT_DEVICE: 0
void __iomem *
__arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype)
{
    // phys_addr: 0x10481000, size: 0x1000, mtype: MT_DEVICE: 0
    return arch_ioremap_caller(phys_addr, size, mtype,
        __builtin_return_address(0));
}
EXPORT_SYMBOL(__arm_ioremap);
  • return arch_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0));
  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()->ioremap()
    • ioremap()->__arm_ioremap()->arm_ioremap_caller()

ioremap.c::__arm_ioremap_caller()

  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()->gic_of_init()->of_iomap()->ioremap()
    • ioremap()->__arm_ioremap()->arm_ioremap_caller()
// ARM10C 20141018
// phys_addr: 0x10481000, size: 0x1000, mtype: MT_DEVICE: 0, __builtin_return_address(0)
void __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size,
    unsigned int mtype, void *caller)
{
    phys_addr_t last_addr;
    // phys_addr: 0x10481000, PAGE_MASK: 0xFFFFF000
    unsigned long offset = phys_addr & ~PAGE_MASK;
    // offset: 0

    // phys_addr: 0x10481000, __phys_to_pfn(0x10481000): 0x10481
    unsigned long pfn = __phys_to_pfn(phys_addr);
    // pfn: 0x10481

    /*
     * Don't allow wraparound or zero size
     */
    // phys_addr: 0x10481000, size: 0x1000
    last_addr = phys_addr + size - 1;
    // last_addr: 0x10481fff

    // size: 0x1000, last_addr: 0x10481fff, phys_addr: 0x10481000
    if (!size || last_addr < phys_addr)
        return NULL;

    // pfn: 0x10481, offset: 0, size: 0x1000, mtype: MT_DEVICE: 0, caller: __builtin_return_address(0)
    return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,
            caller);
}
  • start_kernel()->init_IRQ()->gic_of_init()->of_iomap()->ioremap()->__arm_ioremap()->__arm_ioremap_caller()
    • __arm_ioremap_pfn_caller(pfn, offset, size, mtype, caller);

ioremap.c::__arm_ioremap_pfn_caller()

  • start_kernel()->init_IRQ()->gic_of_init()->of_iomap()->ioremap()->__arm_ioremap()->__arm_ioremap_caller()
    • __arm_ioremap_pfn_caller(pfn, offset, size, mtype, caller);
// ARM10C 20141018
// pfn: 0x10481, offset: 0, size: 0x1000, mtype: MT_DEVICE: 0, caller: __builtin_return_address(0)
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;
    // pfn: 0x10481, __pfn_to_phys(0x10481): 0x10481000
    phys_addr_t paddr = __pfn_to_phys(pfn);
    // paddr: 0x10481000

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

    // mtype: MT_DEVICE: 0
    // get_mem_type(MT_DEVICE: 0): &mem_types[0]
    type = get_mem_type(mtype);
    // type: &mem_types[0]

    // type: &mem_types[0]
    if (!type)
        return NULL;

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

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

        // paddr: 0x10481000 size: 0x1000, mtype: MT_DEVICE: 0
        // find_static_vm_paddr(0x10481000, 0x1000, MT_DEVICE: 0): NULL
        svm = find_static_vm_paddr(paddr, size, mtype);
        // svm: NULL
        if (svm) {
            addr = (unsigned long)svm->vm.addr;
            addr += paddr - svm->vm.phys_addr;
            return (void __iomem *) (offset + addr);
        }
    }

// 2014/10/18 종료
// 2014/10/25 시작

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

    // size: 0x1000, VM_IOREMAP: 0x00000001, caller: __builtin_return_address(0)
    // get_vm_area_caller(0x1000, 0x00000001, __builtin_return_address(0)): kmem_cache#30-oX (vm_struct)
    area = get_vm_area_caller(size, VM_IOREMAP, caller);
    // area: kmem_cache#30-oX (vm_struct)

    /*
    // get_vm_area_caller이 한일:
    // alloc area (GIC) 를 만들고 rb tree에 alloc area 를 추가
    // 가상주소 va_start 기준으로 GIC 를 RB Tree 추가한 결과
    //
    //                                  CHID-b
    //                               (0xF8000000)
    //                              /            \
    //                         TMR-r               PMU-r
    //                    (0xF6300000)             (0xF8180000)
    //                      /      \               /           \
    //                 SYSC-b      WDT-b         CMU-b         SRAM-b
    //            (0xF6100000)   (0xF6400000)  (0xF8100000)   (0xF8400000)
    //             /                                                 \
    //        GIC-r                                                   ROMC-r
    //   (0xF0000000)                                                 (0xF84C0000)
    //
    // (kmem_cache#30-oX (vm_struct))->flags: GFP_KERNEL: 0xD0
    // (kmem_cache#30-oX (vm_struct))->addr: 0xf0000000
    // (kmem_cache#30-oX (vm_struct))->size: 0x2000
    // (kmem_cache#30-oX (vm_struct))->caller: __builtin_return_address(0)
    //
    // (kmem_cache#30-oX (vmap_area GIC))->vm: kmem_cache#30-oX (vm_struct)
    // (kmem_cache#30-oX (vmap_area GIC))->flags: 0x04
    */

    // area: kmem_cache#30-oX (vm_struct)
    if (!area)
        return NULL;

    // area->addr: (kmem_cache#30-oX (vm_struct))->addr: 0xf0000000
    addr = (unsigned long)area->addr;
    // addr: 0xf0000000

    // area->phys_addr: (kmem_cache#30-oX (vm_struct))->phys_addr, paddr: 0x10481000
    area->phys_addr = paddr;
    // area->phys_addr: (kmem_cache#30-oX (vm_struct))->phys_addr: 0x10481000

#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE) // CONFIG_SMP=y, CONFIG_ARM_LPAE=n
    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
        // addr: 0xf0000000, size: 0x1000, paddr: 0x10481000,
        // type->prot_pte: (&mem_types[0])->prot_pte: PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED | L_PTE_SHARED (0x653)
        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);
}

댓글 없음:

댓글 쓰기