您的位置:首页 > 运维架构 > Linux

Linux内核---46.关于mem_map

2016-07-09 10:24 316 查看
一. Unable to handle kernel paging
request at virtual address
移植LCD驱动,会出现如下的错误:
Unable to handle kernel paging request at virtual address 0xf7100130
定位一下发现是在drviers/video/samsun/s3cfb_fimd4x.c中,一调用writel就会报错

void s3cfb_pre_init(void)

{

    s3cfb_fimd.vidintcon0 &= ~S3C_VIDINTCON0_FRAMESEL0_MASK;

    s3cfb_fimd.vidintcon0 |= S3C_VIDINTCON0_FRAMESEL0_VSYNC;

    s3cfb_fimd.vidintcon0 |= S3C_VIDINTCON0_INTFRMEN_ENABLE;

    //打印dbmsg("S3C_VIDINTCON0=0x%x",S3C_VIDINTCON0);

    writel(s3cfb_fimd.vidintcon0, S3C_VIDINTCON0);

}

同时打印S3C_VIDINTCON0的值正好是0xf7100130,
也就是说0xf7100130这个虚拟地址没有与io端口相关联.
查找开发板自带的内核发现是在arch/arm/mach-s3c64xx/mach-smdk6410.c中,
添加了如下定义:

static struct map_desc smdk6410_iodesc[] = {

    {        

        .virtual = (unsigned long)S3C_VA_LCD,  
//0x

        .pfn = __phys_to_pfn(S3C_PA_FB),

        .length = SZ_16K,

        .type = MT_DEVICE,

    },

};

那么这段代码有什么作用呢?
二. IO端口映射
用到smdk6410_iodesc的是:
在arch/arm/mach-s3c64xx/mach-smdk6410.c中

static void __init smdk6410_map_io(void)

{

    //映射 IRQ SYS MEM TIMER WATCHDOG UART LCD

    s3c64xx_init_io(smdk6410_iodesc, ARRAY_SIZE(smdk6410_iodesc));   

}

在arch/arm/mach-s3c64xx/cpu.c中

void __init s3c64xx_init_io(struct map_desc *mach_desc, int size)

{

    unsigned long idcode;

    //下面这一行映射 IRQ SYS MEM TIMER WATCHDOG UART 

    iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));  

    //下面这一行只映射LCD 

    iotable_init(mach_desc, size);      //下面就以LCD为例说一下

    idcode = __raw_readl(S3C_VA_SYS + 0x118);

    if (!idcode) {

        __raw_writel(0x0, S3C_VA_SYS + 0xA1C);

        idcode = __raw_readl(S3C_VA_SYS + 0xA1C);

    }

    s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));

}

在arch/arm/mm/mmu.c中

void __init iotable_init(struct map_desc *io_desc, int nr)

{

    //LCD的map_desc只有一项,所以这个nr=1

    for (i = 0; i < nr; i++)   

        create_mapping(io_desc + i);

}

在arch/arm/mm/mmu.c中

static void __init create_mapping(struct map_desc *md)

{

    unsigned long addr, length, end;

    phys_addr_t phys;

    const struct mem_type *type;

    pgd_t *pgd;

    if (md->virtual != vectors_base() && md->virtual < TASK_SIZE)

        return;    

    if ((md->type == MT_DEVICE || md->type == MT_ROM) &&

     md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) {        

    }

    type = &mem_types[md->type];

    //页帧地址>0x100000,即实际的物理地址>0x100000000=4G, 大于4G的物理地址,现在不适合

    if (md->pfn >= 0x100000) {    

        create_36bit_mapping(md, type);

        return;

    }

    addr = md->virtual & PAGE_MASK;

    phys = __pfn_to_phys(md->pfn);

    length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));

    if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {       

        return;

    }

    pgd = pgd_offset_k(addr);

    end = addr + length;

    do {

        unsigned long next = pgd_addr_end(addr, end);

        alloc_init_pud(pgd, addr, next, phys, type);

        phys += next - addr;

        addr = next;

    } while (pgd++, addr != end);

}

内存映射在内核启动时就会去映射,并且早于驱动的加载,如果没有映射端口就在驱动中使用了虚拟地址就会报上面的错误.
http://blog.chinaunix.net/uid-26009923-id-3860465.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: