您的位置:首页 > 其它

一个用x86汇编编写的、页式管理、图形化的CS -- Boot篇

2016-06-03 17:02 295 查看
自己动手做一个CS,是一件有趣的事情。在编写的过程中,能够学习知识,增长见识。看到自己编写的系统在虚拟机上成功的运行起来,也是一件让人愉悦的事情。
本Boot源自《x86汇编语言-从实模式到保护模式》第17章的代码,在其基础上进行了有限的修改。
该Boot主要实现了以下一些功能:
1. 启动图形模式(因为整个CS都是在图形模式下运行);
2. 启动32位保护模式;
3. 启动内存页管理模式;
4. 将0-1M内存空间映射到0xc0000000-0xc0100000处(3G处);
5. 将CS代码从硬盘拷贝到0xc0010000处;
6. 将GDT、堆栈指针调整到0xc0000000以上;
7. 跳转到CS代码处开始运行;
详细的基础内容,请参考《x86汇编语言-从实模式到保护模式》一书,里面有非常详细的解答。
使用nasm进行编译,运行的虚拟机为virtualbox 4.3.24。
 
Boot具体的实现代码如下(mbr.asm):
;===============================================================================

;=== 本代码为系统Boot,功能包括:                                                                                                      ===

;=== 1.启动图形模式                                                                                                                                ===

;=== 2.进入32位保护模式                                                                                                                        ===

;=== 3.启动页管理模式                                                                                                                            ===

;=== 4.将0-1M内存空间映射到0xc0000000-0xc0100000处                                                                    ===

;===   -其中,系统代码在0xc0010000处开始;                                                                                      ===

;===   -系统进程的页表1指向新页面,保障后面加载用户进程的顺利进行                                              ===

;=== 5.调整gdt、堆栈指针到0xc0000000以上                                 
c7ea
                                                       ===

;=== 6.跳转到系统代码开始运行                                                                                                             ===

;===============================================================================

;===============================================================================

;===                                                            该处用于保存常量                                                                ===

;===============================================================================

    core_base_address equ 0x00010000           ;常数,内核加载的起始内存地址(64K起始) 

    core_start_sector equ 0x00000001               ;常数,内核的起始逻辑扇区号 

    core_sector_num   equ 0x00000032             ;常数,内核代码的扇区个数

        

;-------------------------------------------------------------------------------         

    gdt_base_address  equ 0x00008000           ;常数,gdt表的起始地址

    gdt_code_high_dd  equ 0x00cf9800            ;常数,gdt表中代码段的高双字

    gdt_code_low_dd   equ 0x0000ffff               ;常数,gdt表中代码段的低双字

    gdt_data_high_dd  equ 0x00cf9200             ;常数,gdt表中数据段的高双字

    gdt_data_low_dd   equ 0x0000ffff                ;常数,gdt表中数据段的低双字

  

    boot_code_addr    equ 0x00007000            ;常数,boot代码页的硬件地址

 

;-------------------------------------------------------------------------------         

    page_dir_address  equ 0x1000                   ;常数,页目录硬件地址(第1页)

    page_1_address    equ 0x2000                   ;常数,第一页页表的硬件地址(第2页)

    page_1_new_addr   equ 0x3000                 ;常数,将代码放入3G地址后,更新页表1地址

         

;===============================================================================

;===                                                          该处开始Boot代码                                                                   ===

;===============================================================================

SECTION  mbr  vstart=0x00007c00         

    cli                                                                    ;中断机制尚未工作

    xor ax, ax                                                        ;初始化ds和ss段寄存器 

    mov ds, ax

    mov ax, cs

    mov ss, ax

    mov sp, 0x7c00

  

;-------------------------------------------------------------------------------

    sti                                                                   ;开中断,使BIOS中断正常运行

    mov ax, 0x13                                                 ;设置为图形模式320×200-256色

    int 0x10      

    cli                                                                   ;关中断

       

;------------------------在gdt表中安装代码和数据段------------------------------

    ;计算GDT所在的逻辑段地址

     mov eax,[cs:pgdt+0x02]                               ;GDT的32位物理地址

    ;跳过0#号描述符的槽位 

    ;创建1#描述符,保护模式下的代码段描述符

    mov dword [eax+0x08],gdt_code_low_dd     ;基地址为0,界限0xFFFFF,DPL=00 

    mov dword [eax+0x0c],gdt_code_high_dd    ;4KB粒度,代码段描述符,向上扩展

    ;创建2#描述符,保护模式下的数据段和堆栈段描述符 

    mov dword [eax+0x10],gdt_data_low_dd     ;基地址为0,界限0xFFFFF,DPL=00

    mov dword [eax+0x14],gdt_data_high_dd    ;4KB粒度,数据段描述符,向上扩展

    ;初始化描述符表寄存器GDTR

    mov word [cs:pgdt],23                                   ;描述符表的界限(三个描述符)   

 

    lgdt [cs:pgdt]                                                  ;将gdt写入gdt寄存器

     

;----------------------------启动32位保护模式-----------------------------------

    in al,0x92                                                      ;南桥芯片内的端口 

    or al,0000_0010B

    out 0x92,al                                                    ;打开A20

     mov eax,cr0                  

    or eax,1

    mov cr0,eax                                                  ;设置PE位

      

    ;以下进入保护模式... ...

    jmp dword 0x0008:flush                                ;16位的描述符选择子:32位偏移

                                                                          ;清流水线并串行化处理器

    [bits 32]               

