侧边栏切换

ioremap源码跟踪

最后编辑于: 2016-05-28 00:41  |  分类: linux  |  标签: 驱动   |  浏览数: 613  |  评论数: 0


又一篇讲老版本内核驱动的,多年没写过内核驱动了,也不知这些技术现在还适用不,权当纪念吧。

Original address:http://blog.chinaunix.net/uid-26009923-id-3291183.html

TQ2440的watchdog linux驱动在内核源码linux-2.6.30.4的:./drivers/watchdog/s3c2410_wdt.c

  1. watchdog ioremap的过程:
res->start=0x53000000   //物理地址
wdt_base=0xc5400000 //虚拟地址
wdt_base = ioremap(res->start, size);

将物理地址res->start映射到了虚拟地址0xc5400000处,映射大小为:size=0x100000.

注: 不知道为什么刚开始这个size=8, 后来就成了0x100000(1M)。

  1. arch/arm/include/asm/io.h中, 找到ioremap的定义:
    //出现两个ioremap
    #ifndef __arch_ioremap
    #define ioremap(cookie,size)        __arm_ioremap(cookie, size, MT_DEVICE)
    #define ioremap_nocache(cookie,size)    __arm_ioremap(cookie, size, MT_DEVICE)
    #define ioremap_cached(cookie,size)    __arm_ioremap(cookie, size, MT_DEVICE_CACHED)
    #define ioremap_wc(cookie,size)        __arm_ioremap(cookie, size, MT_DEVICE_WC)
    #define iounmap(cookie)            __iounmap(cookie)
    #else
    #define ioremap(cookie,size)        __arch_ioremap((cookie), (size), MT_DEVICE)
    #define ioremap_nocache(cookie,size)    __arch_ioremap((cookie), (size), MT_DEVICE)
    #define ioremap_cached(cookie,size)    __arch_ioremap((cookie), (size), MT_DEVICE_CACHED)
    #define ioremap_wc(cookie,size)        __arch_ioremap((cookie), (size), MT_DEVICE_WC)
    #define iounmap(cookie)            __arch_iounmap(cookie)
    #endif

    // 没有定义 #ifndef __arch_ioremap,所以是
    #define MT_DEVICE        0
    #define ioremap(cookie,size)        __arm_ioremap(cookie, size, MT_DEVICE)
  1. arch/arm/mm/ioremap.c
    /**
    __arm_ioremap(cookie, size, MT_DEVICE)也有两个定义,
    一个是在nommu.c中,
    一个是在arch/arm/mm/ioremap.c中,所以很明显进入arch/arm/mm/ioremap.c中:
    */

    //这个函数将物理地址phys_addr拆成两部分高20位的页帧号(Page Frame Number)pfn和低12位的页内偏移地址offset
    void __iomem *
    __arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
    {
        unsigned long 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;
         //此处打印值如下: last_addr=0x530fffff, size=0x100000, offset=0x0, pfn=0x53000
         return __arm_ioremap_pfn(pfn, offset, size, mtype);
    }
    EXPORT_SYMBOL(__arm_ioremap);

    /*
     * Convert a physical address to a Page Frame Number and back
     */
    #define PAGE_SHIFT        12
    #define    __phys_to_pfn(paddr)    ((paddr) >> PAGE_SHIFT)
  1. arch/arm/mm/ioremap.c
    /*
     * Remap an arbitrary physical address space into the kernel virtual
     * address space. Needed when the kernel wants to access high addresses
     * directly.
     *
     * We need to allow non-page-aligned mappings too: we will obviously
     * have to convert them into an offset in a page-aligned mapping, but the
     * caller shouldn't need to know that small detail.
     *
     * 'flags' are the extra L_PTE_ flags that you want to specify for this
     * mapping. See <asm/pgtable.h> for more information.
     */
    void __iomem *
    __arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size,
             unsigned int mtype)
    {
        const struct mem_type *type;
        int err;
        unsigned long addr;
         struct vm_struct * area;

        /*
         * High mappings must be supersection aligned
         */
        if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))
            return NULL;

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

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

         area = get_vm_area(size, VM_IOREMAP); //分配虚拟地址空间
         if (!area)
             return NULL;
         addr = (unsigned long)area->addr;
         //此处打印出的地址: addr=0xc5400000
    #ifndef CONFIG_SMP //不进入此处
        if (DOMAIN_IO == 0 &&
         (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
         cpu_is_xsc3()) && pfn >= 0x100000 &&
         !((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {
            area->flags |= VM_ARM_SECTION_MAPPING;
            err = remap_area_supersections(addr, pfn, size, type);
        } else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) {
            area->flags |= VM_ARM_SECTION_MAPPING;
            err = remap_area_sections(addr, pfn, size, type);
        } else
    #endif
            err = remap_area_pages(addr, pfn, size, type);

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

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

area = get_vm_area(size, VM_IOREMAP); //分配虚拟地址空间

err = remap_area_pages(addr, pfn, size, type);

这两个函数。

  1. 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;

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

        pgd = pgd_offset_k(addr);
        do {
            pmd_t *pmd = pmd_offset(pgd, addr);

            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 += PGDIR_SIZE;
            pgd++;
        } while (addr < end);

        return 0;
    }

上一篇: win下vscode with sdl2配置lvgl模拟器开发环境

下一篇: 关于vim插件安装的一些基本知识