2014년 10월 18일 토요일

[Linux Kernel] 74주차(2014.10.18)

ARM10C 74주차 후기

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

도움될 만한 정보 공유

진도 :

  • init_IRQ();
    • exynos-combiner.c 에 정의된 함수를 사용하여 초기화 수행
    • __irqchip_begin: irqchip_of_match_exynos4210_combiner

main.c::start_kernel()

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();

지난시간에 이어 init_IRQ() 분석 진행 중

irq-gic.c::gic_of_init()

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;

    if (WARN_ON(!node))
        return -ENODEV;

    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;
}

address.c::of_iomap()

  • dist_base = of_iomap(node, 0);
void __iomem *of_iomap(struct device_node *np, int index)
{
    struct resource res;

    if (of_address_to_resource(np, index, &res))
        return NULL;

    return ioremap(res.start, resource_size(&res));
}
EXPORT_SYMBOL(of_iomap);

resource

...

address.c::of_address_to_resource()

  • if (of_address_to_resource(np, index, &res))
int of_address_to_resource(struct device_node *dev, int index,
               struct resource *r)
{
    const __be32    *addrp;
    u64     size;
    unsigned int    flags;
    const char  *name = NULL;

    addrp = of_get_address(dev, index, &size, &flags);
    if (addrp == NULL)
        return -EINVAL;

    /* Get optional "reg-names" property to add a name to a resource */
    of_property_read_string_index(dev, "reg-names", index, &name);

    return __of_address_to_resource(dev, addrp, size, flags, name, r);
}
EXPORT_SYMBOL_GPL(of_address_to_resource);

address.c::of_get_address()

  • addrp = of_get_address(dev, index, &size, &flags);
  • of.h::device_node
struct device_node {
    const char *name;
    const char *type;
    phandle phandle;
    const char *full_name;

    struct  property *properties;
    struct  property *deadprops;    /* removed properties */
    struct  device_node *parent;
    struct  device_node *child;
    struct  device_node *sibling;
    struct  device_node *next;  /* next device of same type */
    struct  device_node *allnext;   /* next in list of all nodes */
    struct  proc_dir_entry *pde;    /* this node's proc directory */
    struct  kref kref;
    unsigned long _flags;
    void    *data;
#if defined(CONFIG_SPARC) // CONFIG_SPARC=n
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};
  • address.c::of_match_bus()
  • address.c::of_bus of_busses[] = {
    /* ISA */
    {
        .name = "isa",
        .addresses = "reg",
        .match = of_bus_isa_match,
        .count_cells = of_bus_isa_count_cells,
        .map = of_bus_isa_map,
        .translate = of_bus_isa_translate,
        .get_flags = of_bus_isa_get_flags,
    },
    /* Default */
    {
        .name = "default",
        .addresses = "reg",
        .match = NULL,
        .count_cells = of_bus_default_count_cells,
        .map = of_bus_default_map,
        .translate = of_bus_default_translate,
        .get_flags = of_bus_default_get_flags,
    },
  • address.c::of_bus_isa_match()
static int of_bus_isa_match(struct device_node *np)
{
    return !strcmp(np->name, "isa");
}
static struct of_bus *of_match_bus(struct device_node *np)
{
    int i;

    for (i = 0; i < ARRAY_SIZE(of_busses); i++)
        if (!of_busses[i].match || of_busses[i].match(np))
            // i: 1
            return &of_busses[i];
            // return &of_busses[1]
    BUG();
    return NULL;
}
  • 여기서 i: 1이 되어 ISA가 아닌 Default가 반환된다.
  • address.c::of_get_address()에서 
  • bus = of_match_bus(parent);
  • // bus: &of_busses[1]
  • address.c::of_bus_default_count_cells()
static void of_bus_default_count_cells(struct device_node *dev,
                       int *addrc, int *sizec)
{
    if (addrc)
        *addrc = of_n_addr_cells(dev);
    if (sizec)
        *sizec = of_n_size_cells(dev);
}
  • base.c::of_n_addr_cells()
int of_n_addr_cells(struct device_node *np)
{
    const __be32 *ip;

    do {
        if (np->parent)
            np = np->parent;
        ip = of_get_property(np, "#address-cells", NULL);
        if (ip)
            return be32_to_cpup(ip);
    } while (np->parent);
    /* No #address-cells property for the root node */
    return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
}
EXPORT_SYMBOL(of_n_addr_cells);
  • base.c::of_n_size_cells()
int of_n_size_cells(struct device_node *np)
{
    const __be32 *ip;

    do {
        if (np->parent)
            np = np->parent;
        ip = of_get_property(np, "#size-cells", NULL);
        if (ip)
            return be32_to_cpup(ip);
    } while (np->parent);
    /* No #size-cells property for the root node */
    return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
}
EXPORT_SYMBOL(of_n_size_cells);
  • root'/'는 address-cells: 1, size-cells: 1이다. 
  • 32비트 이기 때문에 address-cells은 1이 면 되고, size-cells은 memory mapped io라면 사이즈의 크기를 말한다.
  • 64비트라면 address-cells는 2로 표시되어야 하고 size-cells또한 2로 표현되어야 한다.
  • of.h::of_read_number()
static inline u64 of_read_number(const __be32 *cell, int size)
{
    u64 r = 0;
    while (size--)
        r = (r << 32) | be32_to_cpu(*(cell++));
    return r;
}
  • size 값 (0x1000)을 반환한다.
  • ioport.h::
#define IORESOURCE_MEM      0x00000200
  • address.h::of_bus_default_get_flags()
static unsigned int of_bus_default_get_flags(const __be32 *addr)
{
    return IORESOURCE_MEM;
}
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
            unsigned int *flags)
{
    const __be32 *prop;
    unsigned int psize;
    struct device_node *parent;
    struct of_bus *bus;
    int onesize, i, na, ns;

    /* Get parent & match bus type */
    parent = of_get_parent(dev);
    if (parent == NULL)
        return NULL;
    bus = of_match_bus(parent);
    bus->count_cells(dev, &na, &ns);
    of_node_put(parent);
    if (!OF_CHECK_ADDR_COUNT(na))
        return NULL;

    /* Get "reg" or "assigned-addresses" property */
    prop = of_get_property(dev, bus->addresses, &psize);
    if (prop == NULL)
        return NULL;
    psize /= 4;

    onesize = na + ns;
    for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
        if (i == index) {
            if (size)
                *size = of_read_number(prop + na, ns);
            if (flags)
                *flags = bus->get_flags(prop);
            return prop;
        }
    return NULL;
}
EXPORT_SYMBOL(of_get_address);
  • return gic node의 reg property 값은 시작 주소.

