静态映射表建立过程分析

建立映射表的三个关键部分
1.映射表具体物理地址和虚拟地址的值相关的宏定义:
位于arch/arm/plat-s5p/cpu.c文件中s5p_iodesc结构体数组
static struct map_desc s5p_iodesc[] __initdata = {
{
.virtual = (unsigned long)S5P_VA_CHIPID,
.pfn = __phys_to_pfn(S5P_PA_CHIPID),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_SYS,
.pfn = __phys_to_pfn(S5P_PA_SYSCON),
.length = SZ_64K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_UART,
.pfn = __phys_to_pfn(S3C_PA_UART),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)VA_VIC0,
.pfn = __phys_to_pfn(S5P_PA_VIC0),
.length = SZ_16K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)VA_VIC1,
.pfn = __phys_to_pfn(S5P_PA_VIC1),
.length = SZ_16K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_TIMER,
.pfn = __phys_to_pfn(S5P_PA_TIMER),
.length = SZ_16K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S5P_VA_GPIO,
.pfn = __phys_to_pfn(S5P_PA_GPIO),
.length = SZ_4K,
.type = MT_DEVICE,
},
};
这个数组对应的是一个映射表,每一个元素都是一个映射的关系。
例如:
#define S5P_VA_GPIO S3C_ADDR(0x00500000) = 0xFD500000
#define S5PV210_PA_GPIO (0xE0200000)
#define S5P_PA_GPIO S5PV210_PA_GPIO

	.virtual	= (unsigned long)S5P_VA_GPIO,
	.pfn		= __phys_to_pfn(S5P_PA_GPIO),
	.length		= SZ_4K,
	.type		= MT_DEVICE,

这个映射表就是物理地址0xE0200000,映射到虚拟地址0xFD500000。
SZ_4K应该就是映射的长度,这个就是4KByte的长度。
我们的映射的虚拟地址存在于两个文件:
arch/arm/plat-s5p/include/plat/map-s5p.h和
arch/arm/plat-samsung/include/plat/map-base.h中。


2.映射表建立函数。该函数负责由(1)中的映射表来建立linux内核的页表映射关系。
在kernel/arch/arm/mach-s5pv210/mach-smdkc110.c中的smdkc110_map_io函数
smdkc110_map_io
s5p_init_io
iotable_init
结论:经过分析,真正的内核移植时给定的静态映射表在arch/arm/plat-s5p/cpu.c中的s5p_iodesc,本质是一个结构体数组,数组中每一个元素就是一个映射,这个映射描述了一段物理地址到虚拟地址之间的映射。这个结构体数组所记录的几个映射关系被iotable_init所使用,该函数负责将这个结构体数组格式的表建立成MMU所能识别的页表映射关系,这样在开机后可以直接使用相对应的虚拟地址来访问对应的物理地址


3.开机时调用映射表建立函数
问题:开机时(kernel启动时)smdkc110_map_io怎么被调用的?
赋值:.map_io = smdkc110_map_io,
调用:
start_kernel
setup_arch
paging_init
devicemaps_init

if (mdesc->map_io)
mdesc->map_io();


使用writel和readl函数,以及读改写操作,见代码:

static void __iomem * test_base_addr = NULL;
#define S5P_GPJ0CON_OFFSET 0
#define S5P_GPJ0DAT_OFFSET 4
if (!(test_base_addr = ioremap(PHY_GPJ0CON, 8)))
{
printk(KERN_INFO “request_mem_region fail\n”);
goto ioremap_error;
}
temp = readl(test_base_addr + S5P_GPJ0CON_OFFSET);
temp |= (1<<12) | (1<<16) | (1<<20);
writel(temp, test_base_addr + S5P_GPJ0CON_OFFSET);

temp = readl(test_base_addr + S5P_GPJ0DAT_OFFSET);
temp &= ~((1<<3) | (1<<4) | (1<<5));
writel(temp, test_base_addr + S5P_GPJ0DAT_OFFSET);

我们使用ioremap返回void __iomem *类型的值,其实是void *类型,__iomem 是一个attribute的修饰符。返回一个指向物理地址的虚拟地址,是void * 类型的。
readl:传参是一个虚拟地址。
writel:传参第一个是一个值,第二个是一个虚拟地址。
读该写操作:
1.使用readl读出来值
2.修改该值
3.writel写回去
还有两个函数:
iowrite32和ioread32,使用方法和上面的类似

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值