【OS学习笔记】二十 保护模式六:保户模式下操作系统内核如何加载用户程序并运行 对应的汇编代码之主引导扇区程序
发布日期:2021-07-01 00:05:43 浏览次数:2 分类:技术文章

本文共 7156 字,大约阅读时间需要 23 分钟。

本汇编代码对应 的实际主引导扇区代码:

  • 对应的内核代码在:
  • 对应的用户程序代码在:
;代码清单13-1         ;文件名:c13_mbr.asm         ;文件说明:硬盘主引导扇区代码          ;创建日期:2011-10-28 22:35        ;设置堆栈段和栈指针                   core_base_address equ 0x00040000   ;常数,内核加载的起始内存地址          core_start_sector equ 0x00000001   ;常数,内核的起始逻辑扇区号                   mov ax,cs               mov ss,ax         mov sp,0x7c00               ;计算GDT所在的逻辑段地址         mov eax,[cs:pgdt+0x7c00+0x02]      ;GDT的32位物理地址          xor edx,edx         mov ebx,16         div ebx                            ;分解成16位逻辑地址          mov ds,eax                         ;令DS指向该段以进行操作         mov ebx,edx                        ;段内起始偏移地址          ;跳过0#号描述符的槽位          ;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间         mov dword [ebx+0x08],0x0000ffff    ;基地址为0,段界限为0xFFFFF         mov dword [ebx+0x0c],0x00cf9200    ;粒度为4KB,存储器段描述符          ;创建保护模式下初始代码段描述符         mov dword [ebx+0x10],0x7c0001ff    ;基地址为0x00007c00,界限0x1FF          mov dword [ebx+0x14],0x00409800    ;粒度为1个字节,代码段描述符          ;建立保护模式下的堆栈段描述符      ;基地址为0x00007C00,界限0xFFFFE          mov dword [ebx+0x18],0x7c00fffe    ;粒度为4KB          mov dword [ebx+0x1c],0x00cf9600                  ;建立保护模式下的显示缓冲区描述符            mov dword [ebx+0x20],0x80007fff    ;基地址为0x000B8000,界限0x07FFF          mov dword [ebx+0x24],0x0040920b    ;粒度为字节                  ;初始化描述符表寄存器GDTR         mov word [cs: pgdt+0x7c00],39      ;描述符表的界限             lgdt [cs: pgdt+0x7c00]               in al,0x92                         ;南桥芯片内的端口          or al,0000_0010B         out 0x92,al                        ;打开A20         cli                                ;中断机制尚未工作         mov eax,cr0         or eax,1         mov cr0,eax                        ;设置PE位               ;以下进入保护模式... ...         jmp dword 0x0010:flush             ;16位的描述符选择子:32位偏移                                            ;清流水线并串行化处理器         [bits 32]                 flush:                                           mov eax,0x0008                     ;加载数据段(0..4GB)选择子         mov ds,eax               mov eax,0x0018                     ;加载堆栈段选择子          mov ss,eax         xor esp,esp                        ;堆栈指针 <- 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                        ;512字节每扇区         div ecx         or edx,edx         jnz @1                             ;未除尽,因此结果比实际扇区数少1          dec eax                            ;已经读了一个扇区,扇区总数减1    @1:         or eax,eax                         ;考虑实际长度≤512个字节的情况          jz setup                           ;EAX=0 ?         ;读取剩余的扇区         mov ecx,eax                        ;32位模式下的LOOP使用ECX         mov eax,core_start_sector         inc eax                            ;从下一个逻辑扇区接着读   @2:         call read_hard_disk_0         inc eax         loop @2                            ;循环读,直到读完整个内核  setup:         mov esi,[0x7c00+pgdt+0x02]         ;不可以在代码段内寻址pgdt,但可以                                            ;通过4GB的段来访问         ;建立公用例程段描述符         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+pgdt],63          ;描述符表的界限                                                 lgdt [0x7c00+pgdt]                           jmp far [edi+0x10]         ;-------------------------------------------------------------------------------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;-------------------------------------------------------------------------------make_gdt_descriptor:                     ;构造描述符                                         ;输入:EAX=线性基地址                                         ;      EBX=段界限                                         ;      ECX=属性(各属性位都在原始                                         ;      位置,其它没用到的位置0)                                          ;返回:EDX:EAX=完整的描述符         mov edx,eax         shl eax,16                              or ax,bx                        ;描述符前32位(EAX)构造完毕               and edx,0xffff0000              ;清除基地址中无关的位         rol edx,8         bswap edx                       ;装配基址的31~24和23~16  (80486+)               xor bx,bx         or edx,ebx                      ;装配段界限的高4位               or edx,ecx                      ;装配属性                ret      ;-------------------------------------------------------------------------------         pgdt             dw 0                          dd 0x00007e00      ;GDT的物理地址;-------------------------------------------------------------------------------                                      times 510-($-$$) db 0                          db 0x55,0xaa

转载地址:https://lyy-0217.blog.csdn.net/article/details/84996777 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【OS学习笔记】二十一 保护模式六:保户模式下操作系统内核如何加载用户程序并运行 对应的汇编代码之内核代码
下一篇:【OS学习笔记】十九 保护模式六:保户模式下操作系统内核如何加载用户程序并运行

发表评论

最新留言

不错!
[***.144.177.141]2024年04月14日 06时45分27秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章