《操作系统真象还原》——踩坑之路

我用的环境是vmware16+centos7.9。
安装前环境依赖:

sudo yum -y install gtk2 gtk2-devel
sudo yum -y install libXt libXt-devel
sudo yum -y install libXpm libXpm-devel
sudo yum -y install SDL SDL-devel
sudo yum -y install libXrandr-devel.x86_64 
sudo yum -y install xorg-x11-server-devel 
sudo yum -y install gcc-c++

安装bochs:

wget https://jaist.dl.sourceforge.net/project/bochs/bochs/2.6.2/bochs-2.6.2.tar.gz
 
tar -xvzf bochs-2.6.2.tar.gz
 
cd bochs-2.6.2

三部曲一:
这里注意一下,在- -prefix=/usr/local/share/bochs \ 命令中,将/usr/local/share/bochs换成自己的路径。我的是下载且解压缩之后,默认给我放到/usr/local/share/bochs这里的,也可以自己该。

./configure \
--prefix=/usr/local/share/bochs \  
--enable-debugger \
--enable-disasm \
--enable-iodebug \
--enable-x86-debugger \
--with-x \
--with-x11 \
LDFLAGS='-pthread' \
LIBS='-lX11'

三部曲二:
在当前目录下(应该是bochs-2.6.2)执行make指令。

[root@zklEdu bochs-2.6.2]# make

三部曲三:
没问题的话紧接着执行make install指令。

[root@zklEdu bochs-2.6.2]# make install

配置bochs:
搞完三部曲之后就要搞bochsrc.disk了。

vim bochsrc.disk

在bochsrc.disk中键入以下内容(个人建议自已亲自敲一下,直接从网上复制的很可能不靠谱,都是泪~):

megs: 32

romimage: file=/usr/local/share/bochs/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/local/share/bochs/share/bochs/VGABIOS-lgpl-latest

boot: disk

log: bochs.out

mouse: enabled=0
keyboard_mapping: enabled=1, map=/usr/local/share/bochs/share/bochs/keymaps/x11-pc-us.map

ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="hd60M.img", mode=flat, cylinders=121, heads=16, spt=63
#gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0

提几个点:
1、这里的 romimage: file=/usr/local/share/bochs/share/bochs/BIOS-bochs-latest和
vgaromimage: file=/usr/local/share/bochs/share/bochs/VGABIOS-lgpl-latest,路径要注意,跟上面- -prefix=/usr/local/share/bochs \指令中的路径一样,我的都是/usr/local/share/bochs,然后后面的都一样。
2、这里的 ata0-master: type=disk, path=“hd60M.img”, mode=flat, cylinders=121, heads=16, spt=63,因为后面创建硬盘还是会加的,这里就一起写了。
3、这里的 #gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0是把这句话注掉了,因为前面说了不要打开- -enable-gdb-stub,所以这里不注释掉,会报错。(吐槽一下书上没注释掉这句话,我傻傻地照着书抄,虽然前面说了 T-T)

然后就是创建磁盘了:
我是直接键入bin/bximage,然后照着提示往下做,提示挺清晰的。
依次键入hd、flat、60M、hd60M.img。

运行bochs:

bin/bochs -f bochsrc.disk

有意思的来了,会发现出现的界面跟书上不一样,只有一大块黑框:

在这里插入图片描述
这时需要再键入一个小c(吐槽哇~还好有万能的网友):

在这里插入图片描述
然后才会出现想要的界面(一个panic T-T):

在这里插入图片描述
再接下来就是MBR的事了:
首先:

yum install -y nasm

然后编写mbr.S,其内容为:

SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
 
mov ax, 0x600
mov bx, 0x700
mov cx, 0
mov dx, 0x184f
int 0x10
 
mov ah, 3
mov bh, 0
int 0x10
 
mov ax, message
mov bp, ax
mov cx, 48
mov ax, 0x1301
mov bx, 0x2
int 0x10
 
jmp $
 
message db"I believe I will succeed in the end"
times 510-($-$$) db 0
db 0x55,0xaa

接着就是编译:

nasm -o mbr.bin mbr.S

再然后就是用dd了(还是老问题,注意路径):

dd if=/usr/local/share/mbr.bin of=/usr/local/share/bochs/hd60M.img bs=512 count=1 conv=notrunc

然后运行bochs(别忘了那个小写的c):

bin/bochs -f bochsrc.disk

看成品(真心不容易 T-T):

在这里插入图片描述
让MBR使用硬盘:
在mbr.S同级目录下创建include目录,并在include目录下创建boot.inc,其内容为:

LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2

