Chapter 3:增強 MBR === :::info 這是讀書筆記 ![679544](https://hackmd.io/_uploads/HJB-ZFKV-l.jpg =30%x) 作者:鄭鋼 出版社:佳魁資訊股份有限公司 出版日期:2017/05/31 ::: >[time=2025-04-16] --- :::warning 這邊討論都是 x86 架構下的組合語言,組譯工具是 ``nasm``, 如果是 ARM 架構下的組合語言,組譯工具是 ``armasm``。 ::: # 位址、section、vstart * 編譯器的主要工作就是給各種符號編址。 * CPU 無法判斷是指令還是一般資料。 * 組合語言裡面的 section 僅是在邏輯上方便程式開發人員整理程式用。 * vstart 是虛擬的起始位址,loader 應該要根據其位址載入到記憶體相對應位址。 # CPU 的真實模式 ![截圖 2025-04-12 晚上8.56.33](https://hackmd.io/_uploads/HJHXXyO0Jl.png) >參考資料:[CPU Registers x86](https://wiki.osdev.org/CPU_Registers_x86) * CPU 分成三個部分:控制單元、運算單元、儲存單元。 * CPU 定址模式: * 暫存器定址:存取暫存器 * 立即數定址:常數 * 記憶體定址:存取記憶體。 * 真實模式下的 ret/retf。 * 真實模式下的 call。 * Near call * Far call * 真實模式下的 jmp。 * jmp short * jmp near * jmp far * 標示暫存器 flags。 ![screen-shot-2012-07-26-at-10-02-44-pm](https://hackmd.io/_uploads/HJWAmk_AJx.png) * Conditional Jumps ![Conditional+jump+instructions.](https://hackmd.io/_uploads/BkpVUJd01g.jpg) # 顯示器 ## x86 I/O 通訊 * Intel x86 的 I/O 是使用 PMI/O。 >[!Note] ARM 通常使用 MMI/O。 ## 顯示卡、顯示記憶體、顯示器 ![IMG_0524](https://hackmd.io/_uploads/S1zMZeORJg.jpg) ## 改寫 MBR,直接操作顯示卡 ### Source Code ```asm= ;主引導程序 ; ;LOADER_BASE_ADDR equ 0xA000 ;LOADER_START_SECTOR equ 0x2 ;------------------------------------------------------------ SECTION MBR vstart=0x7c00 mov ax,cs mov ds,ax mov es,ax mov ss,ax mov fs,ax mov sp,0x7c00 mov ax,0xb800 mov gs,ax ; 清屏 ;利用0x06號功能,上卷全部行,則可清屏。 ; ----------------------------------------------------------- ;INT 0x10 功能號:0x06 功能描述:上卷窗口 ;------------------------------------------------------ ;輸入: ;AH 功能號= 0x06 ;AL = 上卷的行數(如果為0,表示全部) ;BH = 上卷行屬性 ;(CL,CH) = 窗口左上角的(X,Y)位置 ;(DL,DH) = 窗口右下角的(X,Y)位置 ;無返回值: mov ax, 0600h mov bx, 0700h mov cx, 0 ; 左上角: (0, 0) mov dx, 184fh ; 右下角: (80,25), ; 因為VGA文本模式中,一行只能容納80個字符,共25行。 ; 下標從0開始,所以0x18=24,0x4f=79 int 10h ; int 10h ; 輸出背景色綠色,前景色紅色,並且跳動的字符串"1 MBR" mov byte [gs:0x00],'1' mov byte [gs:0x01],0xA4 ; A表示綠色背景閃爍,4表示前景色為紅色 mov byte [gs:0x02],' ' mov byte [gs:0x03],0xA4 mov byte [gs:0x04],'M' mov byte [gs:0x05],0xA4 mov byte [gs:0x06],'B' mov byte [gs:0x07],0xA4 mov byte [gs:0x08],'R' mov byte [gs:0x09],0xA4 jmp $ ; 通過死循環使程序懸停在此 times 510-($-$$) db 0 db 0x55,0xaa ``` ### Compile ``` nasm -o mbr.bin mbr.S ``` ### Hard Disk Image ``` dd if=../code/mbr.bin of=./sr_hd60m.img bs=512 count=1 conv=notrunc ``` ### Result ![截圖 2025-04-12 晚上11.09.32](https://hackmd.io/_uploads/Byg8GZ_Ayl.png) # bochs 偵錯方法 ``` ./bochs -debugger ``` 查看 help 資訊 ![截圖 2025-04-12 晚上11.48.55](https://hackmd.io/_uploads/BkkaoW_0ye.png) 查看 BIOS 的入口點:0xFFFF0 ![截圖 2025-04-12 晚上11.57.51](https://hackmd.io/_uploads/rJilRbdCJx.png) 反組譯機器碼 ![截圖 2025-04-13 凌晨12.01.19](https://hackmd.io/_uploads/Sks_0bu0Jl.png) 查看 MBR 的入口點:0x7c00 >[!Important] 一開始是沒有值,BIOS 負責把 MBR 從硬碟載到 0x7c00 順便可以看看 BIOS 吐出來的資訊,而游標的位置就是上一章的範例,MBR 印出字元的位置。 ![截圖 2025-04-13 凌晨12.42.10](https://hackmd.io/_uploads/rJROOf_01l.png) # 硬碟介紹 >[!Tip]一些歷史和原理介紹我就不贅述了。 ## 如何操控硬碟 >參考資料:[Bochs' map of I/O ports to functions](https://bochs.sourceforge.io/techspec/PORTS.LST) 0x1F7 / 0x177 Status Register for read. ![截圖 2025-04-13 凌晨4.17.31](https://hackmd.io/_uploads/S1eicSOAye.png) ## 資料傳送方式 * CPU 直接取資料:registers, memory. * ==Polling== * ==Interrupt== * DMA (硬體需支援) * I/O處理器 (硬體需支援) # MBR 載入 Loader ## 載入硬碟資料 ### Source Code >boot.inc ```asm= ;------------- loader和kernel ---------- LOADER_BASE_ADDR equ 0x900 LOADER_START_SECTOR equ 0x2 ``` >mbr.S ```asm= ;主引導程序 ;------------------------------------------------------------ %include "boot.inc" SECTION MBR vstart=0x7c00 mov ax,cs mov ds,ax mov es,ax mov ss,ax mov fs,ax mov sp,0x7c00 mov ax,0xb800 mov gs,ax ; 清屏 ;利用0x06號功能,上卷全部行,則可清屏。 ; ----------------------------------------------------------- ;INT 0x10 功能號:0x06 功能描述:上卷窗口 ;------------------------------------------------------ ;輸入: ;AH 功能號= 0x06 ;AL = 上卷的行數(如果為0,表示全部) ;BH = 上卷行屬性 ;(CL,CH) = 窗口左上角的(X,Y)位置 ;(DL,DH) = 窗口右下角的(X,Y)位置 ;無返回值: mov ax, 0600h mov bx, 0700h mov cx, 0 ; 左上角: (0, 0) mov dx, 184fh ; 右下角: (80,25), ; 因為VGA文本模式中,一行只能容納80個字符,共25行。 ; 下標從0開始,所以0x18=24,0x4f=79 int 10h ; int 10h ; 輸出字符串:MBR mov byte [gs:0x00],'1' mov byte [gs:0x01],0xA4 mov byte [gs:0x02],' ' mov byte [gs:0x03],0xA4 mov byte [gs:0x04],'M' mov byte [gs:0x05],0xA4 ;A表示綠色背景閃爍,4表示前景色為紅色 mov byte [gs:0x06],'B' mov byte [gs:0x07],0xA4 mov byte [gs:0x08],'R' mov byte [gs:0x09],0xA4 mov eax,LOADER_START_SECTOR ; 起始扇區lba地址 mov bx,LOADER_BASE_ADDR ; 寫入的地址 mov cx,1 ; 待讀入的扇區數 call rd_disk_m_16 ; 以下讀取程序的起始部分(一個扇區) jmp LOADER_BASE_ADDR ;------------------------------------------------------------------------------- ;功能:讀取硬盤n個扇區 rd_disk_m_16: ;------------------------------------------------------------------------------- ; eax=LBA扇區號 ; ebx=將數據寫入的內存地址 ; ecx=讀入的扇區數 mov esi,eax ;備份eax mov di,cx ;備份cx ;讀寫硬盤: ;第1步:設置要讀取的扇區數 mov dx,0x1f2 mov al,cl out dx,al ;讀取的扇區數 mov eax,esi ;恢覆ax ;第2步:將LBA地址存入0x1f3 ~ 0x1f6 ;LBA地址7~0位寫入端口0x1f3 mov dx,0x1f3 out dx,al ;LBA地址15~8位寫入端口0x1f4 mov cl,8 shr eax,cl mov dx,0x1f4 out dx,al ;LBA地址23~16位寫入端口0x1f5 shr eax,cl mov dx,0x1f5 out dx,al shr eax,cl and al,0x0f ;lba第24~27位 or al,0xe0 ; 設置7~4位為1110,表示lba模式 mov dx,0x1f6 out dx,al ;第3步:向0x1f7端口寫入讀命令,0x20 mov dx,0x1f7 mov al,0x20 out dx,al ;第4步:檢測硬盤狀態 .not_ready: ;同一端口,寫時表示寫入命令字,讀時表示讀入硬盤狀態 nop in al,dx and al,0x88 ;第4位為1表示硬盤控制器已準備好數據傳輸,第7位為1表示硬盤忙 cmp al,0x08 jnz .not_ready ;若未準備好,繼續等。 ;第5步:從0x1f0端口讀數據 mov ax, di mov dx, 256 mul dx mov cx, ax ; di為要讀取的扇區數,一個扇區有512字節,每次讀入一個字, ; 共需di*512/2次,所以di*256 mov dx, 0x1f0 .go_on_read: in ax,dx mov [bx],ax add bx,2 loop .go_on_read ret times 510-($-$$) db 0 db 0x55,0xaa ``` ### Compile ```shell nasm -I inc/ -o mbr.bin mbr.S ``` ### Hard Disk Image ``` dd if=../code/mbr.bin of=./sr_hd60m.img bs=512 count=1 conv=notrunc ``` ## 準備一個簡單的 Loader ### Source Code >loader.S ```asm= %include "boot.inc" section loader vstart=LOADER_BASE_ADDR ; 輸出背景色綠色,前景色紅色,並且跳動的字符串"1 MBR" mov byte [gs:0x20],'2' mov byte [gs:0x21],0xA4 ; A表示綠色背景閃爍,4表示前景色為紅色 mov byte [gs:0x22],' ' mov byte [gs:0x23],0xA4 mov byte [gs:0x24],'L' mov byte [gs:0x25],0xA4 mov byte [gs:0x26],'O' mov byte [gs:0x27],0xA4 mov byte [gs:0x28],'A' mov byte [gs:0x29],0xA4 mov byte [gs:0x2a],'D' mov byte [gs:0x2b],0xA4 mov byte [gs:0x2c],'E' mov byte [gs:0x2d],0xA4 mov byte [gs:0x2e],'R' mov byte [gs:0x2f],0xA4 jmp $ ; 通過死循環使程序懸停在此 ``` ### Compile ```shell nasm -I inc/ -o loader.bin loader.S ``` ### Hard Disk Image ```shell dd if=../code/loader.bin of=./sr_hd60m.img bs=512 count=1 seek=2 conv=notrunc ``` ### Result ![截圖 2025-04-13 晚上11.32.55](https://hackmd.io/_uploads/rkT8FUF0kx.png)