flush:                                  

    mov eax,0x00010                                          ;加载数据段(4GB)选择子

    mov ds,eax

    mov es,eax

    mov fs,eax

    mov gs,eax

    mov ss,eax                                                    ;加载堆栈段(4GB)选择子

    mov esp,0x00007c00                                    ;堆栈指针,从0x7c00开始向下走 

         

    mov byte [0xa0000], 0x55                             ;显示白点

;-------------------------将系统从硬盘读到64K处--------------------------------

    mov eax, core_start_sector                           ;从逻辑扇区1处开始读取 

    mov ebx, core_base_address                       ;将系统放到64K开始处

    mov ecx, core_sector_num                           ;连续读32个扇区共16K字节

rdcore:                                      

    call read_hard_disk_0

    inc eax                                                           ;下一个逻辑扇区号 

    loop rdcore         

;-----------------------------初始化页目录和页表--------------------------------

page:

    mov eax, page_dir_address                          ;页目录最后一项指向页目录本身 

    mov ebx, eax

    or ebx, 0x3                                                    ;页硬件地址再加上存在位 

    mov dword [eax+4092],ebx                           ;让最后一个页目录指向页目录页自己

                 

;----------------------------将地址投影到高3G处---------------------------------

    ;将页表1的前256项初始化成硬件地址0x0~0x100000(1M)

    mov eax, page_1_address

    xor ebx, ebx

    xor edx, edx                       

    mov edx, 0x3                                                ;增加页存在位(P=1)和权限位

    mov ecx, 256                                                ;一共初始化256页,共1M

page_1_init:

    mov dword [eax+ebx], edx

    add ebx, 4

    add edx, 0x1000                                           ;每次增加一页 

    loop page_1_init

         

    ;让页目录表的第一项和第0x300项指向页表1         

    mov eax, page_dir_address            

    mov edx, page_1_address

    add edx, 0x3

    mov dword [eax+0x0], edx                          ;页目录第0项指向它,否则启动页管理后cp会不知所措

    mov dword [eax+0xc00], edx                      ;页目录第0x300项,对应线性地址0xc0000000起始

;-----------------------------启动页式管理机制----------------------------------

    ;令CR3寄存器指向页目录,并正式开启页功能 

    mov eax, page_dir_address                      ;PCD=PWT=0

    mov cr3,eax

    ;将GDT的线性地址映射到从0xc0000000开始的相同位置 

    sgdt [pgdt]

    mov ebx,[pgdt+2]

    add dword [pgdt+2],0xc0000000              ;GDTR也用的是线性地址

    lgdt [pgdt]

    mov eax,cr0

    or eax,0x80000000

    mov cr0,eax                                              ;开启分页机制

    ;调整堆栈指针 

    add esp, 0xc0000000

  

    ;测试一下页管理开启情况

    mov eax, paged

    add eax, 0xc0000000

    jmp eax

paged:

    mov byte [0xa0002], 0x55                        ;显示白点

        

   ;更新系统进程的页表1

    mov eax, page_1_new_addr

    add eax, 0x3

    mov [0xfffff000], eax                                ;更新系统进程页表一的地址

    mov byte [0xc00a0004], 0x55                 ;显示白点

     

;-----------------------------进入系统开始执行----------------------------------

    ;直接跳到core代码起始处开始执行 

    mov eax, core_base_address

    add eax, 0xc0000000

    jmp eax

        

;-----------------------------读取硬盘扇区函数----------------------------------

read_hard_disk_0:                                  ;从硬盘读取一个逻辑扇区

                                                                ;EAX=逻辑扇区号

                                                                ;DS:EBX=目标缓冲区地址

                                                                ;返回:EBX=EBX+512

         push eax

         push ecx

         push edx

     

         push eax

        

         mov dx,0x1f2

         mov al,1

         out dx,al                          ;读取的扇区数

         inc dx                             ;0x1f3

         pop eax

         out dx,al                          ;LBA地址7~0

         inc dx                             ;0x1f4

         mov cl,8

         shr eax,cl

         out dx,al                          ;LBA地址15~8

         inc dx                             ;0x1f5

         shr eax,cl

         out dx,al                          ;LBA地址23~16

         inc dx                             ;0x1f6

         shr eax,cl

         or al,0xe0                         ;第一硬盘  LBA地址27~24

         out dx,al

         inc dx                             ;0x1f7

         mov al,0x20                        ;读命令

         out dx,al

  .waits:

         in al,dx

         and al,0x88

         cmp al,0x08

         jnz .waits                         ;不忙,且硬盘已准备好数据传输

         mov ecx,256                        ;总共要读取的字数

         mov dx,0x1f0

  .readw:

         in ax,dx

         mov [ebx],ax

         add ebx,2

         loop .readw

         pop edx

         pop ecx

         pop eax

     

         ret        

;===============================================================================

;===                                                       该处开始全局变量                                                                      ===

;===============================================================================

    pgdt                     dw 0

                                dd gdt_base_address     ;GDT的物理/线性地址:32K处开始

;-------------------------------------------------------------------------------                             

    times 510-($-$$)  db 0

                                db 0x55,0xaa

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: