侯捷老師說的「發表是最好的回憶」, 我很贊同, 所以我要精確把努力的過程紀錄下來。
這個程式讀寫物理位址: 0x801000, 0x800000, 並使用分頁轉換將線性位址從
0x800000 轉到 0x800000
以及
0x800000 轉到 0x801000
x86 page table test (0) 只做一對一轉換, 純粹練習 paging 用的, 實際上沒享受到分頁的好處。
由於會使用 call 指令, 所以還是事先設定好 stack 比較好 (沒設定好像也沒事)。
182 mov $(SelectorStack), %ax
183 mov %ax, %ss # 堆疊段選擇子
184 mov $(TopOfStack), %esp
程式先寫入 0xe8 到 0x801000,
189 mov $(SELECTOR_FREE_MEM_AREA), %ax
190 mov %ax, %es
191 movb $0xe8, %es:(0x1000)
再來設定好兩組 page table:
(PAGE_DIR_BASE/PAGE_TBL_BASE)
(PAGE_DIR_BASE_1/PAGE_TBL_BASE_1)
200 call SetupPaging /* set up paging before 32-bit code */
201 call SetupPaging_1 /* set up paging before 32-bit code */
然後使用 switch_page_dir 將 (PAGE_DIR_BASE/PAGE_TBL_BASE) 的設定寫入 cr3。
203 call switch_page_dir
程式寫入 0xf9 到位址 0x800000,
208 mov $(SELECTOR_FREE_MEM_AREA), %ax
209 mov %ax, %es
210 movb $0xf9, %es:(0)
211 movb %es:(0), %al
212 call DispAL
213
214 mov $(SelectorVideo), %ax
215 mov %ax, %gs /* Video segment selector(dest) */
216 mov $20, %edi
217 mov $(SELECTOR_FREE_MEM_AREA), %ax
218 mov %ax, %es
219 # movb $0xf9, %es:(0)
220 movb %es:(0), %al
221 call DispAL
然後再由 0x800000 複製到 al, 兩次的動作藉由 DispAL 將值印出來, 確認寫入的位址無誤。
再來使用 switch_page_dir_1 將 (PAGE_DIR_BASE_1/PAGE_TBL_BASE_1) 的設定寫入 cr3。
225 call switch_page_dir_1
程式先寫入 0xe3 到位址 0x800000 (但其物理位址已經被轉到是 0x801000)
229 mov $(SELECTOR_FREE_MEM_AREA), %ax
230 mov %ax, %es
231 movb $0xe3, %es:(0)
寫入的線性位址是一樣的, %es:(0), 但是經過 page maping 之後, 這是對應到 0x801000, 而不是 0x800000。
將原本的 page table 切回來之後,
236 call switch_page_dir
237
238 mov $(SelectorVideo), %ax
239 mov %ax, %gs /* Video segment selector(dest) */
240 mov $40, %edi
241 mov $(SELECTOR_FREE_MEM_AREA), %ax
242 mov %ax, %es
243 # movb $0xf9, %es:(0)
244 movb %es:(0), %al
245 call DispAL
0x800000 印出 F9
246
247 movb %es:(0x1000), %al
248 mov $50, %edi
249 call DispAL
0x801000 印出 E3
為什麼用 0x801000, 0x800000 這兩個位址來測試呢?這是特別設計過的, 我首先想要做的事情:
- 讓線性位址 0x800000 在分頁轉換後對應到 0x800000;
- 另一個分頁轉換則是, 讓線性位址 0x800000 在分頁轉換後對應到 0x801000;
0x800000 二進位
0000 0000 1000 0000 0000 0000 0000 0000
分成三部份:
0000 0000 10 = 2(十進位) 表示 page directory 要選第二個 entry, 這個 entry 的內容我填入第二個 page table 的位址。所以分頁轉換會去找第二個 page table。
所以修改第二個 page table:
382 # setup the 2nd page
383 mov $(SELECTOR_PAGE_TBL_1), %ax
384 mov %ax, %es
385 mov $(TEST_OFFSET|PG_P | PG_USU | PG_RWW), %eax
386 #mov %eax, %es:(0x3000)
387 mov %eax, %es:(0x2000)
再來的
00 0000 0000 就是第零個 page table item,
所以
387 mov %eax, %es:(0x2000)
就是寫入第二個 page table 的第零個 page table item (第二個 page table 的第一個 page table item 位址是:
%es:(0x2000 + 0x1000)), 讓他變成 0x00801000
.set TEST_OFFSET, 0x00801000 # const for 0x00801000
這是為了讓 0x800000 在分頁轉換後變成了 0x801000。
線性位址 0x800000 表示去查第二個 page table 的第零個 page table item 的值, 目前是
0x00801000, 而 or 上 0x800
000 的
000 變是 0x00801000 (
0x00801 | 000), 這是經過分頁轉換物理位址。
這程式會在切回 dos/real mode 時當掉,
outb %al, $0x92
死在這裡, 不知道為什麼, 有空再來查了。
想要進一步測試可以把
mov %eax, %es:(0x2000) 換成 mov %eax, %es:(0x3000)
由於寫到不正確的分頁表, 所以 0x800000 並不會轉換成 0x00801000。所以最後還是看到 E8。並沒有在第二次的分頁轉換後被改成 E3。而由於這樣是一對一轉換, 所以實際上修改的是 0x800000 的物理位址, 所以 fail 那張圖本來 F9 的地方被改成 E3。
杨文博/
于渊的版本用的是 function call 的方法, 我覺得有點複雜, 所以自己亂想出這個測試程式, 果然藉由程式的實作, 比教科書上來的有印象多了, 560 行在 dos 下執行的程式, 就可以了解什麼是分頁記憶體管理, 比硬背好多了。寫的不夠完整, 要全盤了解這段 code, 可能需要把
杨文博/
于渊的書籍 (保護模式那章) 讀一遍。
214 mov $(SelectorVideo), %ax
215 mov %ax, %gs /* Video segment selector(dest) */
216 mov $20, %edi
216 的 edi 設定是 video address, 表示要把 AL 的內容顯示在哪一行哪一列。
E8, F9, F9, F9, E3 的物理位址分別是 0x801000, 0x800000, 0x800000, 0x800000, 0x801000。
* paging ok
E8, F9, F9, E3, E8 的物理位址分別是 0x801000, 0x800000, 0x800000, 0x800000, 0x801000。
* paging fail
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。