[024][x86汇编语言]第十三章 学习加载程序C13_mbr.asm

学习笔记

《x86汇编语言:从实模式到保护模式》
https://www.jianshu.com/p/d481cb547e9f

第十三章的 代码

  • 用户程序 c13.asm
  • 内核程序 c13_core.asm
  • 加载程序 c13_mbr.asm
第十三章的代码文件.png

主引导程序 C13_mbr.asm

  • 主引导程序 C13_mbr.asm 程序结构
    主引导程序 C13_mbr.asm 程序结构.png
  • 加载程序 加载完 内核程序 后的 GDT 布局

    加载程序 加载完内核后的GDT布局.png

  • 内存布局示意图

    内存布局示意图.png

完整源码 code_13_mbr.asm (增加注释)

;--------------------------------------------------------------------------
;代码清单13-1
;文件名:code_13_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:8:43 2018/6/1
;--------------------------------------------------------------------------


;--------------------------------------------------------------------------
;常数
;--------------------------------------------------------------------------
core_base_address equ 0x00040000    ;内核程序将要被加载到的物理地址
core_start_sector equ 0x00000001    ;内核程序位于硬盘的起始逻辑扇区号

;--------------------------------------------------------------------------
;加载程序开始
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;                   所有数据都是十六进制
;   表内偏移量           GDT                         描述符索引
;   +38         核心代码段(位于核心数据段之后)        0x38
;   +30         核心数据段(位于系统公用例程段之后)  0x30
;   +28         公用例程段(起始地址为00040000)        0x28
;   +20         文本模式显存(000B8000~00BFFFFF)       0x20
;   +18         初始栈段(00006C00~00007C00)         0x18
;   +10         初始代码段(00007C00~00007DFF)            0x10
;   +08         0~4GB数据段(00000000~FFFFFFFF)     0x08
;   +00         空描述符                                0x00
;--------------------------------------------------------------------------
;加载GDT表 偏移+00~+20 5个项目
;--------------------------------------------------------------------------
    mov ax,cs
    mov ss,ax
    mov sp,0x7c00

    ;计算GDT所在的逻辑段地址
    mov eax,[cs:pdgt+0x7c00+0x02]       ;GDT的32位物理地址
    xor edx,edx
    mov ebx,16
    div ebx                             ;分解成16位逻辑地址
    
    mov ds,eax              ;令DS指向该段以进行操作
    mov ebx,edx             ;段内起始偏移地址
    
    ;跳过0#号描述符的槽位(空描述符)
    ;创建1#描述符,这是一个数据段,对应0~4G的线性地址空间
    mov dword [ebx+0x08],0x0000ffff     ;段基址0x00000000
    mov dword [ebx+0x0c],0x00cf9200     

    ;创建保护模式下初始代码段描述符
    mov dword [ebx+0x10],0x7c0001ff     ;段基址0x00007c00
    mov dword [ebx+0x14],0x00409800
    
    ;建立保护模式下的堆栈段描述符
    mov dword [ebx+0x18],0x7c00fffe     ;段基址0x00007c00
    mov dword [ebx+0x1c],0x00cf9600
    
    ;建立保护模式下的显示缓冲区描述符
    mov dword [ebx+0x20],0x80007fff     ;段基址0x000B8000
    mov dword [ebx+0x24],0x0040920b
    
    ;初始化描述符表寄存器GDTR
    mov word [cs:pdgt+0x7c00],39        ;5*8-1=39
    
    lgdt [cs:pdgt+0x7c00]
    
    in al,0x92                          ;南桥芯片内的端口
    or al,0000_0010B
    out 0x92,al                         ;打开A20
    
    cli                                 ;保护模式下中断机制尚未建立,应该禁止中断
    
    mov eax,cr0
    or eax,1
    mov cr0,eax                         ;设置PE位
    
;--------------------------------------------------------------------------
;以下进入保护模式
;设置 DS  指向 0~4GB内存空间
;       SS  指向堆栈段
;--------------------------------------------------------------------------
    jmp dword 0x0010:flush              ; 16位的描述符选择子:32位偏移
                                        ; dword修饰的是偏移地址
                                        ; 清空流水线并串行化处理器
    [bits 32]
flush:
    mov eax,0x0008                      ;加载(0...4GB)选择子
    mov ds,eax          
    
    mov eax,0x0018                      ;加载堆栈选择子
    mov ss,eax
    xor esp,esp                         ;堆栈指针 = 0
    
;--------------------------------------------------------------------------
;以下加载系统核心程序
;
;--------------------------------------------------------------------------
;读取位于硬盘的内核程序 调用子程序 read_hard_disk_0
;-------------------------------------------------------------------------- 
    mov edi,core_base_address
    
    mov eax,core_start_sector       
    mov ebx,edi                         
    call read_hard_disk_0               ;读取内核程序的起始部分(一个扇区)到内存
    
    ;以下判断整个内核程序有多大
    mov eax,[edi]                       ;内核程序长度
    xor edx,edx
    mov ecx,512
    div ecx
    
    or edx,edx
    jnz @1
    dec eax                     ;已经读了一个扇区,扇区总数减1
@1:
    or eax,eax
    jz setup                    ;jz ZF=1 寄存器值为零成立 则跳转
    
    ;读取剩余扇区
    mov ecx,eax
    mov eax,core_start_sector   ;32位模式下的LOOP使用ECX
    inc eax                     ;从下一个逻辑扇区接着读
@2:
    call read_hard_disk_0
    inc eax
    loop @2                     ;循环读,知道读完整个内核程序
    
