boot.S详细注释

 

深入理解 GNU GRUB - 02 boot.S 2.4 boot.S详细注释

分类: Bootloader   239人阅读  评论(0)  收藏  举报

转载注明出处(cppgp: http://blog.csdn.net/cppgp )

 

boot.S位于grub-1.98/boot/i386/pc/目录,采用AT&T汇编语法编写。

 

[c-sharp]  view plain copy
  1. /* -*-Asm-*- */  
  2. /* 
  3.  *  GRUB  --  GRand Unified Bootloader 
  4.  *  Copyright (C) 1999,2000,2001,2002,2005,2006,2007,2008,2009  Free Software Foundation, Inc. 
  5.  * 
  6.  *  GRUB is free software: you can redistribute it and/or modify 
  7.  *  it under the terms of the GNU General Public License as published by 
  8.  *  the Free Software Foundation, either version 3 of the License, or 
  9.  *  (at your option) any later version. 
  10.  * 
  11.  *  GRUB is distributed in the hope that it will be useful, 
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  14.  *  GNU General Public License for more details. 
  15.  * 
  16.  *  You should have received a copy of the GNU General Public License 
  17.  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>. 
  18.  */  
  19. /* 
  20.  * cppgp 注释 
  21.  * 
  22.  * 转载请注明原作者 
  23.  * 
  24.  * 日期: 2011-04-22 
  25.  * 
  26.  * Email 
  27.  *    cppgp@qq.com, 
  28.  *    yanyg02@163.com 
  29.  * 
  30.  * GRUB version: 
  31.  *    gnu grub-1.98 
  32.  */  
  33. /* 
  34.  * boot.S 
  35.  * 
  36.  * boot.S生成boot.img, 共512字节 
  37.  * 
  38.  * 安装程序根据实际情况, 改写kernel_sector所在LBA地址, 
  39.  * kernel_sector占用8字节, 首先是低4字节,然后是高4字节, 
  40.  * 它指明diskboot.img所在绝对扇区位置. 
  41.  * 
  42.  * 安装在硬盘上时,DPT部分(0x1BE~0x1FD)保留不变,   
  43.  * 
  44.  * 安装在软盘上时, DPT部分(0x1BE~0x1FD)是软盘复位和 
  45.  * 扇区探测代码. 扇区末尾两字节 
  46.  * 
  47.  * 扇区最后两字节 写入0xAA55 (小端表示, 0x1FE位置为0x55, 
  48.  * 0x1FF位置为0xAA). 
  49.  * 
  50.  * boot.img开机时加载到0x7C00~0x7DFF, 并以CS:IP=0x0000:0x7C00 
  51.  * 跳转执行, 它将加载diskboot.img到内存0x8000位置,并以 
  52.  * CS:IP=0x0000:0x8000跳转执行. 
  53.  * 
  54.  * boot.img保存磁盘参数和模式到BPB(BIOS parameter block)块. 
  55.  * diskboot.img和kernel.img都用到这些参数. 
  56.  * 
  57.  * boot.img设置堆栈和一些寄存器, diskboot.img和kernel.img都 
  58.  * 使用到这些设置. 包括: 
  59.  *  SP=0x2000 --> stack 
  60.  *  DL=boot-disk-driver 
  61.  *  SI=DAP --> disk address packet, -1(%si) is mode of disk read 
  62.  *  DS=SS=0 --> data segment and stack segment 
  63.  */  
  64. /* 
  65.   * 
  66.   * 关于宏LOCAL 
  67.   * 
  68.   * 所在文件:   grub-1.98/include/grub/symbol.h 
  69.   * 
  70.   * 描述 
  71.   *  #define LOCAL(sym) L_ ## sym 
  72.   *      ##在C语言中其粘贴作用 
  73.   *      LOCAL(sym)在sym符号前附加L_ 
  74.   *      它以一种更易读的方式暗示这是一个局部(本地)符号 
  75.   *      例如, LOCAL(after_BPB)等价于L_after_BPB 
  76.   * 
  77.   */  
  78.    
  79. #include <grub/symbol.h>  
  80. #include <grub/boot.h>  
  81. #include <grub/machine/boot.h>  
  82. /* 
  83.  *  defines for the code go here 
  84.  */  
  85.     /* Print message string */  
  86. /* 
  87.  * MSG宏设置源串指针并调用LOCAL(message)函数 
  88.  *    LOCAL(message)函数通过BIOS INT 10H, AH=0EH 
  89.  *    向终端输出提示信息 
  90.  * 关于BIOS INT 10H, AH=0EH参考2.1.6节 
  91.  */  
  92. #define MSG(x)  movw $x, %si; call LOCAL(message)  
  93.     /* 汇编文件名*/  
  94.     .file   "boot.S"  
  95.     /* 代码段声明*/  
  96.     .text  
  97.     /* Tell GAS to generate 16-bit instructions so that this code works 
  98.        in real mode. */  
  99.     /* 
  100.      * 告知编译器生成实模式下工作的16位指令 
  101.      */  
  102.     .code16  
  103. /* 
  104.  * .globl指令用来声明外部程序可以访问的标签 
  105.  *     程序入口点必须用.globl来声明 
  106.  *     _start是GNU链接器的默认入口点 
  107.  */  
  108. .globl _start, start;  
  109. _start:  
  110. start:  
  111.     /* 
  112.      * _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00 
  113.      */  
  114.     /* 
  115.      * Beginning of the sector is compatible with the FAT/HPFS BIOS 
  116.      * parameter block. 
  117.      */  
  118.     /* 
  119.      * 
  120.      * 跳转到LOCAL(after_BPB) 
  121.      *  LOCAL(after_BPB)之前空间保留 
  122.      *  用来保存磁盘读模式, 以及 
  123.      *  LBA模式下的参数DAP结构体空间, 或者 
  124.      *  CHS模式下保存柱面/磁头/扇区参数值 
  125.      * 
  126.      *  BPB保留的空间足够多 
  127.      *  目前最多只用到17字节, 分别是 
  128.      *    1字节保存磁盘读模式(LBA模式为1,  CHS模式为0) 
  129.      *    16字节保存LBA下的DAP参数 
  130.      * 
  131.      */  
  132.     jmp LOCAL(after_BPB)  
  133.     /* 
  134.      * nop占用1字节空间, 空指令 
  135.      */  
  136.     nop /* do I care about this ??? */  
  137.     /* 
  138.      * This space is for the BIOS parameter block!!!!  Don't change 
  139.      * the first jump, nor start the code anywhere but right after 
  140.      * this area. 
  141.      */  
  142.     /* 
  143.      * GRUB_BOOT_MACHINE_BPB_START=0x03 
  144.      * BPB参数块开始地址: 0x7C04 
  145.      */  
  146.     . = _start + GRUB_BOOT_MACHINE_BPB_START  
  147.     . = _start + 4  
  148.     /* scratch space */  
  149.     /* 
  150.      * mode保存磁盘读模式 
  151.      */  
  152. mode:  
  153.     .byte   0  
  154.     /* 
  155.      * disk_address_packet, BIOS LBA读DAP标签 
  156.      * sectors/heads/cylinders 保存驱动器C/H/S参数 
  157.      *    通过INT 13H, AH=0x08H获取 
  158.      * sector_start/head_start/cylinder_start 
  159.      *    代码中没有用到, 可能是残留下来的标签吧 
  160.      */  
  161. disk_address_packet:  
  162. sectors:  
  163.     .long   0  
  164. heads:  
  165.     .long   0  
  166. cylinders:  
  167.     .word   0  
  168. sector_start:  
  169.     .byte   0  
  170. head_start:  
  171.     .byte   0  
  172. cylinder_start:  
  173.     .word   0  
  174.     /* more space... */  
  175.     /* 
  176.      * GRUB_BOOT_MACHINE_BPB_END=0x5A 
  177.      * 用来给BPB预留更多空间 
  178.      * BPB字节数=0x5A-0x04=0x56=86 Bytes 
  179.      */  
  180.     . = _start + GRUB_BOOT_MACHINE_BPB_END  
  181.     /* 
  182.      * End of BIOS parameter block. 
  183.      */  
  184. /* 
  185.  * GRUB_BOOT_MACHINE_KERNEL_ADDR=0x8000 
  186.  *  GRUB第二步指令装载后位于这个位置 
  187.  */  
  188. kernel_address:  
  189.     .word   GRUB_BOOT_MACHINE_KERNEL_ADDR  
  190.     /* 
  191.      * GRUB_BOOT_MACHINE_KERNEL_SECTOR=0x5C 
  192.      *   确保到达此处时占用字节数等于0x5C 
  193.      *   如果超过编译器会发出抱怨 
  194.      *   如果不够0x5C则预留至0x5C 
  195.      */  
  196.     . = _start + GRUB_BOOT_MACHINE_KERNEL_SECTOR  
  197. /* 
  198.  * 8字节用来表示LBA绝对扇区地址 
  199.  *  boot.S加载位于此处的扇区到0x8000位置 
  200.  *  注意两个4字节值的顺序 
  201.  *  先是低4字节,再是高4字节 
  202.  *  默认值是LBA第1扇区 
  203.  *  在安装过程,安装程序会改写成实际的安装扇区 
  204.  */  
  205. kernel_sector:  
  206.     .long   1, 0  
  207.     /* 
  208.      * GRUB_BOOT_MACHINE_BOOT_DRIVE=0x64 
  209.      *  上面的kernel_sector使用0x5C~0x63 
  210.      *  此处只是确保没有越界 
  211.      *  如果越界编译器会发出警告 
  212.      */  
  213.     . = _start + GRUB_BOOT_MACHINE_BOOT_DRIVE  
  214. /* 
  215.  * 磁盘驱动器号 
  216.  *  第一块软盘是0x00, 第二块软盘是0x01, 依此类推 
  217.  *  第一块硬盘是0x80, 第二块硬盘是0x81, 依此类推 
  218.  *  默认设置为0xff 
  219.  *  在安装过程会更改为实际的磁盘驱动器 
  220.  *  代码中如果探测到这里依然是0xff, 则会当做0x80处理 
  221.  */  
  222. boot_drive:  
  223.     .byte 0xff  /* the disk to load kernel from */  
  224.             /* 0xff means use the boot drive */  
  225. /* 
  226.  * 0x7C00处的跳转到达这里 
  227.  */  
  228. LOCAL(after_BPB):  
  229. /* general setup */  
  230.     /* 
  231.      * cli 禁止中断 
  232.      */  
  233.     cli     /* we're not safe here! */  
  234.         /* 
  235.          * This is a workaround for buggy BIOSes which don't pass boot 
  236.          * drive correctly. If GRUB is installed into a HDD, check if 
  237.          * DL is masked correctly. If not, assume that the BIOS passed 
  238.          * a bogus value and set DL to 0x80, since this is the only 
  239.          * possible boot drive. If GRUB is installed into a floppy, 
  240.          * this does nothing (only jump). 
  241.          */  
  242.          /* 
  243.           * 确保地址安排GRUB_BOOT_MACHINE_DRIVE_CHECK=0x66 
  244.           * cli指令占用1字节, 现在正好到达0x66 
  245.           */  
  246.     . = _start + GRUB_BOOT_MACHINE_DRIVE_CHECK  
  247. /* 
  248.  * BIOS在开机后设置DL为引导磁盘驱动器号 
  249.  * 有些BIOS会传递错误的驱动器号 
  250.  *   硬盘驱动器号是从0x80开始的 
  251.  *   正确的驱动器号和0x80执行testb不会把ZF置0 
  252.  *   testb对两个操作数执行逻辑与操作, 
  253.  *   并设置正确的SF/ZF/PF等状态位 
  254.  *   testb $0x80, %dl等于0表示dl<0x80 
  255.  *   如果GRUB安装在硬盘上,这很明显是一个错误的驱动器号 
  256.  *   因此会重置为0x80 
  257.  * 
  258.  */  
  259. boot_drive_check:  
  260.     /* 
  261.      * 安装程序可能会改写jmp 
  262.      *  例如,安装在硬盘上时, 
  263.      *    可能改写为两个nop 
  264.      *    这样就可以解决一些BIOS传递错误驱动器号的BUG 
  265.      * 在当前版本中 
  266.      * 专门开辟了一字节空间(boot_drive)保存驱动器号 
  267.      * 除非系统上接入了128块硬盘,  
  268.      * 且使用最后一块硬盘引导(此时驱动器号为0xFF) 
  269.      * 否则后面的代码会重置DL为boot_drive中保存的驱动器号 
  270.      * 系统上接入128块硬盘且使用最后一块引导的概率非常低 
  271.      * 再者,如果真使用0xFF引导, 即使检测到有BUG且改成了0x80, 
  272.      * 也解决不了问题--因为实际上引导盘不是0x80而是0xFF 
  273.      * 
  274.      * 因此,是否改写jmp有些无关紧要. 
  275.      * 为了完整性, 我对这个检测的代码逻辑也做了注释 
  276.      */  
  277.         jmp     1f  /* grub-setup may overwrite this jump */  
  278.         /* 
  279.          * 这几行代码是针对安装在硬盘上的GRUB的 
  280.          * 用来解决某些BIOS无法提供正确驱动器号的BUG 
  281.          * testb结果: 如果dl<0x80则操作结果为0, 因此jnz不执行跳转 
  282.          * movb重置DL为0x80 
  283.          * 
  284.          */  
  285.         testb   $0x80, %dl  
  286.         jnz     1f  
  287.         movb    $0x80, %dl  
  288. 1:  
  289.     /* 
  290.      * ljmp to the next instruction because some bogus BIOSes 
  291.      * jump to 07C0:0000 instead of 0000:7C00. 
  292.      */  
  293.     /* 
  294.      * 某些存在BUG的BIOS,  
  295.      * 使用CS:IP=0x07C0:0x0000执行代码 
  296.      * 通过ljmp解决这个BUG 
  297.      * ljmp segment, offset 
  298.      * 其中segment是段寄存器, offset是偏移值 
  299.      */  
  300.     ljmp    $0, $real_start  
  301. real_start:  
  302.     /* set up %ds and %ss as offset from 0 */  
  303.     /* 
  304.      * 数据段/堆栈段寄存器置0 
  305.      * 前面通过ljmp, 已经确保数据段寄存器置0了 
  306.      */  
  307.     xorw    %ax, %ax  
  308.     movw    %ax, %ds  
  309.     movw    %ax, %ss  
  310.     /* set up the REAL stack */  
  311.     /* 
  312.      * GRUB_BOOT_MACHINE_STACK_SEG=0x2000 
  313.      * 设置实模式下的堆栈 
  314.      */  
  315.     movw    $GRUB_BOOT_MACHINE_STACK_SEG, %sp  
  316.     /* 打开中断 */  
  317.     sti     /* we're safe again */  
  318.     /* 
  319.      * 现在CS/DS/SS都是0x0000 
  320.      * 并且设置好了实模式堆栈SP=0x2000 
  321.      * 下面可以安全的使用pushx/popx等指令了 
  322.      */  
  323.     /* 
  324.      *  Check if we have a forced disk reference here 
  325.      */  
  326.     /* 
  327.      * 如果boot_drive依然为0xff, 则使用BIOS传递的驱动器号 
  328.      * 否则使用boot_drive中保存的驱动器号 
  329.      * 这样就允许GRUB的第一步和后续不在同一块磁盘也能工作 
  330.      * 
  331.      */  
  332.     movb   boot_drive, %al  
  333.     cmpb    $0xff, %al  
  334.     je  1f  
  335.     movb    %al, %dl  
  336. 1:  
  337.     /* save drive reference first thing! */  
  338.     /* 
  339.      * 压栈DX, 
  340.      * DL中保存有驱动器号 
  341.      * BIOS调用都有可能会更改DL寄存器 
  342.      */  
  343.     pushw   %dx  
  344.     /* print a notification message on the screen */  
  345.     /* 
  346.      * 向终端输出提示信息notification_string 
  347.      * notification_string="GRUB" 
  348.      * 如果注意,可以看到在启动GRUB引导的Linux时, 
  349.      * 屏幕上有GRUB字样一闪而过. 
  350.      */  
  351.     MSG(notification_string)  
  352.     /* set %si to the disk address packet */  
  353.     /* 
  354.      * 设置SI寄存器 
  355.      * 下面的BIOS调用及结果保存都会用到 
  356.      * movw将disk_address_packet地址保存到SI 
  357.      */  
  358.     movw    $disk_address_packet, %si  
  359.     /* do not probe LBA if the drive is a floppy */  
  360.     /* 
  361.      * 探测如果驱动器小于0x80, 不再执行LBA探测, 
  362.      *  因为小于0x80为软盘,软盘只支持CHS. 
  363.      */  
  364.     testb   $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl  
  365.     jz  LOCAL(chs_mode)  
  366.     /* check if LBA is supported */  
  367.     /* 
  368.      * BIOS INT 13H, AH=41H 
  369.      * 检测磁盘扩展读支持情况 
  370.      * 详细查看2.1.1节 
  371.      */  
  372.     movb    $0x41, %ah  
  373.     movw    $0x55aa, %bx  
  374.     int $0x13  
  375.     /* 
  376.      *  %dl may have been clobbered by INT 13, AH=41H. 
  377.      *  This happens, for example, with AST BIOS 1.04. 
  378.      */  
  379.      /* 
  380.       * 驱动器DL重置, 因为BIOS调用可能已经更改了它 
  381.       */  
  382.     popw    %dx  
  383.     pushw   %dx  
  384.     /* use CHS if fails */  
  385.     /* 
  386.      * 检测结果, 如果支持LBA,则有: 
  387.      *  1. CF清零; 2. BX==AA55H; 3. CX=1 
  388.      * 
  389.      * 如果BIOS调用失败按照CHS处理 
  390.      */  
  391.     jc  LOCAL(chs_mode)  
  392.     cmpw    $0xaa55, %bx  
  393.     jne LOCAL(chs_mode)  
  394.     andw    $1, %cx  
  395.     jz  LOCAL(chs_mode)  
  396. lba_mode:  
  397. /* 
  398.  * 初步探测支持LBA扩展读 
  399.  * 如果LBA读失败依然进入CHS处理 
  400.  */  
  401.     /* 
  402.      * 如下代码设置LBA读的DAP结构及寄存器 
  403.      * 并调用LBA读BIOS例程 
  404.      * 如果读失败进入CHS处理 
  405.      * 成功则数据被读到了0x7000:0x0000(内存0x700000)位置 
  406.      * 跳转到LOCAL(copy_buffer)拷贝至0x8000并跳转执行 
  407.      */  
  408.      /* 
  409.       * BIOS INT 13H, AH=42H 
  410.       * LBA读 
  411.       * 详细查看2.1.2节 
  412.       */  
  413.     xorw    %ax, %ax  
  414.     movw    %ax, 4(%si)  
  415.     // 支持LBA读,保存LBA读到mode地址,LBA读标记为0  
  416.     /* 
  417.      * 保存硬盘读模式为LBA 
  418.      * mode保存1表示支持LBA 
  419.      */  
  420.     incw    %ax  
  421.     /* set the mode to non-zero */  
  422.     movb    %al, -1(%si)  
  423.     // 设置BIOS调用参数  
  424.     /* 
  425.      * 以下是填充LBA读的DAP结构 
  426.      * 2.1.2节对此有详细描述 
  427.      */  
  428.     /* the blocks */  
  429.     /* 
  430.      * 读取扇区数,只读1扇区 
  431.      */  
  432.     movw    %ax, 2(%si)  
  433.     /* the size and the reserved byte */  
  434.     /* 
  435.      * DAP结构体大小,当前为16=0x10 
  436.      * 把需要读取扇区数的高字节置0 
  437.      */  
  438.     movw    $0x0010, (%si)  
  439.     /* the absolute address */  
  440.     /* 
  441.      * 8字节的LBA绝对扇区地址 
  442.      */  
  443.     movl    kernel_sector, %ebx  
  444.     movl    %ebx, 8(%si)  
  445.     movl    kernel_sector + 4, %ebx  
  446.     movl    %ebx, 12(%si)  
  447.     /* the segment of buffer address */  
  448.     /* 
  449.      * GRUB_BOOT_MACHINE_BUFFER_SEG=0x7000 
  450.      * 缓冲区段地址 
  451.      */  
  452.     movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)  
  453. /* 
  454.  * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory 
  455.  *  Call with   %ah = 0x42 
  456.  *          %dl = drive number 
  457.  *          %ds:%si = segment:offset of disk address packet 
  458.  *  Return: 
  459.  *          %al = 0x0 on success; err code on failure 
  460.  */  
  461.     /* 
  462.      * 调用INT 13H, AH=42H执行LBA读 
  463.      *   缓冲区segment:offset=0x7000:0x0000 
  464.      */  
  465.     movb    $0x42, %ah  
  466.     int $0x13  
  467.     /* 
  468.      * LBA读失败跳转到LOCAL(chs_mode)尝试CHS读 
  469.      */  
  470.     /* LBA read is not supported, so fallback to CHS.  */  
  471.     jc  LOCAL(chs_mode)  
  472.     /* 
  473.      * GRUB_BOOT_MACHINE_BUFFER_SEG=0x7000 
  474.      * 设置BX为缓冲区段地址0x7000 
  475.      *  跳转执行LOCAL(copy_buffer) 
  476.      *  它将搬运0x7000:0x0000处一扇区的数据到0x8000处 
  477.      */  
  478.     movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx  
  479.     jmp LOCAL(copy_buffer)  
  480. /* 
  481.  * CHS模式读处理 
  482.  *  1. BIOS INT 13H, AH=08H获取磁盘CHS参数 
  483.  *  2. BIOS INT 13H, AH=02H实现CHS读 
  484.  *  3. 可能尝试软盘驱动器复位和CHS读 
  485.  */  
  486. LOCAL(chs_mode):  
  487.     /* 
  488.      *  Determine the hard disk geometry from the BIOS! 
  489.      *  We do this first, so that LS-120 IDE floppies work correctly. 
  490.      */  
  491.     /* 
  492.      * 获取磁盘CHS参数 
  493.      * 有关该调用细节查看2.1.3节 
  494.      * 如果成功直接跳转到LOCAL(final_init) 
  495.      */  
  496.     movb    $8, %ah  
  497.     int $0x13  
  498.     jnc LOCAL(final_init)  
  499.     /* 
  500.      *  The call failed, so maybe use the floppy probe instead. 
  501.      */  
  502.     /* 
  503.      * GRUB_BOOT_MACHINE_BIOS_HD_FLAG=0x80 
  504.      *  检测如果驱动器DL小于0x80, 
  505.      *  则尝试软盘复位和读 
  506.      *  如果大于等于0x80, 表明硬盘错误,跳转进入LOCAL(hd_probe_error)处理 
  507.      */  
  508.     testb   $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl  
  509.     jz  LOCAL(floppy_probe)  
  510.     /* Nope, we definitely have a hard disk, and we're screwed. */  
  511.     jmp LOCAL(hd_probe_error)  
  512. LOCAL(final_init):  
  513.     /* set the mode to zero */  
  514.     /* 
  515.      * 保存磁盘CHS参数 
  516.      *  S索引从1开始,直接保存它即可 
  517.      *  柱面C使用寄存器CH表示低8位 
  518.      *               使用CL高两位表示高2位 
  519.      *  磁头H使用DH 
  520.      *  扇区S使用CL低6位 
  521.      *  注意,获取到的C/H/S都是最大索引 
  522.      *  C/H索引都是从0开始,因此它们的值应该加1保存 
  523.      *  有关细节见2.1.3节 
  524.      */  
  525.     /* 
  526.      * movzbl保存dh到al,同时eax其他三字节置0 
  527.      * movb将保存0到mode, 表示支持CHS读 
  528.      */  
  529.     movzbl  %dh, %eax  
  530.     movb    %ah, -1(%si)  
  531.     /* save number of heads */  
  532.     /* 
  533.      * 保存磁头参数H到BPB的heads 
  534.      * 磁头索引从0开始,递增表示磁盘磁头数 
  535.      * SI在前面已经设置成DAP地址了 
  536.      * 而CHS和DAP是复用的 
  537.      */  
  538.     incw    %ax  
  539.     movl    %eax, 4(%si)  
  540.     /* 
  541.      * 移位操作 
  542.      * 10位的柱面C保存到AX中 
  543.      */  
  544.     movzbw  %cl, %dx  
  545.     shlw    $2, %dx  
  546.     movb    %ch, %al  
  547.     movb    %dh, %ah  
  548.     /* save number of cylinders */  
  549.     /* 
  550.      * 柱面索引从0开始,递增表示磁盘柱面数 
  551.      */  
  552.     incw    %ax  
  553.     movw    %ax, 8(%si)  
  554.     /* 
  555.      * 上面保存柱面C时使用了DX 
  556.      * DX曾左移2位,因此: 
  557.      * DH低2位保存柱面8~9位 
  558.      * DL高6位保存扇区数 
  559.      * movzbw让AH为0,AL为DL 
  560.      * AL右移两位,正好表示扇区 
  561.      * 扇区索引从1开始,因此不需要递增 
  562.      */  
  563.     movzbw  %dl, %ax  
  564.     shrb    $2, %al  
  565.     /* save number of sectors */  
  566.     /* 
  567.      * 保存磁盘扇区数 
  568.      */  
  569.     movl    %eax, (%si)  
  570.     /* 
  571.      * 现在磁盘的C/H/S数已保存 
  572.      *      下面的读使用它们来判断需要读取的扇区 
  573.      *      是否越界(CHS 7.88Gib屏障,参考2.1节) 
  574.      * 
  575.      *  待读取的扇区采用LBA表示,占用8字节 
  576.      *      需要转换成C/H/S表示的方法, 
  577.      *      且确保在C/H/S寻址范围之内 
  578.      *  转换算法: 
  579.      * 
  580.      */  
  581. setup_sectors:  
  582.     /* load logical sector start (top half) */  
  583.     /* 
  584.      * LBA表示的高4字节地址加载到EAX 
  585.      * 如果EAX不为0, 则肯定超过CHS寻址范围 
  586.      * 因此直接报告错误 
  587.      */  
  588.     movl    kernel_sector + 4, %eax  
  589.     /* 
  590.      * orl没什么作用 
  591.      * EAX与自身执行或运算,EAX结果不会有变化 
  592.      * 但是会设置标识寄存器EFLAGS的某些位 
  593.      * 例如SF/ZF等 
  594.      * 如果EAX不等于0,则CHS越界,跳转到LOCAL(geometry_error) 
  595.      */  
  596.     orl %eax, %eax  
  597.     jnz LOCAL(geometry_error)  
  598.     /* load logical sector start (bottom half) */  
  599.     /* 
  600.      * LBA表示的低4字节地址 
  601.      */  
  602.     movl    kernel_sector, %eax  
  603.     /* zero %edx */  
  604.     /* 
  605.      * EDX清零,因为32位的div操作的被除数是EDX:EAX 
  606.      */  
  607.     xorl    %edx, %edx  
  608.     /* divide by number of sectors */  
  609.     /* 
  610.      * 操作数 
  611.      *   被除数: EDX:EAX=0:EAX 
  612.      *   除数: (%si) 磁盘扇区数S 
  613.      * 结果 
  614.      *   商: EAX 
  615.      *   余数: EDX 
  616.      */  
  617.     divl    (%si)  
  618.     /* save sector start */  
  619.     /* 
  620.      * 余数表示起始扇区 
  621.      * 保存到CL 
  622.      * 因为扇区起始索引为1,需要递增 
  623.      * 递增操作见稍后代码 
  624.      */  
  625.     movb    %dl, %cl  
  626.     /* 
  627.      * EAX是上次div的结果, 
  628.      * 再除以磁头数H, 
  629.      * 就可得到起始柱面Cylinder 
  630.      */  
  631.     /* 
  632.      * 清零DX 
  633.      * 在上面的divl操作中, 
  634.      *   除数是扇区数(6位) 
  635.      *   因此余数不会超过一字节 
  636.      *   清零DX则EDX全部为0 
  637.      */  
  638.     xorw    %dx, %dx    /* zero %edx */  
  639.     /* 
  640.      * 操作数 
  641.      *   被除数: EDX:EAX=0:EAX 
  642.      *   除数: 4(%si) 磁盘磁头数H 
  643.      * 结果 
  644.      *   商: EAX 
  645.      *   余数: EDX 
  646.      */  
  647.     divl    4(%si)      /* divide by number of heads */  
  648.     /* 
  649.      * 现在, 
  650.      *  AX中保存有起始柱面C 
  651.      *  DX中保存有起始磁头S 
  652.      */  
  653.     /* do we need too many cylinders? */  
  654.     /* 
  655.      * 判断起始柱面有否越界 
  656.      * 如果越界跳转到LOCAL(geometry_error) 
  657.      */  
  658.     cmpw    8(%si), %ax  
  659.     jge LOCAL(geometry_error)  
  660.     /* 
  661.      * 注意, 
  662.      *  众所周知,余数不可能超过除数 
  663.      *  上面的除法中除数是磁盘磁头数H 
  664.      *  因此起始磁头不会越界,不需要检测是否越界 
  665.      */  
  666.     /* normalize sector start (1-based) */  
  667.     /* 
  668.      * 扇区索引从1开始, 
  669.      * 因此起始扇区需要加1 
  670.      */  
  671.     incb    %cl  
  672.     /* 
  673.      * 现在 
  674.      *  AX=起始柱面C 
  675.      *  DX=起始磁头H 
  676.      *  CL=起始扇区S 
  677.      * 
  678.      * 下面通过移位运算,完成 
  679.      *    CHS读的寄存器设置 
  680.      * 详细查看2.1.4节 
  681.      * 
  682.      */  
  683.     /* low bits of cylinder start */  
  684.     /* 
  685.      * 保存起始柱面C的0~7位到CH 
  686.      */  
  687.     movb    %al, %ch  
  688.     /* high bits of cylinder start */  
  689.     /* 
  690.      * 保存起始柱面C的8~9位到CL高2位 
  691.      * CL中低6位已经包含合法的起始扇区 
  692.      * orb或操作起始柱面8~9位和起始扇区0~5位 
  693.      */  
  694.     xorb    %al, %al  
  695.     shrw    $2, %ax  
  696.     orb %al, %cl  
  697.     /* save head start */  
  698.     /* 
  699.      * divl操作后, 
  700.      *  Dl存有起始磁头,保存到AL中 
  701.      */  
  702.     movb    %dl, %al  
  703.     /* restore %dl */  
  704.     /* 
  705.      * DL值已被更改,还原磁盘驱动器值 
  706.      */  
  707.     popw    %dx  
  708.     /* head start */  
  709.     /* 
  710.      * 保存起始磁头到AH中 
  711.      */  
  712.     movb    %al, %dh  
  713. /* 
  714.  * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory 
  715.  *  Call with   %ah = 0x2 
  716.  *          %al = number of sectors 
  717.  *          %ch = cylinder 
  718.  *          %cl = sector (bits 6-7 are high bits of "cylinder") 
  719.  *          %dh = head 
  720.  *          %dl = drive (0x80 for hard disk, 0x0 for floppy disk) 
  721.  *          %es:%bx = segment:offset of buffer 
  722.  *  Return: 
  723.  *          %al = 0x0 on success; err code on failure 
  724.  */  
  725.     /* 
  726.      * GRUB_BOOT_MACHINE_BUFFER_SEG=0x7000 
  727.      *  缓冲区段地址 
  728.      */  
  729.     movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx  
  730.     movw    %bx, %es    /* load %es segment with disk buffer */  
  731.     /* 
  732.      * 调用INT 13H, AH=02H执行CHS读 
  733.      *   缓冲区segment:offset=0x7000:0x0000 
  734.      */  
  735.     xorw    %bx, %bx    /* %bx = 0, put it at 0 in the segment */  
  736.     movw    $0x0201, %ax    /* function 2 */  
  737.     int $0x13  
  738.     /* 
  739.      * 检测CHS读是否成功 
  740.      *  CHS读成功CF清零,失败则置1 
  741.      */  
  742.     jc  LOCAL(read_error)  
  743.     /* 
  744.      * 设置BX为缓冲区段地址 
  745.      */  
  746.     movw    %es, %bx  
  747. LOCAL(copy_buffer):  
  748.     /* 
  749.      * We need to save %cx and %si because the startup code in 
  750.      * kernel uses them without initializing them. 
  751.      */  
  752.     /* 
  753.      * 无论CHS或LBA,一扇区的内容都被放置在 
  754.      *  segment:offset=%bx:0处 
  755.      * 这里将数据搬运到0x0000:0x8000并跳转执行 
  756.      *  
  757.      */  
  758.     /* 
  759.      * 寄存器压栈, 因为 
  760.      *   数据搬运操作将使用这些寄存器 
  761.      *   而GRUB第二步将假定这些寄存器包含正确值 
  762.      *   并直接使用 
  763.      */  
  764.     pusha  
  765.     pushw   %ds  
  766.     /* 
  767.      * movsw指令: 
  768.      *  搬运两字节数据 
  769.      *  使用segment:offset=DS:SI作为源操作数 
  770.      *  使用segment:offset=ES:DI作为目的操作数 
  771.      * 
  772.      * rep指令: 
  773.      *  使用CX寄存器作为循环标记, 
  774.      *  循环执行紧跟其后的指令CX次 
  775.      */  
  776.     /* 
  777.      * GRUB_BOOT_MACHINE_KERNEL_ADDR=0x8000 
  778.      * 设置循环次数为100次 
  779.      * 设置DS:SI=0x7000:0x0000即CHS/LBA读的缓冲区 
  780.      * 设置ES:DI=0x0000:0x8000, 数据将搬运到这里 
  781.      */  
  782.     movw    $0x100, %cx  
  783.     movw    %bx, %ds  
  784.     xorw    %si, %si  
  785.     movw    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di  
  786.     movw    %si, %es  
  787.     /* 
  788.      * DF清零 
  789.      *  movsw自动增加源和目的操作数值 
  790.      *  清零后每次执行movsw, SI和DI都加2 
  791.      */  
  792.     cld  
  793.     /* 
  794.      * 循环搬运 
  795.      *  把0x7000:0x0000处的512字节搬运到0x0000:0x8000处 
  796.      */  
  797.     rep  
  798.     movsw  
  799.     /* 
  800.      * 还原寄存器 
  801.      */  
  802.     popw    %ds  
  803.     popa  
  804.     /* boot kernel */  
  805.     /* 
  806.      * 跳转到0x0000:0x8000执行 
  807.      * 这里存放的是刚才得到的指令数据 
  808.      * 一次成功的GRUB引导都会执行到这里 
  809.      * 此后将进入第二步执行 
  810.      * 这里设置的堆栈和保存的BPB数据 
  811.      *    在后来的第二步(diskboot.S)及第三步(startup.S)中还会用到 
  812.      */  
  813.     jmp *(kernel_address)  
  814. /* END OF MAIN LOOP */  
  815. /* 
  816.  * BIOS Geometry translation error (past the end of the disk geometry!). 
  817.  */  
  818.  /* 
  819.   * 以下是几个错误处理函数 
  820.   * 向终端输出错误提示信息并等待手动关机或重启 
  821.   */  
  822.     
  823. /* 
  824.  * 向终端输出错误提示"Geom"并跳转到LOCAL(general_error) 
  825.  * 磁盘物理结构错误(一般是C/H/S参数越界导致的错误) 
  826.  */  
  827. LOCAL(geometry_error):  
  828.     MSG(geometry_error_string)  
  829.     jmp LOCAL(general_error)  
  830. /* 
  831.  * Disk probe failure. 
  832.  */  
  833. /* 
  834.  * 向终端输出错误提示"Hard Disk"并跳转到LOCAL(general_error) 
  835.  * 不支持LBA或者LBA读失败的硬盘,在获取CHS参数时错误 
  836.  */  
  837. LOCAL(hd_probe_error):  
  838.     MSG(hd_probe_error_string)  
  839.     jmp LOCAL(general_error)  
  840. /* 
  841.  * Read error on the disk. 
  842.  */  
  843. /* 
  844.  * 向终端输出错误提示"Hard Disk"并跳转到LOCAL(general_error) 
  845.  * CHS读失败错误 
  846.  */  
  847. LOCAL(read_error):  
  848.     MSG(read_error_string)  
  849. /* 
  850.  * 向终端输出错误提示"Error/r/n" 
  851.  */  
  852. LOCAL(general_error):  
  853.     MSG(general_error_string)  
  854. /* go here when you need to stop the machine hard after an error condition */  
  855.         /* tell the BIOS a boot failure, which may result in no effect */  
  856.         /* 
  857.          * 向BIOS报告引导失败 
  858.          */  
  859.         int $0x18  
  860. /* 
  861.  * BIOS对引导失败没有响应到达这里 
  862.  * 此时用户需要手动关机/重启 
  863.  */  
  864. LOCAL(stop):  
  865.     jmp LOCAL(stop)  
  866. /* 
  867.  * 错误提示字符串 
  868.  */  
  869. notification_string:    .asciz "GRUB "  
  870. geometry_error_string:  .asciz "Geom"  
  871. hd_probe_error_string:  .asciz "Hard Disk"  
  872. read_error_string:  .asciz "Read"  
  873. general_error_string:   .asciz " Error/r/n"  
  874. /* 
  875.  * message: write the string pointed to by %si 
  876.  * 
  877.  *   WARNING: trashes %si, %ax, and %bx 
  878.  */  
  879. /* 
  880.  * 错误提示函数 
  881.  * 使用BIOS INT 10H, AH=0EH调用向终端输出错误提示字符串 
  882.  * 关于该中断细节查看2.1.6节 
  883.  */  
  884.     /* 
  885.      * Use BIOS "int 10H Function 0Eh" to write character in teletype mode 
  886.      *  %ah = 0xe   %al = character 
  887.      *  %bh = page  %bl = foreground color (graphics modes) 
  888.      */  
  889. 1:  
  890.     movw    $0x0001, %bx  
  891.     movb    $0xe, %ah  
  892.     int $0x10       /* display a byte */  
  893. LOCAL(message):  
  894.     /* 
  895.      * lodsb加载DS:SI指向的一字节数据到AL 
  896.      * 加载后自动更新SI值(指向下一字符) 
  897.      * 如果字符为'/0'返回, 
  898.      * 否则调用BIOS中断输出该字符 
  899.      */  
  900.     lodsb  
  901.     cmpb    $0, %al  
  902.     jne 1b  /* if not end of string, jmp to display */  
  903.     ret  
  904.     /* 
  905.      *  Windows NT breaks compatibility by embedding a magic 
  906.      *  number here. 
  907.      */  
  908.     /* 
  909.      * WindowsNT在这里插入一个幻数 
  910.      * 在Linux下, 把这个位置的6个字节清零 
  911.      * 不会导致任何问题 
  912.      * 这个测试详见2.3节末尾部分 
  913.      */  
  914.     . = _start + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC  
  915. nt_magic:  
  916.     .long 0  
  917.     .word 0  
  918.     /* 
  919.      *  This is where an MBR would go if on a hard disk.  The code 
  920.      *  here isn't even referenced unless we're on a floppy.  Kinda 
  921.      *  sneaky, huh? 
  922.      */  
  923.     /* 
  924.      * GRUB_BOOT_MACHINE_PART_START=0x1BE 
  925.      * 从这里开始是硬盘MBR的DPT区 
  926.      * 如果是安装在软盘,则这里存放 
  927.      * 软盘驱动器复位和CHS读取的指令 
  928.      */  
  929.     . = _start + GRUB_BOOT_MACHINE_PART_START  
  930. part_start:  
  931. /* 
  932.  * 解决软盘获取CHS参数错误的问题 
  933.  * 
  934.  * 一系列可能的软盘扇区参数. 即: 
  935.  *  软盘可能的最大扇区数 
  936.  *  当软盘的CHS参数获取调用失败以后, 
  937.  *  设置柱面和磁头均为0,然后 
  938.  *  遍历尝试读取可能的最大扇区 
  939.  *  成功以后, 设置CHS参数为C/H/S=0/0/CL 
  940.  *  并进入LOCAL(final_init)并执行正常的CHS流程 
  941.  * 
  942.  * 例如, 假设第一次复位/读测试成功,则 
  943.  *    软盘C/H/S=0/0/36. 可寻址范围36*512=18KiB 
  944.  */  
  945. probe_values:  
  946.     .byte   36, 18, 15, 9, 0  
  947. LOCAL(floppy_probe):  
  948. /* 
  949.  *  Perform floppy probe. 
  950.  */  
  951.     /* 
  952.      * LOCAL(probe_loop)循环中总是先递增SI 
  953.      * 然后取值,因此设置SI初始值为$probe_values-1 
  954.      * 这样可以防止错过probe_values中的第一个数据 
  955.      */  
  956.     movw    $probe_values - 1, %si  
  957. LOCAL(probe_loop):  
  958.     /* reset floppy controller INT 13h AH=0 */  
  959.     /* 
  960.      * 复位软盘驱动器: BIOS INT 13H, AH=00H 
  961.     */  
  962.     xorw    %ax, %ax  
  963.     int $0x13  
  964.     incw    %si  
  965.     movb    (%si), %cl  
  966.     /* if number of sectors is 0, display error and die */  
  967.     /* 
  968.      * 起始扇区是否为0 
  969.      * 如果为0则提示错误结束 
  970.      * 
  971.      * LOCAL(probe_loop)将依次遍历probe_values中设定的值, 
  972.      * 直到出现0则结束遍历, 并输出错误提示 
  973.      */  
  974.     cmpb    $0, %cl  
  975.     jne 1f  
  976. /* 
  977.  * Floppy disk probe failure. 
  978.  */  
  979. /* 
  980.  * 终端输出"Floppy"并跳转到LOCAL(general_error) 
  981.  */  
  982.     MSG(fd_probe_error_string)  
  983.     jmp LOCAL(general_error)  
  984. /* "Floppy" */  
  985. fd_probe_error_string:  .asciz "Floppy"  
  986. 1:  
  987.     /* perform read */  
  988.     /* 
  989.      * BIOS INT 13H, AH=02H读(和硬盘CHS读使用同一BIOS中断) 
  990.      *  设置CH为0, CL为起始扇区,柱面C为0 
  991.      *  设置CL中为probe_values中的其中一个 
  992.      *  设置DH为0, 磁头H为0 
  993.      *  设置AL为1, 只读取一扇区 
  994.      *  设置AH为2, BIOS INT 13H CHS读函数序号 
  995.      *  设置缓冲区segment:offset=ES:BX=0x0000:0x7000 
  996.      * INT 13H调用将读取软盘0柱面0磁头CL扇区 
  997.      *    如果读成功,跳转到LOCAL(final_init) 
  998.      *    将再次执行CHS读 
  999.      *    因此这里的缓冲区数据并不会被处理 
  1000.      */  
  1001.     movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx  
  1002.     movw    $0x201, %ax  
  1003.     movb    $0, %ch  
  1004.     movb    $0, %dh  
  1005.     int $0x13  
  1006.     /* if error, jump to "LOCAL(probe_loop)" */  
  1007.     /* 
  1008.      * 失败跳转回LOCAL(probe_loop)继续尝试 
  1009.      * 成功跳转到LOCAL(final_init) 
  1010.      * 下次讲尝试读取probe_values设定的另一扇区 
  1011.      * 如果probe_values所有值都读失败,提示错误信息 
  1012.      */  
  1013.     jc  LOCAL(probe_loop)  
  1014.     /* %cl is already the correct value! */  
  1015.     movb    $1, %dh  
  1016.     movb    $79, %ch  
  1017.     /* 
  1018.      * 幸运的,判断出0柱面0磁头CL扇区是可以工作的 
  1019.      * 因此跳转回LOCAL(final_init) 
  1020.      * 在那儿将保存软盘的CHS参数为C/H/S=0/0/CL 
  1021.      */  
  1022.     jmp LOCAL(final_init)  
  1023.     . = _start + GRUB_BOOT_MACHINE_PART_END  
  1024. /* the last 2 bytes in the sector 0 contain the signature */  
  1025. /* 
  1026.  * MBR幻数,小端(little endian) 下总是等于0xAA55 
  1027.  */  
  1028.     .word   GRUB_BOOT_MACHINE_SIGNATURE  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值