address.c::of_address_to_resource()

int of_address_to_resource(struct device_node *dev, int index,
               struct resource *r)
{
    const __be32    *addrp;
    u64     size;
    unsigned int    flags;
    const char  *name = NULL;

    addrp = of_get_address(dev, index, &size, &flags);
    // addrp: gic node 값의 시작 주소. 
    if (addrp == NULL)
        return -EINVAL;

    /* Get optional "reg-names" property to add a name to a resource */
    of_property_read_string_index(dev, "reg-names", index, &name);

    return __of_address_to_resource(dev, addrp, size, flags, name, r);
}
EXPORT_SYMBOL_GPL(of_address_to_resource);

base.c::of_property_read_string_index()

  • of_property_read_string_index(dev, "reg-names", index, &name);
  • propname: reg-name
int of_property_read_string_index(struct device_node *np, const char *propname,
                  int index, const char **output)
{
    struct property *prop = of_find_property(np, propname, NULL);
    int i = 0;
    size_t l = 0, total = 0;
    const char *p;

    if (!prop)
        return -EINVAL;
    if (!prop->value)
        return -ENODATA;
    if (strnlen(prop->value, prop->length) >= prop->length)
        return -EILSEQ;

    p = prop->value;

    for (i = 0; total < prop->length; total += l, p += l) {
        l = strlen(p) + 1;
        if (i++ == index) {
            *output = p;
            return 0;
        }
    }
    return -ENODATA;
}
EXPORT_SYMBOL_GPL(of_property_read_string_index);
  • return -EINVAL;

address.c::__of_address_to_resource()

  • return __of_address_to_resource(dev, addrp, size, flags, name, r);
  • dev:
  • addrp:
  • size:
  • flags:
  • name:
  • r:
static int __of_address_to_resource(struct device_node *dev,
        const __be32 *addrp, u64 size, unsigned int flags,
        const char *name, struct resource *r)
{
    u64 taddr;

    if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
        return -EINVAL;
    taddr = of_translate_address(dev, addrp);
    if (taddr == OF_BAD_ADDR)
        return -EINVAL;
    memset(r, 0, sizeof(struct resource));
    if (flags & IORESOURCE_IO) {
        unsigned long port;
        port = pci_address_to_pio(taddr);
        if (port == (unsigned long)-1)
            return -EINVAL;
        r->start = port;
        r->end = port + size - 1;
    } else {
        r->start = taddr;
        r->end = taddr + size - 1;
    }
    r->flags = flags;
    r->name = name ? name : dev->full_name;

    return 0;
}

address.c::of_translate_address()

  • taddr = of_translate_address(dev, addrp);
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
{
    return __of_translate_address(dev, in_addr, "ranges");
}
EXPORT_SYMBOL(of_translate_address);

address.c::__of_translate_address()

