上篇中介绍了基于PowerPC的Linux第二阶段启动过程的一部分,由于MMU的初始化涉及的内容较多,而且代码量很大,所以这部分分为上下两部。本部分继续阐述MMU硬件的初始化和最终真正使能MMU的过程。
在开始之前,先指出前面文章出现的一个错误,在对mmu_off函数中的RFI指令的介绍时,我简单的认为是中断返回,但后来想想,CPU初始化时,中断还没有使能,所以中断返回的说法是不正确的,查了下资料,原来使用RFI指令也可以做程序跳转使用,使用RFI进行程序跳转的好处是,程序跳转后将自动执行isync指令,以保证指令空间的同步,在Linux的初始化阶段,使用RFI指令进行程序跳转比较常见,这里的RFI指令与中断返回是没有任何关系的。造成误解请见谅。
另外,最近被工作上的事情困扰的有些力不从心了,感觉这篇对MMU的硬件初始化分析写的极其的烂,待以后收拾了心情,我一定会加以改进的,当然,也希望高手能不吝指教,就当交个朋友吧。
先来看一下MMU_init_hw的详细代码(位于mm/ppc_mmu_32.c):
void __init MMU_init_hw(void)
{
unsigned int hmask, mb, mb2;
unsigned int n_hpteg, lg_n_hpteg;
/*定义于hash_low_32.S,填充和清除Hash表*/
extern unsigned int hash_page_patch_A[];
extern unsigned int hash_page_patch_B[], hash_page_patch_C[];
extern unsigned int hash_page[];
extern unsigned int flush_hash_patch_A[], flush_hash_patch_B[];
if (!mmu_has_feature(MMU_FTR_HPTE_TABLE)) {
/*在hash_page的开始处放置blr指令,因为在603处理器中仍能接收到DSI(Data Storage Interrupt)异常*/
hash_page[0] = 0x4e800020;
flush_icache_range((unsigned long) &hash_page[0],
(unsigned long) &hash_page[1]); /*清空指令cache*/
return;
}
if ( ppc_md.progress ) ppc_md.progress("hash:enter", 0x105);
#define LG_HPTEG_SIZE 6 /* 每个PTEG为64个字节 */
#define SDR1_LOW_BITS ((n_hpteg - 1) >> 10)
#define MIN_N_HPTEG 1024 /* min 64kB hash table */
/*允许每页内存都有一个HPTE*/
n_hpteg = total_memory / (PAGE_SIZE * 8);
if (n_hpteg < MIN_N_HPTEG)
n_hpteg = MIN_N_HPTEG;
lg_n_hpteg = __ilog2(n_hpteg);
if (n_hpteg & (n_hpteg - 1)) {
++lg_n_hpteg; /* round up if not power of 2 */
n_hpteg = 1 << lg_n_hpteg;
}
Hash_size = n_hpteg << LG_HPTEG_SIZE;
/*为哈希表申请内存地址,这两步就类似于malloc和memset*/
if ( ppc_md.progress ) ppc_md.progress("hash:find piece", 0x322);
Hash = __va(memblock_alloc_base(Hash_size, Hash_size,