作者:wogoyixikexie@gliet
void Launch(DWORD dwLaunchAddr)这个函数是在SMDK2440A/Src/Bootloader/Eboot/util.s(32)实现的
;******************************************************************************
INCLUDE kxarm.h
PHY_RAM_START EQU 0x30000000
VIR_RAM_START EQU 0x80000000
TEXTAREA
LEAF_ENTRY Launch
ldr r2, = PhysicalStart
ldr r3, = (VIR_RAM_START - PHY_RAM_START)
sub r2, r2, r3
mov r1, #0x0070 ; Disable MMU
mcr p15, 0, r1, c1, c0, 0
nop
mov pc, r2 ; Jump to PStart
nop
; MMU & caches now disabled.
PhysicalStart
mov r2, #0
mcr p15, 0, r2, c8, c7, 0 ; Flush the TLB
mov pc, r0 ; Jump to program we are launching.
根据C语言中void Launch(DWORD dwLaunchAddr),只有一个参数以及C和汇编函数调用的参数传递规则。
这个DWORD dwLaunchAddr应该传递给汇编中的r0。
但是我搞不明白这东西是怎么启动起来的。并且这个启动方法和优龙的有很大差别。
根据config.bib中的设置,这个内核开始的物理地址应该是)0x30200000
按照道理应该是直接运行PhysicalStart这个汇编段才能运行起来,但是怎么会在前面横插下面的代码呢?
ldr r2, = PhysicalStart
ldr r3, = (VIR_RAM_START - PHY_RAM_START)
sub r2, r2, r3
mov r1, #0x0070 ; Disable MMU
mcr p15, 0, r1, c1, c0, 0
nop
mov pc, r2 ; Jump to PStart
还有一个疑问,为什么eboot要在nboot之后开启MMU,但是在启动内核的时候又关闭MMU,不能在eboot把MMU都关闭吗?
这样做有什么好处?
=================================================================
你说的我明白,我的意思是对他的做法有点不解。
我觉得这个比较令人费解。
ldr r2, = PhysicalStart
ldr r3, = (VIR_RAM_START - PHY_RAM_START) //为什么要这样做呢?
sub r2, r2, r3
直接把这个0x30200000放到PC不行吗?
------------------------------------------------
优龙是直接跳转到相应的内存物理地址运行的
不会像在上面那样搞得模模糊糊。
// ==================================================== // 优龙bootloader加载NK运行入口函数:使用函数指针的方法, // 相当巧妙,程序可读性比三星自带的eboot强悍 // ==================================================== void call_linux(U32 a0, U32 a1, U32 a2) { void ( * goto_start)(U32, U32); rINTMSK = BIT_ALLMSK; cache_clean_invalidate(); tlb_invalidate(); __asm{ // mov r0, a0 // %0 // mov r1, a1 // %1 // mov r2, a2 // %2 mov ip, # 0 mcr p15, 0 , ip, c13, c0, 0 /* zero PID */ mcr p15, 0 , ip, c7, c7, 0 /* invalidate I,D caches */ mcr p15, 0 , ip, c7, c10, 4 /* drain write buffer */ mcr p15, 0 , ip, c8, c7, 0 /* invalidate I,D TLBs */ mrc p15, 0 , ip, c1, c0, 0 /* get control register */ bic ip, ip, # 0x0001 /* disable MMU */ mcr p15, 0 , ip, c1, c0, 0 /* write control register */ // mov pc, r2 // nop // nop /* no outpus */ // : "r" (a0), "r" (a1), "r" (a2) } // SetClockDivider(1, 1); // SetSysFclk(FCLK_200M); // start kernel, use 200M // SET_IF(); goto_start = ( void ( * )(U32, U32))a2; // 这个a2=0x30200000,在调用的时候传递进来 ( * goto_start)(a0, a1); }
----------------------------------------------------------------------------
问题的关键是在Disable MMU后必须在2条指令内执行mov pc操作,否则程序就跑飞
但是在真正跳转到NK地址前,还需要flush cache等操作
所以就先跳到PhysicalStart处,做完其他操作再跳到NK入口
-----------------------------------------------------------------------------
问题的关键是在Disable MMU后必须在2条指令内执行mov pc操作,否则程序就跑飞
这个在哪里有介绍呢?这个我是第一听说,学习了。
但是在真正跳转到NK地址前,还需要flush cache等操作 ,所以就先跳到PhysicalStart处,做完其他操作再跳到NK入口
对,的确如此。
我的疑惑是
ldr r2, = PhysicalStart
ldr r3, = (VIR_RAM_START - PHY_RAM_START) //求出虚拟地址和物理地址之差。其实虚拟、物理地址转换就是这个原理
sub r2, r2, r3 //这个相当于是PhysicalStart 所在地址减去(-)虚拟地址和物理地址之差
mov r1, #0x0070 ; Disable MMU
mcr p15, 0, r1, c1, c0, 0
nop
mov pc, r2 ; Jump to PStart 这个难道会跳到PhysicalStart 去执行?按照他的程序,是会的。
难道程序这么做是把这个PhysicalStart所在的虚拟地址转换为物理地址?
就是PhysicalStart(虚拟地址) -虚拟地址和物理地址之差
难道编译器编译出来的东西也有物理地址和虚拟地址之分的?
真是这样吗?我觉得这个是比较合理的解释。
因为优龙没有使用(VIR_RAM_START - PHY_RAM_START)的原因就是因为,它在bootloader阶段已经关闭了MMU,没有必要这么做了。
各位前辈,不知道我这样理解是否正确,请指教。
----------------------------------------------------------------------------
引用 14 楼 wohuazhen 的回复:
难道编译器编译出来的东西也有物理地址和虚拟地址之分的?
我认为ldr r0, =PhysicalStart中的PhysicalStart,编译器给的是物理地址,但是当在打开MMU后,这段代码加载到cpu解释过程中会被处理器转换成虚拟地址。
不是啊,这个标号所标识的地址在MMU打开的时候是是标识虚拟地址,当关闭MMU它就是物理地址,由于,这个代码已经在MMU打开的时候跑到内存运行了,所以我们要手动把它变回物理地址,就是下面的代码
ldr r2, = PhysicalStart
ldr r3, = (VIR_RAM_START - PHY_RAM_START)
sub r2, r2, r3
mov r1, #0x0070 ; Disable MMU
mcr p15, 0, r1, c1, c0, 0
nop
mov pc, r2 ; Jump to PStart
——这个代码非常巧妙。刚开始还真是没有反映过来。
----------------------------------------------------------------------------
至于eboot为什么要开MMU,而优龙的ADS Bootloader为什么不开,
我想大家一定和我一样很想知道为什么。
——其实这是由于eboot要调用 FMD驱动以及一些微软的函数,这些函数是运行在虚拟内存的,eboot为了讨好她,利用他们,只能委曲求全了,裸奔还要开MMU,岂有此理!哈哈。
优龙 Bootloader,自然是没有和上面这些联系了。自然没有必要开。
所以才会造成启动NK函数有如此大的不同。
哈哈。自己解决也不错。
----------------------------------------------------------------------------
引用 17 楼 hhyh612 的回复:
编译器其实是不区分虚实地址的,只不过boot.bib里写了RAMIMAGE的地址是80000000(所谓的虚地址),所以PhysicalStart这个标号的值就是8XXXXXXX的虚地址,在关闭mmu后,必须把它转成物理地址3XXXXXXX
至于为什么Disable MMU后2条指令内必须mov pc
是因为执行mcr p15, 0, r1, c1, c0, 0 指令时pc是虚地址8XXXXXXX
执行完MMU就关了,8XXXXXXX就不存在了,所以需要马上让pc变成实地址3XXXXXXX
2条指令内是因为ARM流水线的原因,所以MMU关了,但是mcr之后的两条指令以及在pc是虚地址的时候预取了,所以不会报错
如果2条指令之内不mov pc到3XXXXXXX,接下来就要Prefetch Abort了。。——完全明白了,谢谢你
谢谢 hhyh612 ,所有迷惑解了。
引用 15 楼 gooogleman 的回复:
引用 14 楼 wohuazhen 的回复:
难道编译器编译出来的东西也有物理地址和虚拟地址之分的?
我认为ldr r0, =PhysicalStart中的PhysicalStart,编译器给的是物理地址,但是当在打开MMU后,这段代码加载到cpu解释过程中会被处理器转换成虚拟地址。
不是啊,这个标号所标识的地址在MMU打开的时候是是标识虚拟地址,当关闭MMU它就是物理地址,由于,这个代码已经在MMU打开的时候跑到内存运行了,所以我们要手动把它变回物理地…
关于程序相关标号,昨下班回去我查看了《ARM系列处理器应用完全技术手册》一书,它的解释,这种标号在汇编时被处理成PC值加上或减去一个数字常量。我想这是为什么“这个标号所标识的地址在MMU打开的时候是是标识虚拟地址,当关闭MMU它就是物理地址”的原因了。
另外“2条指令内是因为ARM流水线的原因”合理的解释,学习了。
==================================================================
引用 17 楼 hhyh612 的回复:
编译器其实是不区分虚实地址的,只不过boot.bib里写了RAMIMAGE的地址是80000000(所谓的虚地址),所以PhysicalStart这个标号的值就是8XXXXXXX的虚地址,在关闭mmu后,必须把它转成物理地址3XXXXXXX
至于为什么Disable MMU后2条指令内必须mov pc
是因为执行mcr p15, 0, r1, c1, c0, 0 指令时pc是虚地址8XXXXXXX
执行完MMU就关了,8XXXXXXX就不存在了,所以需要马上让pc变成实地址3XXXXXXX
2条指令内是因为ARM流水线的原因,所以MMU关了,但是mcr之后的两条指令以及在pc是虚地址的时候预取了,所以不会报错
如果2条指令之内不mov pc到3XXXXXXX,接下来就要Prefetch Abort了。。——完全明白了,谢谢你
谢谢 hhyh612 ,所有迷惑解了。
=========================================================================
引用 19 楼 wohuazhen 的回复:
引用 15 楼 gooogleman 的回复:
引用 14 楼 wohuazhen 的回复:
难道编译器编译出来的东西也有物理地址和虚拟地址之分的?
我认为ldr r0, =PhysicalStart中的PhysicalStart,编译器给的是物理地址,但是当在打开MMU后,这段代码加载到cpu解释过程中会被处理器转换成虚拟地址。
不是啊,这个标号所标识的地址在MMU打开的时候是是标识虚拟地址,当关闭MMU它就是物理地址,由于,这个代码已经在MMU打开的时候跑到内存运行…
不是吧,标号的值不会变得
用mov pc的话,一定用的是绝对地址,和pc无关
如果用b指令,那么用的是pc相关的相对地址
=============================================================================
最后,还要给点提醒:就是汇编和C混合编程在ARM下使用R0,R1,R2,R3来传递参数,而X86使用堆栈来传递
关于汇编和C语言函数参数传递问题请参考这个帖子
http://linux.chinaunix.net/techdoc/develop/2008/06/28/1013691.shtml
http://topic.csdn.net/u/20071128/16/d7c16f51-b70c-4b2f-a65b-f4c90c519d00.html
转载请标明:作者wogoyixikexie@gliet.桂林电子科技大学一系科协。如有错误,希望能够留言指出。