MMU及PTS说明
MMU與PTS表格
最近在FPGA上仿真調試Virgo(基于ARM11的一款處理器)芯片。MMU部分總是出錯,具體的現象是查看物理地址和虛擬地址的映射時候芯片經常會掛掉。先是懷疑MMU的寄存器配置有問題,后來又懷疑MMU映射使用的PTS表格有問題,最后發現竟然是RAM沒有清零導致的,唉,竟然犯了這種的錯誤,實在是雷人。
為了解決問題,這兩天對這部分代碼進行了分析和調試,擔心過兩天會忘掉,趕緊寫下來。
1.MMU初始化代碼分析
其實MMU的初始化過程就是PTS表格的初始化過程。
那么啥是PTS表格呢?
PTS表格是供MMU進行地址映射和察看內存屬性信息的表格。
PTS表格主要記錄了兩方面的信息,第一:虛擬地址對應的物理地址在哪個位置,第二:虛擬地址的屬性信息,如上面的0x402/0x40e/0x41e。
MMU詳細的初始化過程參照下面的代碼解釋:
| ?? ????????b?????? . ? ??????? INCLUDE oemaddrtab_cfg.inc ? ;------------------------------------------------------------------ ?????? ?; Compute physical address of the OEMAddressTable. 20??? add???? r11, pc, #g_oalAddressTable - (. + 8) ??? ldr???? r10, =PTs??????? ; (r10) = 1st level page table ? ? ??? ; Setup 1st level page table (using section descriptor)???? ??? ; Fill in first level page table entries to create "un-mapped" regions ??? ; from the contents of the MemoryMap array. ??? ; ??? ;?? (r10) = 1st level page table ??? ;?? (r11) = ptr to MemoryMap array ? ??? ; 接下來這三行代碼是配置ptr指針的位置,以及初始化DRAM部分物理地址在PTS映射表中的標記,即E ??? ; 后面會將這個標記放置到PTS映射表中 ??? add???? r10, r10, #0x2000?????? ; (r10) = ptr to 1st PTE for "unmapped space" ??? mov???? r0, #0x0E?????????? ; (r0) = PTE for 0: 1MB cachable bufferable ??? orr???? r0, r0, #0x400????? ; set kernel r/w permission 25??? mov???? r1, r11???????? ; (r1) = ptr to MemoryMap array ? ??? ??? ; 獲取g_oalAddressTable的參數 ??? ; 哈哈,這個就不用解釋了 30??? ldr???? r2, [r1], #4??????? ; (r2) = virtual address to map Bank at ??? ldr???? r3, [r1], #4????? ??; (r3) = physical address to map from ??? ldr???? r4, [r1], #4??????? ; (r4) = num MB to map ? ??? ; g_oalAddressTable表格的最后一行是DCD???? 0x00000000, 0x00000000,? 0? ??? ; 也即r4 = 0 ??? cmp???? r4, #0????????? ; End of table? ??? beq???? %f40 ? ??? ; 這里也不用說了,就是限定最大值為MB和GB ??? ldr???? r5, =0x1FF00000 ??? and???? r2, r2, r5????????? ; VA needs 512MB, 1MB aligned.??????? ? ??? ldr???? r5, =0xFFF00000 ??? and???? r3, r3, r5????????? ; PA needs 4GB, 1MB aligned. ? ??? ; 值得一提的是下面的這個值,網上的爭論也比較多 ??? ; PTS表格中的最小元素代表了MB的物理地址空間,這也是g_oalAddressTable中映射的最小單位是MB的原因 ??? ; 假如說有MB的DRAM需要進行映射,每MB在PTS表格中占據一個元素(四個字節的位置),最終就是: ??? ; 第一個MB放置在PTS表格偏移為x0的位置,假如說這段MB DRAM 的物理地址是x3000 0000,則存放到這里的數據就是x3000 0000 ??? ; 第二個MB放置在PTS表格中偏移為x4的位置,數據是x3010 0000[即這MB空間的起始物理地址] ??? ; 第三個MB放置在PTS表格中偏移為x8的位置,數據是x3020 0000[即這MB空間的起始物理地址] ??? ; 第四個MB放置在PTS表格中偏移為xc的位置,數據是x3030 0000[即這MB空間的起始物理地址] ??? ; 如果DRAM很大的話,依次類推 ??? ; 注意觀察一下上面的偏移x0,其實可以通過(x3000 0000-0x3000 0000)>>18計算出來 ??? ; 注意觀察一下上面的偏移x4,其實可以通過(x3010 0000-0x3000 0000)>>18計算出來 ??? ; 注意觀察一下上面的偏移x8,其實可以通過(x3020 0000-0x3000 0000)>>18計算出來 ??? ; 注意觀察一下上面的偏移xc,其實可以通過(x3030 0000-0x3000 0000)>>18計算出來 ??? ; 很明顯,這個為的右移值是由PTS的最小元素所代表的物理空間大小決定的 ??? add???? r2, r10, r2, LSR #18 ??? add???? r0, r0, r3????????? ; (r0) = PTE for next physical page ? ??? ; 接下來這四行代碼就是將DRAM或者寄存器對應的物理地址填充到PTS表格中,r2是表格的指針,r0是待映射的物理地址 35 ???str???? r0, [r2], #4 ??? add???? r0, r0, #0x00100000???? ; (r0) = PTE for next physical page ??? sub???? r4, r4, #1????????? ; Decrement number of MB left ??? cmp???? r4, #0 ??? bne???? %b35??????????? ; Map next MB ? ??? bic???? r0, r0, #0xF0000000??? ?; Clear Section Base Address Field ??? bic???? r0, r0, #0x0FF00000???? ; Clear Section Base Address Field ??? ; 查詢g_oalAddressTable表格的下一個Element(不知道該咋翻譯) ??? ; 起始一個Element就對應表格g_oalAddressTable的一行,如DCD???? 0x93300000, 0xD0102000,? 1就是一個element ??? b?????? %b30??????????? ; Get next element ??? ??? ; 下面這行代碼是用來將g_oalAddressTable表格中的物理地址同時也映射到xa000 0000~0xbfff ffff這個Uncache空間中 ??? ; tst???? r0, #8和bic???? r0, r0, #0x0C是用來計算后需要填充PTS表格中的標記,其實結果就是x402 ??? ; 第三行add???? r10, r10, #0x0800中的x0800其實就是xa000 0000在PTS表格中的相對偏移(相對于虛擬地址x8000 0000 ??? ; 在pts表格中位置的偏移),可以這樣計算 ??? ; (0xa000 0000-0x8000 0000)>>18 = 0x0800 ??? ; 第行代碼沒有意義,可以刪除 40??? tst???? r0, #8 ??? bic???? r0, r0, #0x0C?????? ; clear cachable & bufferable bits in PTE ??? add???? r10, r10, #0x0800?????? ; (r10) = ptr to 1st PTE for "unmapped uncached space" ??? bne???? %b25??????????? ; go setup PTEs for uncached space ??? sub???? r10, r10, #0x3000?????? ; (r10) = restore address of 1st level page table ? ? ??? ; 接下來是將虛擬地址x0000 0000~0x000f ffff這段空間映射到物理RAM的前MB空間 ??? ; 該芯片上RAM的物理基址在x7000 0000,所以對應的就是x7000 0000~0x700f ffff ??? ; 值得說明的是x7000040E,表示位于x7000 0000這MB空間的基址 ??? ; 而r0表示虛擬地址x0000 0000在PTS表格中的位置,其實就在PTS表格中的最開始位置 ??? ; 1. Setup mmu to map (VA == 0) to (PA == 0x70000000). ??? ; 1-1. cached area. ??? ldr???? r0, =PTs??????? ; PTE entry for VA = 0 ??? ldr???? r1, =0x7000040E???? ; cache/buffer/rw, PA base == 0x70000000 ??? ;ldr? ???r1, =0x70000402???? ; cache/buffer/rw, PA base == 0x70000000 ??? str???? r1, [r0] ? ??? ; 下面三行其實和上面的四行代碼類似,表示將虛擬地址x2000 0000映射到物理地址x7000 0000 ??? ; 第一行代碼中的x0800表示虛擬地址x2000 0000在PTS表格中的偏移 ??? ; 而是UNCACHE ram的標記 ??? ; 1-2. uncached area. ??? add???? r0, r0, #0x0800???? ; PTE entry for VA = 0x0200.0000 , uncached???? ??? ldr???? r1, =0x70000402???? ; uncache/unbuffer/rw, base == 0x70000000 ??? str???? r1, [r0] ??? ??? ??? ; 接下來這段代碼將虛擬地址x7000 0000映射到物理地址x7000 0000,這段映射空間的大小是MB ??? ; 即DRAM空間的大小 ??? ; Comment: ??? ; The following loop is to direct map RAM VA == PA. i.e. ??? ;?? VA == 0x70XXXXXX => PA == 0x70XXXXXX for Virgo ??? ; Fill in 8 entries to have a direct mapping for DRAM ??? ; ??? ldr???? r10, =PTs?????????? ; restore address of 1st level page table ??? ldr???? r0,? =PHYBASE ? ??? ; 下面這一行#(0x7000 / 4)同樣是計算虛擬地址x7000 0000在PTS表格中的偏移 ??? ; 下面這段代碼我沒有改,抄襲了三星的做法,它們沒有寫好,正確的寫法應該是: ??? ; (0x7000 0000>>18),是不是搞得你云里霧里的,鄙視Samsung,將來Vrigo的方案 ??? ; 出去之后,一定要把公版BSP給改的簡單易懂,要不然OEM廠家又要罵了 ??? add???? r10, r10, #(0x7000 / 4) ; (r10) = ptr to 1st PTE for (0x70000000>>16)/sizeof(DWORD) ? ??? ; 下面的#0x1E和#0x400最終組合成一個標記x40e,類似于前面的x402和x40e。 ??? add???? r0, r0, #0x1E?????? ; 1MB cachable bufferable ??? orr???? r0, r0, #0x400????? ; set kernel r/w permission ??? mov???? r1, #0 ??? mov???? r3, #64????????? ; 128MB SDRAM ??? ;mov ????r3, #128??????? ; 128MB SDRAM ??? ; 下面的r2表示當前的映射在PTS表格中的偏移 ??? ; 第三行代碼純屬三星的人發賤,正確易懂的寫法是add???? r2, r10, r2, LSL2 ??? ; 干脆用C語言寫更加易懂一些,就是r2 = r2*4 + r10,這里的左移Bit主要原因還是PTS中的每個元素是個字節 45??? mov???? r2, r1????????? ; (r2) = virtual address to map Bank at ??? cmp ????r2, #0x20000000:SHR:BANK_SHIFT ??? add???? r2, r10, r2, LSL #BANK_SHIFT-18 ??? strlo??? r0, [r2] ??? add???? r0, r0, #0x00100000???? ; (r0) = PTE for next physical page ??? subs???? r3, r3, #1 ??? add???? r1, r1, #1 ??? bgt???? %b45 ? ? ??? ; 兄弟們肯定在想,我考你在這里搞了大半天,修改的都是PTS,那MMU咋能知道呢? ??? ; 呵呵,不要急,到了,下面的p15, 0, r10, c2, c0, 0不是把PTS的地址給MMU了么,哈哈,大功告成 ??? ; 就剩下啟動MMU了 ??? ldr???? r10, =PTs?????????? ; (r10) = restore address of 1st level page table ? ??? ; The page tables and exception vectors are setup. ??? ; Initialize the MMU and turn it on. ??? mov???? r1, #1 ??? mcr???? p15, 0, r1, c3, c0, 0?? ; setup access to domain 0 ??? mcr???? p15, 0, r10, c2, c0, 0 ? ??? mcr???? p15, 0, r0, c8, c7, 0?? ; flush I+D TLBs ??? ;??? mrc???? p15,0,r1,c1,c0,0 ??? ??? orr???? r1, r1, #0x0071???????? ; Enable: MMU ??? orr???? r1, r1, #0x0004???? ; Enable the cache ? ? ??? ldr???? r0, =VirtualStart ? ??? cmp???? r0, #0????????? ; make sure no stall on "mov pc,r0" below ??? ; OK,終于把MMU給enable了,可以用了,哈哈,爽 ??? mcr???? p15, 0, r1, c1, c0, 0 ? ??? mov???? pc, r0????????? ;? & jump to new virtual address ??? nop ? ? ??? ; MMU & caches now enabled. ??? ;?? (r10) = physcial address of 1st level page table ??? ; ;------------------------------------------------------------------ ? VirtualStart ? ??????? mrs???? r0, cpsr ? ??????? ; 下面這段是堆棧的配置,如果你發現EBoot下面的變量和數組比較多的話,一定要調整下面 ??????? ; 如Samsung的whimory.eboot就需要相當大的Stack空間,小的話就會出莫名其妙的問題 ??????? ; 哦,對了,差點忘了,Stack是從上朝下增長的,而Ram是從從下朝上增長的,不要越界了 ??????? bic???? r0, r0, #Mode_MASK ??????? orr???? r1, r0, #Mode_IRQ | NOINT ??????? msr???? cpsr_cxsf, r1?????????????? ; IRQMode ?????? ?mov???? sp, #0x80000000 ??????? add???? sp, sp, #0x3d000??????? ; ? ??????? bic???? r0, r0, #Mode_MASK | NOINT ??????? orr???? r1, r0, #Mode_SVC ??????? msr???? cpsr_cxsf, r1?????????????? ; SVCMode?????? ??????? mov???? sp, #0x80000000 ??????? add???? sp, sp, #0x40000??????? ; ??????? b?????? main ? ??????? ENTRY_END ? ??????? LTORG | 
2.最終生成的PTS表格
上面的代碼太抽象了,我把PTS表格Dump出來之后用表格列寫了以下,如下:
其中,第四列表示虛擬地址,第三列表示虛擬地址對應物理地址,第一列表示PTS表格中的位置偏移,而第二列為PTS表格中存放的數據。每1MB的虛擬地址在下面的表格中都對應一行。
| address | value | physical address | VIRTUAL ADD | CHIP | SPACE | 
| 0X70012000 | 0X7000040E | 0X70000000 | 0x80000000 | DDR | Cached Space | 
| 0X70012004 | 0X7010040E | 0X70100000 | 
 | ||
