簡單地說,Bochs是一款仿真軟件,能夠用軟件的方式模擬硬件的工做。同類軟件有Qemu,仿真軟件與虛擬機(hypervisor)還不徹底相同,仿真軟件是徹底軟件模擬硬件,而虛擬機軟件(好比Vmware, VirtualBox)是利用主機的硬件進行工做。html
Bochs的主頁地址:http://bochs.sourceforge.net/數組
Bochs軟件的下載地址:http://sourceforge.net/projects/bochs/files/bochs/bash
Bochs的使用依賴配置文件,經過配置文件指定不一樣的硬件,以及指定存儲介質的映像文件(BIOS的ROM文件、磁盤文件等)。數據結構
在Windows環境下安裝過Bochs後,在配置文件上右鍵菜單會出現Run, Debug的菜單選項,從而啓動運行或者調試。app
Bochs的調試器命令與gdb命令十分類似,可是更增強大。簡單介紹幾條命令的使用:框架
1: #流程控制
2: c #continue, 繼續執行
3: s [count] #step, 單步執行count次
4: #斷點
5: vb seg:off #設置邏輯地址斷點
6: lb addr #設置物理地址斷點
7: info break #查看斷點
8: d n #刪除斷點
9: #查看內存
10: x/n[bhwg][xduotc] #查看內存
11: [bhwg] #顯示單元大小,分別表明byte, half, word, giant word
12: [xduotc] #顯示格式,分別表明hex, dec, unsigned, octal, binary, char
13: #查看寄存器
14: r #查看基本寄存器
15: sreg #查看段寄存器
更多的命令,請輸入help查看。函數
首先說一下編譯源碼的動機,當咱們安裝了Bochs以後就已經可使用它來運行或者調試一個被仿真的系統了。 這種調試相似於gdb,調試目標是運行在Bochs之上的系統。oop
然而,咱們知道,既然Bochs是一個開源的項目,以經過軟件的方式仿真了硬件系統,那麼咱們就能夠經過查看Bochs的源碼來學習相關的硬件知識(好比Intel體系結構,BIOS,DMA等)了。學習
從上面的下載地址下載一份源代碼,解壓後,能看到vs2008/bochs.sln文件,從而打開Visual Studio項目進行編譯。this
默認配置選項中沒有包含對bochsdbg的支持,所以咱們須要從新運行configure程序,悲劇的是configure是Linux下面的程序,咱們能夠經過如下方式來達到一樣的目的:
1: --enable-debugger --enable-disasm
1: bash.exe .conf.win32_vcpp
完成以上步驟以後,就能夠編譯出具備debug功能的Bochs可執行程序了。
咱們能夠在位置上設置斷點:
1: void bx_dbg_user_input_loop(void) /*dbg_main.cc*/
而後在調試窗口中輸入命令
1: r
程序會在這兩個斷點處中斷,這個bx_dbg_user_input_loop函數就是不斷接收調試命令的循環體,它會把接收到的調試命令通過lex&yacc框架進行解析,而後調用到相應的handler來處理調試請求。
這些handler都在debug.h文件中進行聲明,好比處理r命令的handler定義爲
1: void bx_dbg_info_registers_command(int);
在該函數的定義處設置斷點,咱們就可以瞭解到Bochs是怎樣處理r這樣的調試請求的。
經過跟蹤幾個調試命令的實現,咱們發現了三個重要的全局變量:
1: BOCHSAPI BX_CPU_C bx_cpu;
2: BOCHSAPI BX_MEM_C bx_mem;
3: bx_devices_c bx_devices;
分別保存着用來描述CPU、內存和外部設備的數據結構。
因爲咱們但願經過Bochs來學習硬件相關的內容,因此會對IN和OUT這兩條指令很感興趣,由於CPU就是經過這兩條指令與外部設備之間進行協調工做的。
咱們經過嘗試,找到了下面這個函數
1: /*
2: * Write a byte of data to the IO memory address space.
3: */
4:
5: void BX_CPP_AttrRegparmN(3)
6: bx_devices_c::outp(Bit16u addr, Bit32u value, unsigned io_len)
bx_devices會在內部維護一個外設端口對應的讀和寫的handler的數組
1: struct io_handler_struct **read_port_to_handler;
2: struct io_handler_struct **write_port_to_handler;
這是兩個二維指針數組,用端口號做爲下標能夠找到某個端口對應的讀寫處理函數,默認會把每一個handler都設置成io_write_handlers
1: /* set handlers to the default one */
2: for (i=0; i < PORTS; i++) {
3: read_port_to_handler[i] = &io_read_handlers;
4: write_port_to_handler[i] = &io_write_handlers;
5: }
經過查找函數
1: #define DEV_register_ioread_handler(b,c,d,e,f) bx_devices.register_io_read_handler(b,c,d,e,f)
2: #define DEV_register_iowrite_handler(b,c,d,e,f) bx_devices.register_io_write_handler(b,c,d,e,f)
咱們能夠找到哪些設備支持了本身的IO讀寫功能,以及其對應的handler。
以DMA爲例,咱們能夠找到以下的註冊handler代碼
1: // 0000..000F
2: for (i=0x0000; i<=0x000F; i++) {
3: DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
4: DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
5: }
6:
7: // 00080..008F
8: for (i=0x0080; i<=0x008F; i++) {
9: DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
10: DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
11: }
12:
13: // 000C0..00DE
14: for (i=0x00C0; i<=0x00DE; i+=2) {
15: DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
16: DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
17: }
只要在DMA模塊的read_handler和write_handler處理設置斷點,咱們就能夠動態地調試DMA的處理邏輯了。
通過了以上的準備工做以後,咱們就能夠開始調試一個具體的系統了。我是以DLX爲目標進行調試的,在調試過程當中,咱們能夠一步一步地瞭解到從計算機加電後執行BIOS開機自檢程序,到加載MBR,經過LILO一步一步地把Linux操做系統啓動起來的全過程,一個奇妙的旅程即將開始!
有人已經這樣作了,而且根據Bochs的代碼,出了一本書:http://www.mouseos.com/books/x86-64/index.html