;--------------------------------------------------------------------------
;                   所有数据都是十六进制
;   表内偏移量           GDT                         描述符索引
;   +38         核心代码段(位于核心数据段之后)        0x38
;   +30         核心数据段(位于系统公用例程段之后)  0x30
;   +28         公用例程段(起始地址为00040000)        0x28
;   +20         文本模式显存(000B8000~00BFFFFF)       0x20
;   +18         初始栈段(00006C00~00007C00)         0x18
;   +10         初始代码段(00007C00~00007DFF)            0x10
;   +08         0~4GB数据段(00000000~FFFFFFFF)     0x08
;   +00         空描述符                                0x00
;--------------------------------------------------------------------------
;再加载GDT表 偏移+28~+38 3个项目
;--------------------------------------------------------------------------
;********************** 内核程序 c13_core.asm ****************************
;*  ;以下是系统核心的头部,用于加载核心程序                             
;*  core_length      dd core_end                ;核心程序总长度#00;         
;*                                                                       
;*  sys_routine_seg  dd section.sys_routine.start                        
;*                                              ;系统公用例程段位置#04   
;*
;*  core_data_seg    dd section.core_data.start
;*                                              ;核心数据段位置#08
;*
;*  core_code_seg    dd section.core_code.start
;*                                              ;核心代码段位置#0c
;*
;*  core_entry      dd start                    ;核心代码段入口点#10
;*                  dw core_code_seg_sel
;*
;*********************** 内核程序 c13_core.asm ****************************
;--------------------------------------------------------------------------
setup:
    
    mov esi,[0x7c00+pdgt+0x02]  ;使用0~4GB的段来访问,寻址pdgt
    
    ;建立公用例程段描述符     ;mov edi,core_base_address
    mov eax,[edi+0x04]          ;公用例程段 在内核程序中的起始汇编地址
    mov ebx,[edi+0x08]          ;核心数据段 在内核程序中的起始汇编地址
    sub ebx,eax
    dec ebx                     ;公用例程段的 段界限
    add eax,edi                 ;公用例程段的 段基地址
    mov ecx,0x00409800          ;字节粒度的代码段描述符
    call make_gdt_descriptor
    mov [esi+0x28],eax
    mov [esi+0x2c],edx
    

    
    ;建立核心数据段描述符
    mov eax,[edi+0x08]          ;核心数据段 起始汇编地址
    mov ebx,[edi+0x0c]          ;核心代码段 起始汇编地址
    sub ebx,eax
    dec ebx                     ;核心数据段 段界限
    add eax,edi                 ;核心数据段 基地址
    mov ecx,0x00409200          ;字节粒度的数据段描述符
    call make_gdt_descriptor
    mov [esi+0x30],eax
    mov [esi+0x34],edx

    ;建立核心代码段描述符
    mov eax,[edi+0x0c]          ;核心代码段 起始汇编地址
    mov ebx,[edi+0x00]          ;程序总长度
    sub ebx,eax
    dec ebx                     ;核心代码段 段界限
    add eax,edi                 ;核心代码段  段基址
    mov ecx,0x00409800          ;字节粒度的代码段描述符
    call make_gdt_descriptor
    mov [esi+0x38],eax
    mov [esi+0x3c],edx
    
    mov word [0x7c00+pdgt],63   ;8*8-1=63
    lgdt [0x7c00+pdgt]
    

    
;--------------------------------------------------------------------------
;转移控制权给内核程序
;-------------------------------------------------------------------------- 
    jmp far [edi+0x10]

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

;--------------------------------------------------------------------------
;子程序:   read_hard_disk_0
;--------------------------------------------------------------------------
;功能:        从硬盘读取一个逻辑扇区
;参数:        EAX = 逻辑扇区号
;           DS:EBX=目标缓冲区地址
;返回:        EBX = EBX + 512 
;-------------------------------------------------------------------------- 
read_hard_disk_0:
    
            push eax
            push ecx
            push edx
        
            ;设置28位起始LBA扇区号
            push eax
        
            mov dx,0x1f2    ;0x1f2
            mov al,1        ;读取的扇区数为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    ;0x1f0
    .readw:
            in ax,dx
            mov [ebx],ax
            add ebx,2
            loop .readw
            
            pop edx
            pop ecx
            pop eax
            
ret


;--------------------------------------------------------------------------
;子程序:   make_gdt_descriptor
;--------------------------------------------------------------------------
;功能:    构造描述符
;输入:    EAX=线性基地址
;       EBX=段界限
;       ECX=属性(各属性都在原始位置,其他没用到的位置置为零)
;返回:    EDX:EAX=完整的描述符
;--------------------------------------------------------------------------
make_gdt_descriptor:
        
        mov edx,eax
        shl eax,16
        or ax,bx                    ;描述符低32位构造完毕
            
        and edx,0xffff0000          ;清除基地址中无关的位
        rol edx,8
        bswap   edx                 ;装配基地址的31~24和23~16(80486+)
        
        and ebx,0x000f0000
        or edx,ebx                  ;装配段界限的高4位
        
        or edx,ecx                  ;装配属性
                                    ;描述符高32位构造完毕
ret
;--------------------------------------------------------------------------

;--------------------------------------------------------------------------
;表
;--------------------------------------------------------------------------
    pdgt    dw 0
            dd 0x00007e00       ;GDT的物理地址
;--------------------------------------------------------------------------
;主引导扇区标志
;--------------------------------------------------------------------------
    times 510-($-$$)    db 0
                        db 0x55,0xaa
;--------------------------------------------------------------------------
;加载程序结束
;--------------------------------------------------------------------------

代码说明

  • 加载程序mov eax,[edi]与内核程序core_length dd core_end

    在加载程序中获取内核程序长度

  • 加载程序 jmp far [edi+0x10] 与内核程序 core_entry dd start dw core_code_seg_sel

    转移控制权 从加载程序到内核程序.png

  • 描述符计算 make_gdt_descriptor 举例

    描述符计算 1

    描述符计算 2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容