| 0X70012008 | 0X7020040E | 0X70200000 | 
 | ||
| 0X7001200C | 0X7030040E | 0X70300000 | 
 | ||
| … | … | … | 
 | ||
| 0X700121FC | 0X77F0040E | 0X77F00000 | 
 | ||
| 0x700124C8 | 0xD010040E | 0xD0101000 | 0x93200000 | UART0 | |
| 0X70012800 | 0x70000402 | 0X70000000 | 0xA0000000 | DDR | UNCACHED SPACE | 
| 0X70012804 | 0x70100402 | 0X70100000 | 0xA0100000 | ||
| … | … | … | 
 | ||
| 0x70012CC8 | 0xD0100402 | 0xD0101000 | 0xA3200000 | UART0 | |
| 0X70010000 | 0X7000040E | 0X70000000 | 0X00000000 | DDR | ?映射0地址到物理內存的開始位置,這里只映射1MB的空間,屬性為Cache | 
| 0X70010800 | 0xD0100402 | 0X70000000 | 0X20000000 | DDR | 映射0x20000000地址到物理內存的開始位置,這里也是僅僅映射1MB的空間,屬性為UnCache | 
| 
 | 
 | 
 | 
 | 
 | 
 | 
| 0X70011C00 | 0X7000041E | 0X70000000 | X70000000 | 
 DDR 
 
 
 | 映射地址0X70000000到物理內存開始的位置 | 
