关于arm linux的map_io
文来自本人的旧博客 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