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

自己动手做一个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.进入32位保护模式                                                      ===
;=== 2.启动页管理模式                                                        ===
;=== 3.将0-1M内存空间映射到0xc0000000-0xc0100000处                           ===
;===   -其中,系统代码在0xc0010000处开始;                                   ===
;===   -系统进程的页表1指向新页面,保障后面加载用户进程的顺利进行            ===
;=== 4.调整gdt、堆栈指针到0xc0000000以上                                     ===
;=== 5.跳转到系统代码开始运行                                                ===
;===============================================================================
;===============================================================================
;===                             该处用于保存常量                            ===
;===============================================================================
         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                              ;页硬件地址再加上存在位。0x3为RW=1,P=1
         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

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值