《操作系统真象还原》第三章 完善MBR

3.1 地址、section、vstart浅尝辄止

        本质上,程序中的各种数据结构的访问,就是通过“该数据结构的起始地址+该数据结构所占内存的大小”,所谓数据的地址,就是该数据相对整个程序开头的距离,即偏移量

        section只是为了让程序员在逻辑上将程序划分为几个部分,section本身并没有被分配地址,它是伪指令

        vstart是虚拟起始地址,作用是为section内的数据制定一个虚拟的起始地址。mbr之所以用vstart=0x7c00来修饰,是因为我们人为的规定mbr要被BIOS加载到物理地址0x7c00

3.2.1 CPU的工作原理

        控制单元要取下一条待运行的指令,该指令的地址在程序计数器PC中,在x86CPU上,程序计数器就是cs:ip。读取ip寄存器后,将此地址送上地址总线,CPU根据次地址得到了指令,并将其存入到指令寄存器IR中。译码器根据指令格式检查指令寄存器中的指令,先确定操作码是什么,再检查操作数类型,运算单元开始真正执行指令。ip寄存器的值被加上当前指令的大小,于是ip又指向了下一条指令的地址,循环。

3.2.2  实模式下的寄存器

        寄存器是给CPU处理数据的场所,虽然有些寄存器对程序员不可见,但我们需要对他们进行初始化。

        CPU是使用“段基址:段内偏移地址”来访问内存的,地址被分开存储到代码段CS寄存器和指令指针IP寄存器。在不跨段的前提下,CPU会将“当前ip寄存器中的值+指令长度”作为新的ip寄存器中的值,若需要跨段,则需要加载新的段基址到CS寄存器中。

3.2.3  实模式下内存分段由来

        实模式,即程序中用到的地址都是真实的物理地址。CPU20位地址,但寄存器只有16位,所以通过“基地址:偏移地址”的形式处理,先把16为的段基址左移4位变成20位再加上段内偏移地址。

3.2.4  实模式下CPU内存寻址方式

1.寄存器寻址

        从寄存器里直接拿数据

2.立即数寻址

        即常数

3.内存寻址

        1)直接寻址

        内存地址写入中括号中  

mov ax,[0x1234]
        2)基址寻址

        在操作数中用bx寄存器或寄存器作为地址的气势,地址的变化以它为基础        

        bx寄存器的默认段寄存器是DS,而bp寄存器的默认段寄存器是SS

        3)变址寻址

        mov [si+0x1234],ax

3.2.5  什么是栈

        CPU中有栈段SS寄存器和栈指针Sp寄存器,他们是用来指定当前使用的栈的物理地址。

        栈是线性表中的一种,后进先出,也就是从上往下。在内存中指定一块区域为栈区域,其起始地址存入栈基址寄存器SS,其指针用寄存器sp来指定。栈在使用过程中是向下扩展的,所以栈顶地址小于栈底地址

        push指令,先将SP减去字长,所得新的SP,再讲数据压入。POP指令相反,先弹出数据,才讲SP指令加上字长在更新SP

 

代码3-4  mbr.S

SECTION MBR vstart=0x7c00 ;MBR程序地址0x7c00
mov ax,cs	;由于cs为0,,所以可以用cs来初始化ax
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800	;文本模式显示适配器
mov gs,ax

;利用INT 0x10 功能号:0x06来上卷全部行

mov ax,0x0600	;AH 功能号 = 0x06 AL = 行卷的行数(0表示全部)
mov bx,0x0700	;BH表示上卷行属性,属性是指背景色 
mov cx,0x0000	;cx=(CL,CH)=窗口左上角的(X,Y)
mov dx,0x184f	;右下角(80,25)VGA文本模式,一行80个字符,一共25行
                 ;下标从0开始,所以0x18=24,0x4f=79
int 0x10

                                        ; 输出背景色绿色,前景色红色,并且跳动的字符串"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

编译 

nasm -o mbr.bin mbr.S

写入虚拟机磁盘,跟上一章一样,记得更换你自己的地址

dd if=/home/moyao/Desktop/bochs/boot/mbr.bin of=/home/moyao/Desktop/bochs/hd60M.img bs=512 count=1 conv=notrunc

  执行

bin/bochs -f bochsrc.disk

                      

 

