《操作系统真相还原》二

前言

上次说到在屏幕上显示"1 MBR"。我们从BIOS进入MBR,在mbr里面调用BIOS中断,在屏幕上显示"1 MBR"。上次就到这里了。但是mbr的作用显然不止只要这些。接下来我们就是来完善MBR。

实模式地址分配

起始地址 结束地址 大小 用途
FFFF0 FFFFF 16B BIOS入口地址
F0000 FFFFF 64KB BIOS代码,包括上面介绍的BIOS入口地址
C8000 EFFFF 160KB 映射硬件适配器的ROM或者内存映射式I/O
C0000 C7FFFF 32KB 显示适配器BIOS
B8000 BFFFF 32KB 用于文本模式显示适配器
B0000 B7FFF 32KB 用于黑白显示适配器
A0000 AFFFF 64KB 用于彩色显示适配器
9FC00 9FFFF 1KB EBDA(Extended BIOS Data Area)拓展BIOS数据区
07E00 9FBFF 622080B 可用区域
07C00 07DFF 512B MBR被BIOS加载到此处
00500 07BFF 30464B 可用区域
00400 004FF 256B BIOS Data Area(BIOS数据区)
00000 003FF 1KB Interrupt Vector Table(中断向量表)

完善MBR

首先我们要知道MBR的作用是啥?MBR,又名Master Boot Record,即主引导记录,也被称为主引导扇区,主要记录着硬盘本身的相关信息以及硬盘各个分区的大小及位置信息。

书上个mbr介绍了很多,先调用BIOS的子功能显示字符。后面又从更底层,向显存写入数据,然后再屏幕显示字符。具体可以看开头的实模式地址分配。就是向地址B8000写入数据。从而让字符显示出来

;主引导程序 
;
;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
​

代码先放到这里。

进入loader

到后面我们就进入loader了。进入loader需要mbr帮助,为啥要从mbr到loader呢?因为放到硬盘的0扇区,mbr只有512字节的空间,位置太小,啥事一个干不了。所以mbr需要从硬盘里面读取数据,加载到内存。然后mbr通过jmp指令跳转到刚刚加载的loader程序里面。先把代码贴上

;主引导程序 
;------------------------------------------------------------
%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
​

这里的写了rd_disk_m_16这个函数,里面接收三个参数,起始扇区lba地址,写入的地址,待读入的扇区数。就是从第二个扇区读取一个硬盘,也就是512个字节给放到LOADER_BASE_ADDR这个地址上去。LOADER_START_SECTOR和LOADER_BASE_ADDR都是boot.inc里面的。

代码如下

LOADER_BASE_ADDR equ 0x900 
LOADER_START_SECTOR equ 0x2

接下来就是从mbr进入loader了。进入loader就可以做很多事情了。

书上是先写了个显示字符的例子,看看是不是真的跳到loader里面去了。并且loader还正确运行了。

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
​
; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
mov byte [gs:0x00],'2'
mov byte [gs:0x01],0xA4     ; A表示绿色背景闪烁,4表示前景色为红色
​
mov byte [gs:0x02],' '
mov byte [gs:0x03],0xA4
​
mov byte [gs:0x04],'L'
mov byte [gs:0x05],0xA4   
​
mov byte [gs:0x06],'O'
mov byte [gs:0x07],0xA4
​
mov byte [gs:0x08],'A'
mov byte [gs:0x09],0xA4
​
mov byte [gs:0x0a],'D'
mov byte [gs:0x0b],0xA4
​
mov byte [gs:0x0c],'E'
mov byte [gs:0x0d],0xA4
​
mov byte [gs:0x0e],'R'
mov byte [gs:0x0f],0xA4
​
jmp $              ; 通过死循环使程序悬停在此

之后,我们再将loader.s和mbr.s写入img。运行看看效果。

dd if=mbr.bin of=hd60M.img bs=512 count=1 seek=2 conv=notrunc

写入完成后,启动虚拟机看看效果。

你可能感兴趣的:(linux,java,运维)