然后就是修改原来的mbr.S,新的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,0x600
    mov bx,0x700
    mov cx,0        ;左上角(0,0)
    mov dx,0x184f   ;右下角(80,25)
                    ; VGA文本模式中,一行只能容纳80个字符,共25行。
			        ; 下标从0开始,所以0x18=24,0x4f=79
    int 0x10        ; int 0x10


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

    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扇区号
;bx=将数据写入的内存地址
;cx=读入的扇区数
;------------------------------------------------------
    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
    shr eax,8
    mov dx,0x1f4
    out dx,al

    ;LBA地址23~16位写入端口0x1f5
    shr eax,8
    mov dx,0x1f5
    out dx,al

    ;LBA地址24~27位 device
    shr eax,8
    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                      ;每次读入一个字,需要256*扇区数
    mov cx,ax               
    mov dx,0x1f0

.go_on_read:
    in ax,dx
    mov [bx],ax
    add bx,2
    loop .go_on_read
    ret
    
; -----------------------------------------------------
;功能:填充510+0x55+0xaa
;------------------------------------------------------
    times 510-($-$$) db 0
    db 0x55,0xaa

接着就是编译了:

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

再然后就是用dd命令将mbr.bin写入虚拟硬盘(注意路径):

dd if=./mbr.bin of=/usr/local/share/bochs/hd60M.img bs=512 count=1 conv=notrunc

然后就是实现简单的内核加载器,loader.S的内容为:

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
    mov byte[gs:0x00],'1'
    mov byte[gs:0x01],0xA4

    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 $

还是要编译:

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

用dd命令(注意路径):

dd if=./loader.bin of=/usr/local/share/bochs/hd60M.img bs=512 count=1 seek=2 conv=notrunc

回到bochs目录中,然后运行bochs:

bin/bochs -f bochsrc.disk

接下来是进入保护模式:
首先要修改的是mbr.S,将其中的读入扇区数部分进行修改:

mov cx,4	;原来的是 mov cx,1
call rd_disk_m_16

完整的新的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,0x600
    mov bx,0x700
    mov cx,0        ;左上角(0,0)
    mov dx,0x184f   ;右下角(80,25)
                    ; VGA文本模式中,一行只能容纳80个字符,共25行。
			        ; 下标从0开始,所以0x18=24,0x4f=79
    int 0x10        ; int 0x10


;输出背景色绿色,前景色红色,并且跳动的字符串
    mov byte[gs:0x00],'2'
    mov byte[gs:0x01],0xA4

    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

    mov eax,LOADER_START_SECTOR ;起始扇区的lba地址
    mov bx,LOADER_BASE_ADDR     ;写入的地址
    mov cx,4                    ;待读入的扇区数
    call rd_disk_m_16           ;以下读取程序的起始部分(一个扇区)
    jmp LOADER_BASE_ADDR

; -----------------------------------------------------
;功能:读取硬盘n个扇区
rd_disk_m_16:
;eax=LBA扇区号
;bx=将数据写入的内存地址
;cx=读入的扇区数
;------------------------------------------------------
    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
    shr eax,8
    mov dx,0x1f4
    out dx,al

    ;LBA地址23~16位写入端口0x1f5
    shr eax,8
    mov dx,0x1f5
    out dx,al

    ;LBA地址24~27位 device
    shr eax,8
    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                      ;每次读入一个字,需要256*扇区数
    mov cx,ax               
    mov dx,0x1f0

.go_on_read:
    in ax,dx
    mov [bx],ax
    add bx,2
    loop .go_on_read
    ret

; -----------------------------------------------------
;功能:填充510+0x55+0xaa
;------------------------------------------------------
    times 510-($-$$) db 0
    db 0x55,0xaa

另一个要更新的文件是boot.inc,新的完整代码如下:

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

;-------- gdt描述符属性 --------
DESC_G_4K       equ     10000000_00000000_00000000b
DESC_D_32       equ     01000000_00000000_00000000b
DESC_L          equ     00100000_00000000_00000000b;64位代码标记,此处标记位0便可
DESC_AVL        equ     00000000_00000000_00000000b;暂时没用设为0
DESC_LIMIT_CODE2    equ 1111_00000000_00000000b
DESC_LIMIT_DATA2    equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2   equ 0000_00000000_00001011b
DESC_P          equ     10000000_00000000b
DESC_DPL_0      equ     00000000_00000000b
DESC_DPL_1      equ     00100000_00000000b
DESC_DPL_2      equ     01000000_00000000b
DESC_DPL_3      equ     01100000_00000000b
DESC_S_CODE     equ     00010000_00000000b
DESC_S_DATA     equ     DESC_S_CODE
DESC_S_SYS      equ     00000000_00000000b
;x=1,c=0,r=0,a=0代码段是可执行的,非一致性,不可读,已访问位a清0
DESC_TYPE_CODE  equ     1000_00000000b
;x=0,c=0,r=1,a=0数据段是不可执行的,向上扩张,可写,已访问位a清0
DESC_TYPE_DATA  equ     0010_00000000b