static u64 __of_translate_address(struct device_node *dev,
                  const __be32 *in_addr, const char *rprop)
{
    struct device_node *parent = NULL;
    struct of_bus *bus, *pbus;
    __be32 addr[OF_MAX_ADDR_CELLS];
    int na, ns, pna, pns;
    u64 result = OF_BAD_ADDR;

    pr_debug("OF: ** translation for device %s **\n", of_node_full_name(dev));

    /* Increase refcount at current level */
    of_node_get(dev);

    /* Get parent & match bus type */
    parent = of_get_parent(dev);
    if (parent == NULL)
        goto bail;
    bus = of_match_bus(parent);

    /* Count address cells & copy address locally */
    bus->count_cells(dev, &na, &ns);
    if (!OF_CHECK_COUNTS(na, ns)) {
        printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
               of_node_full_name(dev));
        goto bail;
    }
    memcpy(addr, in_addr, na * 4);

    pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
        bus->name, na, ns, of_node_full_name(parent));
    of_dump_addr("OF: translating address:", addr, na);

    /* Translate */
    for (;;) {
        /* Switch to parent bus */
        of_node_put(dev);
        dev = parent;
        parent = of_get_parent(dev);

        /* If root, we have finished */
        if (parent == NULL) {
            pr_debug("OF: reached root node\n");
            result = of_read_number(addr, na);
            break;
        }

        /* Get new parent bus and counts */
        pbus = of_match_bus(parent);
        pbus->count_cells(dev, &pna, &pns);
        if (!OF_CHECK_COUNTS(pna, pns)) {
            printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
                   of_node_full_name(dev));
            break;
        }

        pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
            pbus->name, pna, pns, of_node_full_name(parent));

        /* Apply bus translation */
        if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
            break;

        /* Complete the move up one level */
        na = pna;
        ns = pns;
        bus = pbus;

        of_dump_addr("OF: one level translation:", addr, na);
    }
 bail:
    of_node_put(parent);
    of_node_put(dev);

    return result;
}
  • pr_debug("OF: ** translation for device %s **\n", of_node_full_name(dev));
  • of.h::of_node_full_name()
  • fullname: @를 포함한 string: /interrupt-controller@10481000
  • name: @뒤를 뺀 node string: interrupt-controller
static inline const char *of_node_full_name(const struct device_node *np)
{
    return np ? np->full_name : "<no-node>";
}
  • of.h::of_node_get(dev);
static inline struct device_node *of_node_get(struct device_node *node)
{
    return node;
}
  • parent = of_get_parent(dev);
    • parent: root node의 주소
  • bus = of_match_bus(parent);
    • bus: &of_busses[1]
  • bus->count_cells(dev, &na, &ns);
    • &of_busses[1]
    • of_bus_default_count_cells()
    • na: 1, ns:1
  • if (!OF_CHECK_COUNTS(na, ns)) {
  • address::OF_CHECK_COUNT()
#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0)
  • pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", bus->name, na, ns, of_node_full_name(parent));
    • bus->name: "default"
    • na: 1, ns:1
    • of_node_fullname: "/"
  • of_dump_addr("OF: translating address:", addr, na);
  • address.c::of_dump_addr()
static void of_dump_addr(const char *s, const __be32 *addr, int na) { }
  • return 0x10481000

ioremap.c::__arm_ioremap_caller()

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);
}

ioremap.c::__arm_ioremap_pfn_caller()

  • type = get_mem_type(mtype);
  • mtype: DT_DEVICE: 0
const struct mem_type *get_mem_type(unsigned int type)
{
    return type < ARRAY_SIZE(mem_types) ? &mem_types[type] : NULL;
}
EXPORT_SYMBOL(get_mem_type);
  • size = PAGE_ALIGN(offset + size);
    • size: 0x1000
  • if (size && !(sizeof(phys_addr_t) == 4 && pfn >= 0x100000)) {
    • (size && !(sizeof(phys_addr_t) == 4 && pfn >= 0x100000)): 1
    • svm = find_static_vm_paddr(paddr, size, mtype);
  • ioremap.c::find_static_vm_paddr()
    • list_for_each_entry(svm, &static_vmlist, list)
    • #define VM_ARM_STATIC_MAPPING 0x40000000
    • #define VM_ARM_MTYPE_MASK (0x1f << 20)
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;
}
  • code
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);
}

git log

From github.com:arm10c/linux-stable
   44dc97e..9f21bbb  master     -> origin/master
   Merge made by the 'recursive' strategy.
arch/arm/include/asm/io.h             |   5 +
arch/arm/include/asm/memory.h         |   2 +
arch/arm/include/asm/pgtable-2level.h |   8 ++
arch/arm/mm/ioremap.c                 |  54 ++++++++-
arch/arm/mm/mm.h                      |   5 +
arch/arm/mm/mmu.c                     |   5 +
drivers/irqchip/irq-gic.c             |   5 +
drivers/of/address.c                  | 212 +++++++++++++++++++++++++++++++++-
drivers/of/base.c                     |  73 +++++++++---
drivers/of/irq.c                      |  82 +++++++------
include/asm-generic/io.h              |   2 +-
include/linux/ioport.h                |   6 +
include/linux/list.h                  |   5 +
include/linux/of.h                    |  12 ++
include/linux/of_address.h            |   2 +-
15 files changed, 420 insertions(+), 58 deletions(-)

댓글 없음:

댓글 쓰기