2014-12-21 11 views
1

我正在開發玩具內核以獲得樂趣和教育(不是類項目)。我開始在內存管理器上工作,所以我試圖在仍處於實模式時使用INT 0x15, EAX=E820調用從BIOS獲取內存映射。我正在從osdev wiki(here,在「獲取E820存儲器映射」一節)中調整我的功能。然而,我希望這是一個我可以從我的C代碼調用的函數,所以我試圖改變它一點。我希望它有兩個參數:一個指向存儲映射條目的位置的指針,以及一個指向整數的指針,該整數將根據表中的條目數增加。內核開發:設置ES:實模式中的DI

根據wiki,ES:DI需要指向數據存儲位置,因此我將第一個參數分爲兩部分(段選擇器pointer_to_map/16和偏移量pointer_to_map % 16)。這裏的C代碼部分:

typedef struct SMAP_entry { 
    unsigned int baseL; // Base address, a QWORD 
    unsigned int baseH; 
    unsigned int lengthL; // Length, a QWORD 
    unsigned int lengthH; 
    unsigned int type; // entry type 
    unsigned int ACPI; // extra data from ACPI 3.0 
} SMAP_entry_t; 

SMAP_entry_t data[100]; 
kprint("Pointer: "); 
kprint_int((int) data, 16); 
kprint_newline(); 

int res = 0; 
read_mem_map(((int) data)/16, ((int) data) % 16, &res); 
kprint("res: "); 
kprint_int(res, 16); 
kprint_newline(); 

這裏是我的ASM代碼的一部分:

; performs a INT 0x15, eax=0xE820 call to find the memory map 
; inputs: the pointer to the data table/16, the pointer % 16, a pointer to an dword (int) which will be 
;  incremented by the number of entries after this function returns. 
; preserves: no registers except esi 
read_mem_map: 
    mov es, [esp + 4]   ; set es to the value of the first argument 
    mov di, [esp + 8]   ; set di to the value of the second argument 

這就是我在粘貼因爲程序三重故障並關閉VM那裏。通過移動ret命令,我發現該函數在第一行崩潰。如果我用C註釋掉這個呼叫,那麼一切都按照你的預期工作。

我已經通過Google閱讀,幾乎沒有任何理由直接設置ES:DI,並且在我找到的代碼中,他們將其設置爲文字。我應該如何設置ES:DI,如果不能直接設置,我應該如何使C和ASM以正確的方式進行交互?

+1

呃? ES:DI是幾個彙編指令的目標地址。爲什麼設置ES或DI會出現問題? –

+0

我不確定,但虛擬機崩潰在這些MOV命令,所以我認爲問題在那裏。我對彙編代碼很陌生,所以我也可能犯了一個愚蠢的錯誤。我的猜測是,它的大小與我試圖設置的數據大小有關,但將演員製作成簡短形式並沒有做任何事情,只是產生了一些編譯器警告。可以將它們放在一起嗎? – zrneely

+0

很難判斷你卡在哪裏。最好的資源之一是[** AOA - 目錄**](https://courses.engr.illinois.edu/ece390/books/artofasm/artofasm.html)。特別看第4章。至於使用'segment:[register]'尋址的具體示例,請參閱[** AOA - 15.3.2 - 索引示例**](https://courses.engr.illinois.edu/ece390/books /artofasm/CH15/CH15-4.html#HEADING4-153)如果你在一個64位的盒子上,函數參數不會在棧上傳遞,它們在'RDI,RSI,RDX,RCX,R8和R9 ' –

回答

2

每個段寄存器(在80x86上)都有一個可見部分和幾個隱藏字段(段基址,段限制和段的屬性 - 讀/寫,特權級別等)。

在保護模式下;當加載段寄存器時,CPU使用可見部分作爲GDT或LDT的索引,並從該描述符(在GDT或LDT中)加載段的隱藏字段。

在實模式下; CPU做了一些完全不同的事情 - 它只將段基設置爲「可見部分* 16」,並且不使用任何(GDT,LDT)表。

鑑於您正在使用32位指針指向數據表和32位堆棧指針(例如mov es, [esp + 4]);我假設你的C代碼處於32位保護模式。這與真實模式完全不兼容,部分原因是段加載的工作完全不同,部分原因是默認操作數/地址大小是32位而不是16位。

所有的BIOS功能都是針對實模式而設計的。它們不能用於保護模式。

基本上;我建議:

  • 的指針數據表傳遞到你的組件作爲一個32位整數/指針(而不是2個獨立的16位整數)
  • 所謂的「去實模式」函數(這會稍微有點棘手,因爲您也將從32位堆棧切換到16位堆棧,並且需要16位代碼中的「32位返回指令」)。
  • 將指向數據表的指針拆分爲其段和偏移量,並加載段(它應該能夠正常工作,因爲您現在處於實模式下)
  • 調用BIOS函數(它應該與您一樣正常工作現在處於實模式)
  • 調用「去保護模式」功能(這將稍微有點棘手,包括32位代碼中的「16位返回指令」)。
  • 返回到(32位保護模式)呼叫者

說明用於切換從實模式到保護模式,並且從保護模式到實模式的切換,被包括在Intel的系統程序員指南。 :)

+0

謝謝,我明白你的意思是使用32位指針。但是我甚至沒有編寫代碼切換到保護模式。由於指向數據表的指針是16位,我應該只是將di指向該指針,並且保持獨立?我有其他使用esp來獲取參數的asm函數,並且它們加載了用於鍵盤中斷處理的idt(這有效),所以我不認爲這是問題所在。 – zrneely

+0

@zrneely:我很困惑。要麼你處於保護模式,確實有IDT和32位堆棧指針(ESP)是有意義的;或者你處於真實模式,擁有BIOS自己的IVT(和BIOS鍵盤IRQ處理程序)並且沒有IDT,16位堆棧指針沒有任何意義,並且你發現了一個晦澀難懂的編譯器,它實際上仍然能夠生成真實模式代碼。我的猜測是你正在使用類似GRUB/multiboot的東西(即使你沒有意識到,即使你沒有自己切換到保護模式也處於保護模式)。 – Brendan