3.4  bochs调试方式

        由于bochs是虚拟机,所以支持硬件级别上的调试,其硬件级别的调试,体现在

        1、调试时可以查看页表、gdt、idt等数据结构

        2、可以查看栈中数据

        3、可以反汇编任意内存

        4、实模式、保护模式互相变换是提醒

        5、中断发生时提醒

        由于bochs已经将选项[6]作为默认的行为,进入bochs后可以直接回车

        具体看p116页介绍

3.5  硬盘介绍

        针对硬盘的IO接口是硬盘控制器,与显卡和显示器不同的是,硬盘控制器是和硬盘链接在一起的

        IO接口与端口

        IO接口:向CPU提供I/O设备的状态信息和进行命令译码。对传送数据提供缓冲,以消除计算机与外设在“定时”或数据处理速度上的差异。

        端口:是指接口电路中的一些寄存器,这些寄存器分别用来存放数据信息、控制信息和状态信息。

        

         data寄存器管理数据。在读硬盘时,硬盘准备好数据后,硬盘控制器将其放在内部的缓冲区中,不断读此寄存器便是读出缓冲区中的全部数据。在写硬盘时,我们要把数据源源不断地运送到此端口,数据便被存入缓冲区里,硬盘控制器发现这个缓冲区中有数据了,便将此处的数据写入相应的扇区中。(16位)

        读硬盘时,端口0x171或者0x1F1的寄存器名字叫Error寄存器,只有读取硬盘失败时有用,会记录失败的信息,尚未读取的扇区数在Sector count寄存器中。写硬盘时此寄存器有了别的用途,叫Feature 寄存器。有些命令需要额外参数,这些参数就写入此端口中。注意Error 和 Feature本质上是同一个端口,只不过不同环境下有不同用途。(8位)

        Sector count 寄存器用来指定待读取或者待写入的硬盘个数。8位所以最大值为255,若设置为0则说明要操作256个扇区。

        LBA(逻辑块地址),对人来说直观的磁盘寻址方式。分为LBA28和LBA48两类,前者用28为比特来描述一个扇区的地址,后者用48位。LBA28有三个寄存器,LBA low,LBA mid,LBA high,每个寄存器都是8位,这里三个寄存器只有24位,还有4位在哪呢?放到了device寄存器上

        device寄存器前四位为LBA地址的第23~27位第四位用来指定通道上的主盘或从盘,0代表主盘,1代表从盘;第六位用来设置是否启用LBA方式,1代表启用,0代表启用CHS模式即“煮面-磁头-扇区”模式;第五位和第七位固定式一,成为MBS位,无需关注。

        读硬盘时,端口0x1F7和0x177的寄存器名称是status,它是8位的寄存器,用来给出硬盘的状态信息第0位是ERR位,若为1,表示命令出错,具体原因可看Error寄存器;第3位是data request位,若为1,表示硬盘已经把数据准备好了,主机现在可以把数据读出来。第6位是DRDY,表示硬盘就绪,此位是对硬盘诊断时用的,表示硬盘检测正常,可以执行一些命令。第7位是BSY位,表示硬盘是否繁忙,若为1则繁忙,此寄存器中的其他位无效。另外的四位暂不关注。

 约定步骤:

1)先选择通道,往该通道的sector count寄存器中写入代操作的扇区数。

2)往该通道上的三个LBA寄存器写入扇区起始地址的低24位。

3)往device寄存器中写入LBA地址的24~27位,并将第6位为1,使其为LbA模式,设置第4位,选择操作的硬盘。

4)往该通道的command寄存器写入操作命令(状态寄存器)。

5)读取该通道的status寄存器,判断硬盘工作是否完成。

6)如果以上步骤是读硬盘,进入下一个步骤。完成,完工。

7)将硬盘数据读出。

硬盘完成工作后,我们选取查询传送方式中断传送方式将数据读出

查询传送方式,也称为程序I/O,PIO,是指在传输之前,由程序先去检测设备的状态。需要满足一定的条件才能传输,适合低速的设备,由于磁盘有status寄存器,所以可以使用此方式

中断传送方式,也称为中断驱动I/O,由于查询传送方式需要不断查询设备状态,这意味着只有最后一刻的查询才有意义,所以效率不高。设备准备好数据后,主动通知CPU来取,这样效率就高了。

