1. 初始化硬件信息存放空间

  • 先说在前,setup在0x90200位置
  • 代码:

    1. start:
    2. mov ax,#0x9000 ; this is done in bootsect already, but...
    3. mov ds,ax
    4. mov ah,#0x03 ; read cursor pos
    5. xor bh,bh
    6. int 0x10 ; save it in known place, con_init fetches
    7. mov [0],dx ; it from 0x90000.
  • 一开始setup就将ax寄存器放入0x9000地址,这里是之前bootsetc复制过来的位置,此时bootsetc没用了,这块空间将用于存放硬件信息

    2. 读取硬件信息

  • 通过中断操作读取硬件信息,int命令就是中断,中断会读取ah寄存器的数据,数据映射了所有中断操作,0x03就是读取光标的操作

  • 通过诸如此类的方法,读取其他硬件信息:

    1. 获取内存信息。
    2. ; Get memory size (extended mem, kB)
    3. mov ah,#0x88
    4. int 0x15
    5. mov [2],ax
    6. 获取显卡显示模式。
    7. ; Get video-card data:
    8. mov ah,#0x0f
    9. int 0x10
    10. mov [4],bx ; bh = display page
    11. mov [6],ax ; al = video mode, ah = window width
    12. 检查显示方式并取参数
    13. ; check for EGA/VGA and some config parameters
    14. mov ah,#0x12
    15. mov bl,#0x10
    16. int 0x10
    17. mov [8],ax
    18. mov [10],bx
    19. mov [12],cx
    20. 获取第一块硬盘的信息。
    21. ; Get hd0 data
    22. mov ax,#0x0000
    23. mov ds,ax
    24. lds si,[4*0x41]
    25. mov ax,#INITSEG
    26. mov es,ax
    27. mov di,#0x0080
    28. mov cx,#0x10
    29. rep
    30. movsb
    31. 获取第二块硬盘的信息。
    32. ; Get hd1 data
    33. mov ax,#0x0000
    34. mov ds,ax
    35. lds si,[4*0x46]
    36. mov ax,#INITSEG
    37. mov es,ax
    38. mov di,#0x0090
    39. mov cx,#0x10
    40. rep
    41. movsb
  • 最终读取结果如下: | 内存地址 | 长度(字节) | 名称 | | —- | —- | —- | | 0x90000 | 2 | 光标位置 | | 0x90002 | 2

    | 扩展内存数 | | 0x90004 | 2 | 显示页面 | | 0x90006 | 1

    | 显示模式 | | 0x90007 | 1 | 字符列数 | | 0x90008 | 2 | 未知 | | 0x9000A | 1

    | 显示内存 | | 0x9000B | 1

    | 显示状态 | | 0x9000C | 2 | 显卡特性参数 | | 0x9000E | 1

    | 屏幕行数 | | 0x9000F | 1 | 屏幕列数 | | 0x90080 | 16

    | 硬盘1参数表 | | 0x90090 | 16 | 硬盘2参数表 | | 0x901FC | 2

    | 根设备号 |

3. 关闭中断

  1. cli ; no interrupts allowed 表示关闭中断;
  • 因为后面我们要把原本是 BIOS 写好的中断向量表给覆盖掉,也就是给破坏掉了,写上我们自己的中断向量表,所以这个时候是不允许中断进来的。

4. 整理内存,完成实模式

  • 将system的那块内存复制彻底覆盖掉boots(此时boots还有块BIOS存放的位置)

image.png

  • 现在只setup和system了

image.png

5. 实模式和保护模式物理地址转换

  • 实模式是16位的,因为一开始CPU是16位的,现在都是32位或64位CPU了,保护模式就是32位或64位了

    实模式地址转换

  • 先说在前,目前还没进入分页模式,所有地址都是基于段的

  • 之前说过,实模式地址就是段基址(ds寄存器存的地址就是段基址)左移后加上偏移量,很简单

image.png

保护模式地址转换

image.png

  • ds不再直接存段基址,而是段描述符
  • 从段描述符表中,ds指向的位置去除段基址
  • 段描述符表(gdt)位置,在setup中有一个标签,setup的起始地址(即0x90200) + gdt标签就是gdt的地址
  • 当然gdt不止存一个段基址,还有段限长等其他字段

    6. 进入保护模式

  • setup初始化gdt,把地址存入寄存器,就是开启保护模式第一步

  • 与gdt相同原理,还有个idt,即中断表,是Linux自己定义的中断信号的变量值
  • 此时内存是这样:

image.png

  • 打开A20地址线用于兼容20位地址(传统操作)
  • 可编程中断控制器 8259 芯片进行编程,设置0x00 ~ 0x19的20个保留中断信号(必须设置,兼容传统)

    中断号是不能冲突的, Intel 把 0 到 0x19 号中断都作为保留中断,比如 0 号中断就规定为除零异常,软件自定义的中断都应该放在这之后,但是 IBM 在原 PC 机中搞砸了,跟保留中断号发生了冲突,以后也没有纠正过来,所以我们得重新对其进行编程,不得不做,却又一点意思也没有。这是 Linus 在上面注释上的原话。

PIC 请求号 中断号 用途
IRQ0 0x20 时钟中断
IRQ1 0x21 键盘中断
IRQ2 0x22 接连从芯片
IRQ3 0x23 串口2
IRQ4 0x24 串口1
IRQ5 0x25 并口2
IRQ6 0x26 软盘驱动器
IRQ7 0x27 并口1
IRQ8 0x28 实时钟中断
IRQ9 0x29 保留
IRQ10 0x2a 保留
IRQ11 0x2b 保留
IRQ12 0x2c 鼠标中断
IRQ13 0x2d 数学协处理器
IRQ14 0x2e 硬盘中断
IRQ15 0x2f 保留
  • 设置cr0寄存器第0位(PE位)的值置1,就代表正式进入了保护模式

7. setup的最后

jmpi命令跳转,根据gdt的定义找到第一项,值是0即跳转到内存0位置,也就是system代码的起始地址
由此,setup结束了,开始了head.s