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
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
相关文章推荐
- Linux内核---45.关于initcall
- Linux内核---44.关于中断号与中断引脚
- Linux内核---43.文件系统的挂载
- Linux内核---42.arm 内存初始化2
- [CentOS7]安装mysql遇到的问题
- Linux内核---41.arm 内存初始化
- Linux内核---40.模块加载过程分析
- Linux内核---39.ELF 结构分析
- centos更新163源并升级内核
- Linux小知识
- linux常用命令
- 在linux使用make编译ArduPilot for Pixhawk/PX4 ArduPilot 编译环境搭建
- gcc: error: elf_i386: No such file or directory
- 橙子第一篇文章
- linux菜鸟学习(一)----ls,cd,pwd,mkdir,rmdir,rm
- centos yum升级php5.3.3到最5.6.3
- 【linux GDB】linux下GDB调试器_学习笔记_003
- centos安装php扩展mssql
- centos最常用命令及终端快捷键整理
- ELF文件分析