Linux 内存虚实内存映射@TOC
以前关于虚拟地址和物理地址的学习只是在书本上,今天在实际的开发板上实践了一下
代码:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h
#include <linux/init_task.h>
int g=0x100;
static int __init tarzan_init(void)
{
int idx;
pr_info(“tarzan_init\n”);
int x=0x10;
int pint = NULL;
printk(“PHYS_OFFSET=0x%lx\r\n” , PHYS_OFFSET);
printk(“max_mapnr:%d\r\n” , max_mapnr);
printk(“high memory:0x%lx\r\n” , high_memory); //0xF000_0000 0xF000_0000- 0xC000_0000=0x3000_0000=768M
printk(“high memory(768M) -> phy addr:0x%lx\r\n” , __pa(high_memory));
pint = kmalloc(1024sizeof(int),GFP_KERNEL);
if(!pint)
{
return -1;
}
else
{
printk(“virtual addr is :0x%lx , phy addr:0x%x\r\n” , pint , __pa(pint));
}
pr_info(“g -> VA=%lx , PA=%lx\r\n” , &g , __pa(&g));
pr_info(“x -> VA=%lx , PA=%lx\r\n” , &x , __pa(&x));
pr_info(“PAGE_OFFSET => %lx” , PAGE_OFFSET);
return 0;
}
static void __exit tarzan_exit(void)
{
pr_info(“tarzan_exit\n”);
}
module_init(tarzan_init);
module_exit(tarzan_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“ZTE-liukai”);
Makefile:
ifneq ($(KERNELRELEASE),)
obj-m := tarzan.o
else
KDIR := /home/zte/work/hisi/hi3519/Hi3519AV100R001C02SPC020_NEW/Hi3519AV100R001C02SPC020/Hi3519AV100R001C02SPC020/01.software/board/Hi3519AV100_SDK_V2.0.2.0/osdrv/opensource/kernel/linux-4.9.y
all:
make -C
(
K
D
I
R
)
M
=
(KDIR) M=
(KDIR)M=(PWD) modules ARCH=arm CROSS_COMPILE=arm-himix200-linux-
clean:
rm -f *.ko *.o *.mod.o *.mod.c .symvers modul
endif
- 实验
3.1 Hi3519AV100平台(32位,ARMv7平台)
运行结果:
/run/media/mmcblk0p4 #
/run/media/mmcblk0p4 # insmod tarzan.ko
tarzan_init
PHYS_OFFSET=0x22000000
max_mapnr:262144 (物理页框的个数,一个页框4KB)
high memory:0xf0000000
high memory(768M) -> phy addr:0x52000000
virtual addr is :0xee89e000 , phy addr:0x5089e000
g -> VA=bf016220 , PA=21016220
x -> VA=ef3d1dc0 , PA=513d1dc0
PAGE_OFFSET => c0000000
Phy addr of 0xc0000000 is -> 0x22000000
Phy addr of 0xc0000001 is -> 0x22000001
Phy addr of 0xc0000002 is -> 0x22000002
Phy addr of 0xc0000003 is -> 0x22000003
Phy addr of 0xc0000004 is -> 0x22000004
Phy addr of 0xc0000005 is -> 0x22000005
Phy addr of 0xc0000006 is -> 0x22000006
Phy addr of 0xc0000007 is -> 0x22000007
Phy addr of 0xc0000008 is -> 0x22000008
Phy addr of 0xc0000009 is -> 0x22000009
Vir addr 0f 0x0 is -> 0x9e000000
Vir addr 0f 0x1 is -> 0x9e000001
Vir addr 0f 0x2 is -> 0x9e000002
Vir addr 0f 0x3 is -> 0x9e000003
Vir addr 0f 0x4 is -> 0x9e000004
Vir addr 0f 0x5 is -> 0x9e000005
Vir addr 0f 0x6 is -> 0x9e000006
Vir addr 0f 0x7 is -> 0x9e000007
Vir addr 0f 0x8 is -> 0x9e000008
Vir addr 0f 0x9 is -> 0x9e000009
名称 进程号 状态 优先级 父进程号
init 1 1 120
udevd 101 1 116
udevd 135 1 118
udevd 136 1 118
lighttpd 393 1 120
sshd 397 1 120
dnsmasq 402 1 120
sh 412 1 120
insmod 425 0 120
/-----------------------------------------------/
/run/media/mmcblk0p4 #
/run/media/mmcblk0p4 #
从运行的LOG来看,内核态起始地址0xC000_0000对应的物理地址是0x2200_0000,和常见的书本上提到的"0xC000_0000映射到物理内存0x0000_0000"有出入,思考了一下,发现问题,DDR颗粒的0地址在ARM内存映射的地址不一定是0地址,系统的定义为:PHYS_OFFSET,该参数的定义为:DDR的地址偏移,后来从海思的手册里找到这个参数的定义:
从这张图里可以看出DDR给到Linux OS的地址是从0x2200_0000偏移开始的。
tarzan_init
PHYS_OFFSET=0x80000000
max_mapnr:131072
high memory:0xe0000000
high memory(768M) -> phy addr:0xa0000000
virtual addr is :0xde7de000 , phy addr:0x9e7de000g -> VA=bf55221c , PA=7f55221c
x -> VA=de711dc0 , PA=9e711dc0
PAGE_OFFSET => c0000000
Phy addr of 0xc0000000 is -> 0x80000000
Phy addr of 0xc0000001 is -> 0x80000001
Phy addr of 0xc0000002 is -> 0x80000002
Phy addr of 0xc0000003 is -> 0x80000003
Phy addr of 0xc0000004 is -> 0x80000004
Phy addr of 0xc0000005 is -> 0x80000005
Phy addr of 0xc0000006 is -> 0x80000006
Phy addr of 0xc0000007 is -> 0x80000007
Phy addr of 0xc0000008 is -> 0x80000008
Phy addr of 0xc0000009 is -> 0x80000009
Vir addr 0f 0x0 is -> 0x40000000
Vir addr 0f 0x1 is -> 0x40000001
Vir addr 0f 0x2 is -> 0x40000002
Vir addr 0f 0x3 is -> 0x40000003
Vir addr 0f 0x4 is -> 0x40000004
Vir addr 0f 0x5 is -> 0x40000005
Vir addr 0f 0x6 is -> 0x40000006
Vir addr 0f 0x7 is -> 0x40000007
Vir addr 0f 0x8 is -> 0x40000008
Vir addr 0f 0x9 is -> 0x40000009
/-----------------------------------------------/
~ #
~ #
可见,Hi3516DV300的 DDR起始地址为0x8000_0000
3.3 飞思卡尔i.max6平台
/mnt # insmod tarzan.ko
tarzan_init
PHYS_OFFSET=0x80000000
max_mapnr:131072
high memory:0xa0000000
high memory(768M) -> phy addr:0xa0000000
virtual addr is :0x88429000 , phy addr:0x88429000
g -> VA=7f00021c , PA=7f00021c
x -> VA=887fbdf0 , PA=887fbdf0
PAGE_OFFSET => 80000000
Phy addr of 0xc0000000 is -> 0xc0000000
Phy addr of 0xc0000001 is -> 0xc0000001
Phy addr of 0xc0000002 is -> 0xc0000002
Phy addr of 0xc0000003 is -> 0xc0000003
Phy addr of 0xc0000004 is -> 0xc0000004
Phy addr of 0xc0000005 is -> 0xc0000005
Phy addr of 0xc0000006 is -> 0xc0000006
Phy addr of 0xc0000007 is -> 0xc0000007
Phy addr of 0xc0000008 is -> 0xc0000008
Phy addr of 0xc0000009 is -> 0xc0000009
Vir addr 0f 0x0 is -> 0x0
Vir addr 0f 0x1 is -> 0x1
Vir addr 0f 0x2 is -> 0x2
Vir addr 0f 0x3 is -> 0x3
Vir addr 0f 0x4 is -> 0x4
Vir addr 0f 0x5 is -> 0x5
Vir addr 0f 0x6 is -> 0x6
Vir addr 0f 0x7 is -> 0x7
Vir addr 0f 0x8 is -> 0x8
Vir addr 0f 0x9 is -> 0x9
飞思卡尔的该芯片使用的是1:1 (user:kernel)模型,即用户态和内核态地址范围均为2GB。
该平台为1:1的使用模型,PAGE_OFFSET=80000000(2GB), PHYS_OFFSET=0x80000000,最后的结果为物理地址=虚拟地址
4. 结论
i.max地址映射范围可知,DDR的起始地址为0x8000_0000 , 故PHYS_OFFSET=0x80000000
这个参数Kernel是怎么知道的?答案是通过单板的DTS传给内核的,起始地址为0x8000_0000,大小为512MB
i.max6 dts 内存配置
hi3519 dts 内存配置
hi3516 dts内存配置
在低端内存的虚实地址换算的方法如下,为线性映射,两者之间差一个固定偏移,这个偏移不一定是0xC000_0000,还和DDR内存地址在系统中的起始地址有关系,具体计算方法如下:
虚拟地址=物理地址 - PHYS_OFFSET + PAGE_OFFSET
物理地址=虚拟地址 - PAGE_OFFSET + PHYS_OFFSET