DESC_CODE_HIGH4 equ     (0x00<<24)+DESC_G_4K+DESC_D_32+DESC_L+DESC_AVL+DESC_LIMIT_CODE2+DESC_P+DESC_DPL_0+DESC_S_CODE+DESC_TYPE_CODE+0x00

DESC_DATA_HIGH4 equ     (0x00<<24)+DESC_G_4K+DESC_D_32+DESC_L+DESC_AVL+DESC_LIMIT_DATA2+DESC_P+DESC_DPL_0+DESC_S_DATA+DESC_TYPE_DATA+0x00

DESC_VIDEO_HIGH4    equ    (0x00<<24)+DESC_G_4K+DESC_D_32+DESC_L+DESC_AVL+DESC_LIMIT_VIDEO2+DESC_P+DESC_DPL_0+DESC_S_DATA+DESC_TYPE_DATA+0x00  

;-------- 选择子属性 --------
RPL0    equ     00b
RPL1    equ     01b
RPL2    equ     10b
RPL3    equ     11b

TI_GDT  equ     000b
TI_LDT  equ     100b

最后就是loader.S,新的完整代码如下:

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
;构建gdt及其内部的描述符
    GDT_BASE:   dd  0x00000000
                dd  0x00000000
    CODE_DESC:  dd  0x0000FFFF
                dd  DESC_CODE_HIGH4
    DATA_STACK_DESC:dd 0x0000FFFF
                    dd DESC_DATA_HIGH4
    ;B0000 B7FFF 32KB 黑白显示适配器
    VIDEO_DESC: dd  0x80000007;limit=(0xbffff-0xb8000)/4k=0x7
                dd  DESC_VIDEO_HIGH4
    GDT_SIZE    equ $-GDT_BASE
    GDT_LIMIT   equ GDT_SIZE-1
    times 60 dq 0;预留60个描述符空位
    SELECTOR_CODE equ (0x0001<<3)+TI_GDT+RPL0
    SELECTOR_DATA equ (0x0002<<3)+TI_GDT+RPL0
    SELECTOR_VIDEO equ (0x0003<<3)+TI_GDT+RPL0
    gdt_ptr dw GDT_LIMIT
            dd GDT_BASE
    loadermsg db '2 loader in real.'

loader_start:
; -----------------------------------------------------------
;INT 0x10   功能号:0x13	   功能描述:打印字符串
;------------------------------------------------------
;输入:
;AH 功能号= 0x13H
;BH = 页码
;BL = 属性(若AL=00H或01H)
;CX = 字符串长度
;ES:BP=字符串地址
;AL=输出方式
;   0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
;   1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
;   2——字符串中含显示字符和显示属性。显示后,光标位置不变
;   3——字符串中含显示字符和显示属性。显示后,光标位置改变
;无返回值

    mov sp,LOADER_BASE_ADDR
    mov bp,loadermsg
    mov cx,17
    mov ax,0x1301           ;AH=13,AL=01
    mov bx,0x001f           ;页号为0,蓝底粉红字(BL=1fh)
    mov dx,0x1800
    int 0x10                ;10h号中断

;-------- 准备进入保护模式 --------    
;1:打开A20
;2:加载gdt
;3:将cr0的pe位置1

    ;-------- 打开A20 --------
    in al,0x92
    or al,0000_0010B
    out 0x92,al

    ;-------- 加载GDT --------
    lgdt [gdt_ptr]

    ;-------- 将cr0的pe位置1 --------
    mov eax,cr0
    or eax,0x00000001
    mov cr0,eax

    jmp dword SELECTOR_CODE:p_mode_start;刷新流水线

[bits 32]
p_mode_start:
    mov ax,SELECTOR_DATA
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov esp,LOADER_STACK_TOP
    mov ax,SELECTOR_VIDEO
    mov gs,ax

    mov byte[gs:160],'p'
    mov byte[gs:161],0x1f

    jmp $

编译:

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

写入虚拟硬盘:

dd if=./mbr.bin of=/usr/local/share/bochs/hd60M.img bs=512 count=1 conv=notrunc
dd if=./loader.bin of=/usr/local/share/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc

回到bochs目录下运行:

bin/bochs -f bochsrc.disk
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值