操作系统入门(五) - 载入32位保护模式代码为c做准备

操作系统入门( 五) - 载入32 位保护模式代码为c 做准备
 
经过上千次的试验,终于还是没有能够在vmware实现用一段程序载入kernel.img然后执行32位的kernel代码,无数次的跳出
无奈呀,后来知道实际上是因为gdt全局描述表没有正确载入造成的问题,至于能跳出这样提示的代码,我想没有人愿意去看吧,错的有什么好看的?
 
于是乎找来了鼎鼎大名的 Bochs来帮忙
http://bochs.sourceforge.net/
它能对自己做的操作系统进行调试,如果早些用它的话,我就不会等了3年才又再玩起操作系统来了,说不定已经出了一些成果了呢。
 
但是自己笨没有办法,还是多向别人学习吧。
 
前面做过的程序仅仅是把16位的kernel.img载入到0x90000地址的地方执行,如果要在kernel.img里面再设置保护模式,那么,那个时候还需要从磁盘提取数据的程序,一大堆,现在我发现启动程序里面还可以把设置保护模式的程序加上而没有超过512字节,所以先加上了,试试看用C混合汇编得到的32位代码的执行效果,以后再看看怎么增强kernel.img
 
下面是启动程序
;;     文件 :fatboot.asm
;;     作用 :      7c00h 处启动
;;                   kernel.img 载入到 0x90000 的地方
;;                   32 位方式执行  
;;     有文件系统 ,1.44M 512bits/80sec 软盘启动,
;;     创建日期 :2004/01/30 flyback
;;     修改日期 :2006/04/24 flyback
;;     http://blog.csdn.net/flyback
;;     mailto:[email protected]
;;     ===================================
 
 
       %define loadpoint 0x9000                          ; 载入点,初始化程序载入到 9000h 的地方
       %define loadoffset 0x0                      
 
       bits 16
       ORG 0x7c00 ; 启动入口地址
main:
       jmp short start              ; 跳转到开始程序入口
       nop                ;
 