3.6  改造MBR

        MBR只有512字节,在这么小的空间中,没法为内核准备好环境,更没法将内核成功加载到内存并运行。所以我们会通过另一个程序完成初始化环境及加载内核的任务,这个程序,我们称为loader,即加载器。MBR占据的是扇区的第0扇区第1扇区是空闲的,我们不妨把loader放到第2扇区。

        首先loader中要定义一些数据结构,且这些数据结构在之后的保护模式中要使用,所以loaer加载到内核后不能被覆盖。所以loader的加载地址选为0x900,当然也可以0x500

目的:我们将loader的代码写入磁盘中的第二个扇区,并在mbr中将loader的代码提取到地址为0x900处

代码实现

库代码实现  myos/boot/include/boot.inc

		;-------------  loader和kernel  ----------
LOADER_BASE_ADDR equ 0x900 
LOADER_START_SECTOR equ 0x2

MBR代码实现  myos/boot/mbr.S

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

   
    mov eax,LOADER_START_SECTOR    ;初始扇区lba地址,第2个扇区,即0x2
    mov bx,LOADER_BASE_ADDR        ;写入的地址 0x900
    mov cx,1                       ;待读入的扇区数
    call rd_disk_m_16              ; 以下读取程序的起始部分(一个扇区)
    
    jmp LOADER_BASE_ADDR           ;来到loader部分0x900
    
        ;-------------------
        ;功能:读取硬盘n个扇区
rd_disk_m_16:	   
        ;-------------------
            ; eax=LBA扇区号
			; ebx=将数据写入的内存地址
			; ecx=读入的扇区数

    mov esi,eax        ;备份eax,LBA的扇区号
    mov di,cx          ;备份cx,读入的扇区数
        ;读写硬盘
        ;第一步:选择通道,往该通道的sector count寄存器写入待操作的扇区数
    mov dx,0x1f2
    mov al,cl        ;在8086中,cx高位两个8位寄存器,高8位的ch和低8位的cl
    out dx,al        ;往sector count寄存器中打入1,代表待操作一个扇区

    mov eax,esi        ;回复ax

        ;第二步,往通道上的三个LBA寄存器写入扇区起始地址的低24位
    
        ;LBA地址的7~0位写入端口0x1f3
    mov dx,0x1f3                       
    out dx,al       

        ;LBA地址15~8位写入端口0x1f4
    mov cl,8
    shr eax,cl    ;逻辑右移8位
    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模式 主盘,LBA模式
    mov dx,0x1f6
    out dx,al

        ;第3步:向0x1f7端口写入读命令,0x20 0x20读扇区命令
    mov dx,0x1f7
    mov al,0x20                        
    out dx,al

        ;第4步:检测硬盘状态
.not_ready:
        ;同一端口,写时表示写入命令字,读时表示读入硬盘状态
    nop    ;相当于sleep一下
    in al,dx    ;这里dx不需要改变command和status是同一个寄存器
    and al,0x88    ;将寄存器信息读取到al中
    cmp al,0x08    ;与第四位相减,若第四位都等于1,则zf=0
    jnz .not_ready    ;若未准备好,继续等。如果zf!=0则跳转

             ;第5步:从0x1f0端口读数据
    mov ax, di    ;di当中存储的是要读取的扇区数
    mov dx, 256    ;每个扇区512字节,一次读取两个字节因为data寄存器是16位,所以一个扇区就要读取256次,与扇区数相乘,就等得到总读取次数
    mul dx    
    mov cx, ax    ;得到了要读取的总次数,然后将这个数字放入cx中
    mov dx, 0x1f0

.go_on_read:
    in ax,dx
    mov [bx],ax
    add bx,2		  
    loop .go_on_read    ;loop循环时会将cx-1
    ret

编译代码mbr.S 

nasm -I include/ -o mbr.bin mbr.S

写入磁盘

dd if=/home/moyao/Desktop/bochs/boot/mbr.bin of=/home/moyao/Desktop/bochs//hd60M.img bs=512 count=1 conv=notrunc

LOADER代码实现  myos/boot/loader.S

%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

nasm -I include/ -o loader.bin loader.S

写入磁盘

dd if=/home/moyao/Desktop/bochs/boot/loader.bin of=/home/moyao/Desktop/bochs//hd60M.img bs=512 count=1 seek=2 conv=notrunc

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值