2017-01-18

文来自本人的旧博客 http://blog.163.com/awaken_ing/blog/static/1206131972016226674429

1. 主要流程

平台:versatilepb ARM Versatile/PB (ARM926EJ-S) 内核版本2.6.35.7

paging_init
|--devicemaps_init
|   |--from VMALLOC_END, pmd_clear(pmd_off_k(addr))
|   |--mdesc->map_io() => versatile_map_io  
|   |   |--iotable_init
|   |   |   |--create_mapping

2. 为何是 MT_DEVICE

linux-2.6.35.7/arch/arm/mach-versatile/core.c

void __init versatile_map_io(void)
{
    iotable_init(versatile_io_desc, ARRAY_SIZE(versatile_io_desc));
}

static struct map_desc versatile_io_desc[] __initdata = {
    {
        ...
        .type       = MT_DEVICE
    },
    ...

这里是 MTDEVICE, 而不是 MTUNCACHED, why?

MTDEVICE类型和 MTUNCACHED 的区别在 .protpte 和 .protsect 上.

看看 .protpte 和 .protsect 的作用?

2.1 .prot_pte 用途

使用到 .prot_pte 的地方:

alloc_init_pte
{
    set_pte_ext(pte, pfn_pte(pfn, type->prot_pte), 0);
}

等效于

cpu_arm926_set_pte_ext(pte, pfn_pte(pfn, type->prot_pte), 0);
#define pfn_pte(pfn,prot)    (__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))

cpuarm926setpteext将主要工作交给 armv3setpteext, 这里并没有判断 LPTEMTDEVSHARED 或者 LPTE_SHARED, 所以, 对于硬件页表, 添加上也没有影响.

2.2 .prot_sect 用途

alloc_init_section
{
pmd_t *pmd = pmd_offset(pgd, addr);
...如果 创建的是 section.
*pmd = phys | type->prot_sect;
}

所以, .prot_sect 用来写到物理页表去的, 不像二级页表, 这里linux不需要转换啥的.

MTDEVICE的 .protsect = PROTSECTDEVICE | PMDSECTS,

等效于 PMDTYPESECT|PMDSECTAPWRITE | PMDSECTS MTUNCACHED的

.prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,

buildmemtypetable()中会根据arm版本, 对 .protsect 进行修正, 比如:

build_mem_type_table
{
    if (cpu_arch < CPU_ARCH_ARMv6 ...)
        mem_types[i].prot_sect &= ~PMD_SECT_S;
}

这里就不走读代码了, 直接打印结果.

往buildmemtype_table()中添加打印后(未添加上domain), 有

MTDEVICE 的 .protsect 为 0x412

MTUNCACHED的 .protsect 为 0x12

C(D3)和B(D2) 都是关闭的.

也就是多了个 PMDSECTAP_WRITE

#define PMD_SECT_AP_WRITE (1 << 10)

先检查domain, 如果为01(Client), 则接着检查AP bits.

如果创建的是section的话, 类型MT_UNCACHED, 最多只能够 read-only, 不符合要求, 故不使用.

3. 虚拟地址指定成啥好呢?

create_mapping
{
    if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
        md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) {
        printk(KERN_WARNING "BUG: mapping for 0x%08llx at 0x%08lx "
               "overlaps vmalloc space\n",
               __pfn_to_phys((u64)md->pfn), md->virtual);
    }
}

如果是MTDEVICE, 那么, 不太建议映射(不是禁止, 因为只是打印了warning而已)到 3G~VMALLOCEND 之间. 建议我们映射到vmalloc与DMA之间的gap内 (vmalloc和DMA间的gap可不是8MB).

看下versatile的映射的虚拟地址. linux-2.6.35.7/arch/arm/mach-versatile/core.c

static struct map_desc versatile_io_desc[] __initdata = {
    {
        .virtual    =  IO_ADDRESS(VERSATILE_SYS_BASE),
        .pfn        = __phys_to_pfn(VERSATILE_SYS_BASE),
        .length        = SZ_4K,
        .type        = MT_DEVICE
    },

#define VERSATILE_SYS_BASE   0x1000 0000
#define IO_ADDRESS(x)  (((x) & 0x0fff ffff) 
                       + (((x) >> 4) & 0x0f00 0000) 
                       + 0xf000 0000)

所以, 计算出来的虚拟地址为 0xf100 0000.

Virtual kernel memory layout:
...
    DMA     : 0xffc00000 - 0xffe00000   (   2 MB)
    vmalloc : 0xc8800000 - 0xd8000000   ( 248 MB)

正是处于vmalloc与DMA之间的gap内.

本文地址: https://awakening-fong.github.io/posts/arm/arm_map_io

转载请注明出处: https://awakening-fong.github.io


若无法评论, 请打开JavaScript, 并通过proxy.


blog comments powered by Disqus