| 0X70011C04 | 0X7010041E | 0X70100000 | 0X70100000 | 
 | |
| 0X70011C08 | 0X7020041E | 0X70200000 | 0X70200000 | 
 | |
| … | … | … | … | 
 | |
| 0X70011DFC | 0X77F0041E | 0X77F00000 | 0X77F00000 | 
 | 
最終賦值給MMU的值如下:
| Value | MMU.SFR | About | 
| 1 | c3&c0 | Open MMU | 
| PTs(0x70010000) | c2&c0 | set up access to domain 0 | 
| 7800041e | c8&c7 | flush I+D TLBs | 
| 5007d | c0&c1 | Enable: MMU and cache | 
3.物理地址和虛擬地址映射關系圖形化顯示
感覺上面的物理地址和虛擬地址的映射不夠形象,我把他們的映射關系用下面的圖形表示。
1>? 0x0000 0000~0x000f ffff和0x7000 0000~0x700f ffff的映射如下
2>? 0x2000 0000~0x200f ffff和0x7000 0000~0x700f ffff的映射如下
3>? 0x8000 0000~0x83ff ffff與0x7000 0000~0x73ff ffff的映射如下
?
4>? 0xa000 0000~0xa3ff ffff與0x7000 0000~0x73ff ffff的映射如下
5>? UART寄存器的映射如下
?
4.附g_oalAddressTable表格
| ; Export Definition ? ??????? EXPORT? g_oalAddressTable[DATA] ? ;------------------------------------------------------------------------------ ; ; TABLE FORMAT ;?????? cached address, physical address, size ;------------------------------------------------------------------------------ ? g_oalAddressTable ? ????? DCD???? 0x80000000, 0x70000000, 64???? ; 512 MB DRAM BANK ???????? DCD???? 0x93200000, 0xD0101000,? 1????? ; uart0 slv register ?????? DCD???? 0x00000000, 0x00000000,? 0????? ; end of table ;------------------------------------------------------------------------------ ??????? END | 
?
累死我了,終于寫完了。
如果有沒寫清楚的地方,歡迎發郵件到guopeixin@126.com或者在此留言。
總結
 
                            
                        - 上一篇: 【Python数据】懒人修仙传数值
- 下一篇: IClass与电源管理