; 引导区文件系统数据
;----------------------------------------------------------------------------
       brOEM           DB   ' -My-0S.'              ; 0003h - 引导程序的名字
       brBPS            DW 0x200            ; 000Bh - 每扇区的字节数 512
       brSPC            DB   0x01                     ; 000Dh - 每簇扇区数
       brResCount    DW 0x0001           ; 000Eh - 保留扇区数
       brFATs           DB   0x02                     ; 0010h - FAT 备份数
       brRootEntries DW 0x00e0           ; 0011h - 根目录入口数
       brSectorCount      
                            DW 2880              ; 0013h - 磁盘容量扇区数 < 32MB
       brMedia          DB   240                ; 0015h - 媒体描述符
       brSPF            DW 9                   ; 0016h - FAT 扇区数
       brSPH            DW 18                  ; 0018h - 每磁道扇区数
       brHPC            DW 2                   ; 001Ah - 盘面数
       brHidden        DD 0                   ; 001Ch - 隐藏扇区数
       brSectors       DD 0                   ; 0020h - 如果大于 32m 的扇区总数
                            DB   0                   ; 0024h - 物理驱动器号
                            DB   0                   ; 0025h - 系统保留
                            DB   29H               ; 0026h - 扩展扇区标记(包含 29h
       brSerialNum     DD    00000006H     ; 0027h - ID
       brLabel           DB   'teachosdisk'   ; 002Bh - 卷标
       brFSID          DB   'FAT12   '            ; 0036h - 系统保留
;------------------------------------------------------------------------
 
start:
       cli                  ; 关中断,防止意外中断打断程序执行
       mov ax, cs     ;
       mov ds, ax     ; 设置数据段
       mov es, ax      ;
 
       mov ax, 0x0000; 设置堆栈段
       mov ss, ax
       mov sp, 0ffffh       ; 堆栈入口
 
       sti                  ; 开中断
             
       mov si, loadmsg     ; 调用显示载入信息
       call pntchr
 
       ; 把磁盘目录信息载入到 7c00:0200 的地方
loadroot:
       mov        cx, 0
       mov        dx, 0      
       mov        ax, 0x0020                   ; 32 个字节 / 文件
       mul         WORD [brRootEntries] ; 32*224 (文件数) = 7168 (最多所有文件所占的字节数)
       div          WORD [brBPS]                   ; 7168/512 = 14 (Root dir) 文件的目录描述字节占用的扇区数 14
       xchg              ax, cx                          ; CX = 14
       mov        al, [brFATs]                  ; 2
       mul         word [brSPF]               ; 9 * 2 = 18
       add         ax, word [brResCount] ; 18 + 1 now AX = 19
       mov        WORD[datasector], ax         ; 目录起始的扇区 19
       add         WORD[datasector], cx         ; 数据区起始扇区 33
 
       ; 目录放入 0x0200 内存
       mov bx, 0x0200
       call ReadSec
 
       ; 比较目录中是否有 init.img 文件存在
       mov     cx, WORD [brRootEntries] ; CX:224 在目录的所有文件中寻找
       mov     di, 0x0200                   ; 从目录入口处开始 0x200
.LOOP:
       push    cx                                       ; 保护 CX = 224
      
       mov        cx, kernellen                 ; 8 个文件名称和 3 个扩展名称
       lea          si, [kernelname]                   ;
       push              di                                
       repe cmpsb                               ; stop compare if cx >0 or the
                                                        ; first unequal is met(zf=1)!
       pop     di
       je        loadfiledec                      ;if zf = 1, jmp LOAD_FAT!(Seek OK!)
       pop     cx
       add     di, 0x0020                           ; 跳到下一个目录入口
       loop    .LOOP                                 ; cx 自动减
       jmp     failure
 
       ; 载入文件描述子节
loadfiledec:
       ; 把启动的镜像文件 *.img 文件的起始单元保存起来
       mov   si, CRLF
       call    pntchr
       mov si, loadfat
       call    pntchr      
       lea   si, [kernelname]
       call pntchr
 
       mov     dx, WORD [di + 0x001A]                         ;the file's first cluster
       mov     WORD [cluster], dx                  ; store the first cluster
      
       ; 计算 fat 的大小并保存到 07c00:0x0200(ds:bx)
       xor     ax, ax
       mov     al, BYTE [brFATs]                ; al:2
       mul     WORD [brSPF]                ; AH:18 = 9*2
       mov     cx, ax                                                     ; CX:18
 
       ; 读取数据放入到 0x7c00:0x0200 ds:bx
       mov     ax, WORD [brResCount]          ; AX:1
       mov     bx, 0x0200                          ;
       call    ReadSec                                              ; AX:1 CX:18 BX:0200
 
       ; 读取 img 放入 9000:0x0000
       mov ax, loadpoint
       mov es, ax
       mov bx, loadoffset
       push    bx
 
LOAD_IMAGE:
       mov     ax, WORD [cluster]     ; cluster to read
       pop     bx                                ; buffer to read into
       call    ClusterLBA                     ; convert cluster to LBA
                                                 ; AX:[cluster] ES:BX[9000:0000](dest)
       xor     cx, cx
       mov     cl, BYTE [brSPC]        ; CL:1
       call    ReadSec                         ; AX:LBA CX:1 BX:0000 ES:0100
       push    bx
      
       ; 计算下一个单元
       mov     ax, WORD [cluster]     ; identify current cluster
       mov     cx, ax                         ; copy current cluster
       mov     dx, ax                         ; copy current cluster
       shr     dx, 0x0001                    ; divide by two
 
       add     cx, dx                          ; sum for (3/2)
       mov     bx, 0x200             ; location of FAT in memory
       add     bx, cx                            ; index into FAT
       mov     dx, WORD [bx]             ; read two bytes from FAT
       test    ax, 0x0001
       jnz     .ODD_CLUSTER
 
.EVEN_CLUSTER:
       and     dx, 0000111111111111b               ; take low twelve bits
       jmp     .DONE
 
.ODD_CLUSTER:
       shr     dx, 0x0004                          ; take high twelve bits
 
.DONE:
       mov     WORD [cluster], dx                  ; store new cluster
       cmp     dx, 0x0FF0                          ; test for end of file,>=0x0FF0: end or bad!
       jb      LOAD_IMAGE                                                 ; if dx<0x0ff0 (CF=1), jmp Load_image
    DONE:                                                                   ; this Label is not used!
       jmp gotopm
      
failure:
       mov si, CRLF
       call pntchr
       mov si, loadfail
       call pntchr
       hlt
      
; ---------------- 子程序区 -------------------------
 
;********************* 显示字符串 ********************************************
;
;
;
;***************************************************************************
 
pntchr:   
       pusha
.pnt:
       lodsb                            ; DS:SI 装载一个字符到 AL
       or al,al                          ;
       jz endpntchr                  ; 如果 al = 0, 返回      
                                          ;
       mov ah,0x0E                ;
       mov bx,0x004a             ; 
       int 0x10                        ; 调用 bios 中断显示字符
       jmp .pnt                ;
                                          ;
endpntchr:                           ;
       popa
       ret                               ; 返回
 
 
;*************************************************************************
; PROCEDURE ReadSec
; ax+1 的地方把 cx 个扇区载入到 es:bx 的内存
; 注意扇区是从 2 开始, 1 扇区已经读入了
;*************************************************************************
ReadSec:
.Main:
 
.secloop:
       push    ax
       push    bx
       push    cx                                                            ;protect ax, bx, cx
 
       call    LBACHS                  ; 调用转换
 
       mov     ah, 0x02        ; BIOS 读取扇区命令
       mov     al, 0x01         ; 一个扇区
       mov     ch, BYTE [Track] ; track
       mov     cl, BYTE [Sector] ; sector
       mov     dh, BYTE [Head]   ; head
       mov     dl, 0                     ; 因为是 a: 所以为 0
       int        0x13                     ; 调用中断
 
       jnc     .SUCCESS                            ; test for read error
      
       xor ax, ax
 
       pop     cx
       pop     bx
       pop     ax
       jnz .secloop
       jmp failure                   ; 错误则显示出错信息并停止
       hlt
      
.SUCCESS
       mov    si, Progress
       call      pntchr
       pop     cx
       pop     bx
       pop     ax
 
       add bx, WORD [brBPS]       ; 读取下一个扇区的内容
       inc   ax                               
       loop .Main                    ; cx -= 1, if cx != 0, jmp .main
       ret
 
;*************************************************************************
; PROCEDURE LBACHS
; 转换逻辑块访问为读取磁盘所使用的磁道,盘面,扇区
; 相对扇区 = ( 逻辑扇区 / 每磁道扇区数 ) + 1
; 相对盘面    = ( 逻辑扇区 / 每磁道扇区数 ) MOD 盘面数
; 相对磁道  = 逻辑扇区 / ( 每磁道扇区数 * 盘面数 )
;*************************************************************************
LBACHS:
       xor     dx, dx                           ; dx = 0
       div     WORD [brSPH]             ; div m16: ax/18 -> :ax 余数 :dx
       inc     dl                                  ;
       mov     BYTE [Sector], dl        ;sector No relative to the Track
       xor     dx, dx                           ; dx = 0
       div     WORD [brHPC]             ; ax/2 -> :ax 余数 :dx
       mov     BYTE [Head], dl           ;
       mov     BYTE [Track], al         ;
       ret
 
;*************************************************************************
; PROCEDURE ClusterLBA
; 转换单元访问到直接扇区访问
; LBA = (cluster - 2) * sectors per cluster
;*************************************************************************
ClusterLBA:
sub     ax, 0x0002                          ; zero base cluster number
xor     cx, cx
mov     cl, BYTE [brSPC]        ; convert byte to word
mul     cx
add     ax, WORD [datasector]               ; base data sector
ret
 
;-------------------- 数据区 -------------------------------
 
loadmsg         db 'Boot',0                                  ; 要显示的字符窜以 0 结尾
loadfail           db 'Load Fail',0                                  ; 载入失败信息
Sector            db 0x00
Head              db 0x00
Track             db 0x00
datasector       dw 0x0000                                 ; 33 数据区起始扇区号
rootaccess      equ 0x0200                                 ; 存放目录数据的偏移地址
cluster            dw 0x0000
CRLF                    db 13,10,0
Progress         db ".", 0
loadfat            db 'Load', 0                                ; 字符串,回车,换行, 0
kernelname     db "KERNEL IMG"                          ; 11 chars                    
kernellen         equ $ - kernelname                      ; 长度
 
gdtr :
dw gdtend - gdt - 1                                                 ; gdt 的长度
dd gdt                                                             ; gdt 的物理地址
gdt:
       gdt0:
       dw 0,0,0,0                                               ; 据说是一定要为 0 否则会有错
codesel_gdt:
       dw   0xffff                                                ; 界限 Limit = 0x100000 * 0x1000 = 4GB
       dw   0                                                      ; 基地址 = 0
       dw 0x9A00                                             ; 表示代码段 可读可执行
       dw   0x00CF                                             ; 粒度(不知道是什么意思)
datasel_gdt:
       dw   0xffff                                                ; 4GB
       dw   0x0                                                   ; 基地址
       dw 0x9200                                              ; 数据段 可读可写
       dw 0x00CF                                             ; 粒度
gdtend:
 
codesel equ codesel_gdt - gdt
datasel equ datasel_gdt - gdt
 
;             从这里启动 32 位的模式
gotopm:
       cli
       lgdt [gdtr]
 
       mov eax, cr0
       or eax,1
       mov cr0, eax
 
       jmp dword codesel:loadpoint * 0x10 + loadoffset
 
; -----------------------------------------------------------------
times 510 - ($ - $$) db 0                     ; 保证 boot 区有 512 个字节
       dw 0AA55h                         ; boot 区标记
 
times 1474560 - ( $ - $$) db 0             ; 大小为 1.44M
 
暗红色的地方做了修改
下面是kernel
;;     =================
;;     文件 :     
;;                   KERNEL.asm
;;     作用 :     
;;                   0x90000 处启动
;;                   显示一串信息
;;     创建日期 :
;;                   2006/04/24 flyback
;;     http://blog.csdn.net/flyback
;;     mailto:[email protected]
;;      =================
[bits 32]
org 0x90000
 
kernel:    
       jmp kernel_start
       msg db 'Portected mode! Start at 0x90000',0
       pos dd 0
       ; re calculate
kernel_start:   
       cli
       lgdt[cs:gdtrs]
 
       mov eax, cr0
       or eax,1
       mov cr0, eax
 
       mov ax, datasels
       mov ds, ax
       mov es, ax
       mov ss, ax
       mov gs, ax
       mov fs, ax
       mov esp, 0x90000
 
       lea esi, [msg]
       call disp
       hlt
 
disp:
              push esi
              push di
              push ax
              ; mov ax, datasels
              ; mov es,ax
.lop:
              mov edi,dword [pos]
              add edi, 0xb8000
              mov al, byte [esi]
              mov ah, 3
              mov [es:edi], ax
              inc dword [pos]
              inc dword [pos]
              inc esi
              test al, al
              jnz .lop
              pop ax
              pop di
              pop esi          
              ret
gdtrs :
dw gdtends - gdts - 1    ; gdt 的长度
dd gdts                         ; gdt 的物理地址
gdts:
       gdt0s:
       dw 0,0,0,0            ; 据说是一定要为 0 否则会有错
codesel_gdts:
       dw   0xffff             ; 界限 Limit = 0x100000 * 0x1000 = 4GB
       dw   0                   ; 基地址 = 0
       dw 0x9A00          ; 表示代码段 可读可执行
       dw   0x00CF          ; 粒度(不知道是什么意思)
 datasel_gdts:
       dw   0xffff             ; 4GB
       dw   0x0                ; 基地址
       dw 0x9200           ; 数据段 可读可写
       dw 0x00CF          ; 粒度
gdtends:
 
codesels equ codesel_gdts - gdts
datasels equ datasel_gdts – gdts
把他们分别编译成fatboot.img和KERNEL.IMG文件然后用WinImage把fatboot.img打开,把kernel.img文件拖入WinImage
保存完毕后就可以使用bochs来调试了
首先要安装好bochs后要写一个批处理文件,一个配置文件
config.txt
# configuration file generated by Bochs
config_interface: textconfig
display_library: win32
megs: 32
romimage: file=E:/ 操作系统 /Bochs-2.1.1/Bochs-2.1.1/BIOS-bochs-latest, address=0xf0000
vgaromimage: E:/ 操作系统 /Bochs-2.1.1/Bochs-2.1.1/VGABIOS-elpin-2.40
boot: floppy
floppya: 1_44="fatboot.img", status=inserted
floppyb: 1_44="none", status=inserted
ata0: enabled=0
ata1: enabled=0
ata2: enabled=0
ata3: enabled=0
parport1: enabled=1, file="parport.out"
com1: enabled=1, dev=""
usb1: enabled=1, ioaddr=0xff80, irq=10
# no sb16
floppy_bootsig_check: disabled=0
vga_update_interval: 300000
keyboard_serial_delay: 250
keyboard_paste_delay: 100000
floppy_command_delay: 500
ips: 1000000
text_snapshot_check: 0
mouse: enabled=0
private_colormap: enabled=0
i440fxsupport: enabled=0
clock: sync=none, time0=local
# no ne2k
newharddrivesupport: enabled=1
# no loader
log: bochsout.txt
logprefix: %t%e%d
debugger_log: debug.txt
panic: action=ask
error: action=report
info: action=report
debug: action=ignore
pass: action=fatal
keyboard_mapping: enabled=0, map=
keyboard_type: mf
user_shortcut: keys=none
# no cmosimage
上面的内容是用bochs.exe在执行的时候回答选项然后保存下来的
start.bat
bochsdbg -f config.txt –q
bochsdbg是bochs的调试程序
-f      参数指定一个名叫config.txt的文件作为配置
-q     参数跳过bochs的内置菜单直接运行
然后运行start.bat
执行的命令有不少,可以看bochs的文档internal-debugger.html一节
常用的是下面的一些
c      继续执行
s [count]单步执行count步指令,没有count就是1
ctrl-c       中断程序执行,出现命令行方式进入单步执行的状态
q      退出bochs
b      address 在address地址处设置断点
u      address address显示在address到address处的指令
info r       显示所有寄存器信息
info b       显示设置的断点
dump_cpu       显示cpu相关信息
然后我们先设置在0x7c00启动地址的断点b 0x7c00
然后让它继续运行c
到了0x7c00它就会停下来
这时候可以让它单步运行s
然后每次敲回车它就可以单步了
如果对上面的程序不放心,那么就用单步调试吧!,最后出现的结果
说明我们的kernel可以载入到0x90000的地方正确运行了

你可能感兴趣的:(OS入门堂)