我們這次來根據dump動手來實際轉化一個虛擬地址到物理地址,此次的地址不是線性地址映射。
Target_Address_|________________logical|_physical______________|
| C:FFFFFF8008015000| A:C549F000
上面0xFFFFFF8008015000就是虛擬地址,而我們努力的方向就是物理地址:0xC549F000。 讓我們朝這個方向一路高歌吧。
轉化的原理:
- 根據TTBR(0/1)寄存器獲取到頁表的基地址
- 頁表基地址+PGD_Index獲取PGD表中的一項,此項就是PMD表的基地址
- 根據PDM基地址+PMD_Index獲取PMD中的一項,此項就是PTE表的基地址
- 根據PTE基地址+PTE_Index獲取直接頁表的基地址
- 根據直接頁表的地址+offset就可以獲取真正的物理地址
轉化之前先確認的是此地址是屬於用戶空間還是內核空間。
- 用戶地址:頁表的基地址是mm_struct→pgd
- 內核空間: 頁表的基地址是init_mm→pgd
很明顯我們需要轉化的地址是屬於內核空間的,則首先需要確認init_mm→pgd的值,它來了
init_mm = (
mmap = 0x0,
mm_rb = (rb_node = 0x0),
vmacache_seqnum = 0x0,
mm_rb_lock = (raw_lock = (cnts = (counter = 0x0), wlocked = 0x0, __lstate = (0x0, 0x0, 0x0), wai
get_unmapped_area = 0x0,
mmap_base = 0x0,
mmap_legacy_base = 0x0,
task_size = 0x0,
highest_vm_end = 0x0,
pgd_=_0xFFFFFF9B4FCA7000 -> ( //虛擬地址
pgd = 0x000000017AC05003), //虛擬地址裏面的值
mm_users = (counter = 0x2),
mm_count = (counter = 0x1),
前期條件:
- 虛擬地址:0xFFFFFF8008015000
- init_mm→pgd的值:0xFFFFFF9B4FCA7000
PGD_Index = 虛擬地址>>30位 = (0xFFFFFF8008015000 >>30)&(0x200-1) = 0
PGD_entry_virt = 0xFFFFFF9B4FCA7000 + 0*8 = 0xFFFFFF9B4FCA7000
PGD_entry_phy = rd(0xFFFFFF9B4FCA7000) = 0x000000017AC05003
PMD_Index = 虛擬地址 >> 21位 = (0xFFFFFF8008015000 >>21)&(0x1ff) = 0x40
PMD_entry_virt = 0x000000017AC05003 + 0x40 * 8 = 0x000000017AC05000 + 0x40 *8 = 0x17AC05200
PMD_entry_phy = rd(0x17AC05200) = 0x17AC06003
PTE_Index = 虛擬地址 >> 12位 = (0xFFFFFF8008015000>>12)&(0x200-1)=0x15
PTE_entry_virt = 0x17AC06003 + 0x15 * 8 = 0x17AC06000 + 0x15*8= 0x17AC060A8
PTE_entry_phy = rd(0x17AC060A8) = 0xE00000C549F793
pfn = 0xE00000C549F793 >> 12 = 0xC549F
物理地址 = 0xC549F000 + 0x000 = 0xC549F000
上面之所以需要清空低12位,還有index*8都是因爲頁表的低12位存儲着一些flag,這些flag如下,比如T32
- present的意思是頁是否是有效的,無效代表虛擬到物理地址之間的轉化無效,當訪問虛擬地址的時候就會page fault
- protection :權限之類的,是否讀寫執行權限之類的。如果你訪問一段虛擬地址,頁表中是無法執行的權限,但是你想執行這段代碼就會出錯
- reference: 引用之類的
- cache: cache是否有效。有效就從cache取,無效從memory
- dirty: 是否寫過,寫過就代表髒。在內存不夠時需要寫會處理的。