/*
* 《linux内核完全注释》第3章
* (C) 2010-10-2 yang hui yu
* mail: gisyhy@163.com
*/
3.2.1 bootsect.s程序问题分析:
1)seg cs :表示下一条语句操作数强制使用该段作为数据段
比如:
seg cs
mov sectors,cx
等价于:
mov cs:[sectors],cx
2)test ax,0x0fff :test只是进行and操作,不返回结果,目的是改变标志位
比如:
test ax,0x0fff !表示and ax,0x0fff,改变了zero flag
jne loop
3)各种跳转指令对应的flags:
jc: CF=1,有进位
js: SF=1,负号
jo: OF=1,溢出
jp: PF=1,偶数
jz/je: ZF=1,零标志
ja: (比较无符号数) 高于
jb: (比较无符号数) 低于
jg: (比较带符号数) 大于
jl: (比较带符号数) 小于
4)flags寄存器标志:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
条件码标志有:
OF:(Overflow Flag)溢出标志,在运算过程中,如操作数超出了机器能表示的范围则称为溢出。此时OF标志位为1(OV),否则置0(NV)。
SF:(Sign Flag)符号标志,记录运算结果的符号,结果为负时置1(NG),否则置0(PL)。
ZF:(Zero Flag)零标志,运算结果为0时置1(ZR),否则置0(NZ)。
CF:(Carry Flag)进位标志,运算时,从最高有效位产生了进位值时置1(CY),否则置0(NC)。
AF:(Auxiliary carry Flag)辅助进位标志,记录运算时最低的4位(半个字节)产生的进位值。有进位值时置1否(AC),则置0(NA)。
PF:(Parity Flag)奇偶标志,用来为机器中传送信息时可能产生的代码出错情况提供检验条件,
当结果操作数中1的个数为偶数时置1(PE),否则置0(PO)。
控制标志有:
DF:(Direction Flag)方向标志,在串处理指令中控制处理信息的方向用,当DF置1(DN)每次操作后,
使变址寄存器SI和DI减量,这样就使串处理从高地址向低地址方向处理。当DF置0(UP)时,则反之。
IF:(Interrupt Flag)中断标志,当IF为1(EI)时,允许中断,否则IF为0(DI)关闭中断。
TF:(Trap Flag)陷井标志,用于单步方式操作,当TF为1时,每条指令执行完后产生陷井,
由系统控制计算机;当IF为0时,CPU正常工作不产生陷井。
3.2.2 setup.s程序问题分析:
1)进入保护模式:
将cr0的PE=1,即比特位0置1
2)GDT描述符:
1>gdtr寄存器6B:2B段限、4B基址
2>描述符项8B:
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low 20 bits available)
; Attr: dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限 1 (2 字节)
dw %1 & 0FFFFh ; 段基址 1 (2 字节)
db (%1 >> 16) & 0FFh ; 段基址 2 (1 字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2 (2 字节)
db (%1 >> 24) & 0FFh ; 段基址 3 (1 字节)
%endmacro ; 共 8 字节
3)IDT描述符:
1>idtr寄存器6B:2B段限、4B基址
2>描述符项8B:
; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择符 (2 字节)
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性 (2 字节)
dw ((%2 >> 16) & 0FFFFh) ; 偏移 2 (2 字节)
%endmacro ; 共 8 字节
4)段选择符:
;选择符图示:
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; ┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9 ┃ 8 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
; ┃ 描述符索引 ┃ TI ┃ RPL ┃
; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
;
; RPL(Requested Privilege Level): 请求特权级,用于特权检查。
;
; TI(Table Indicator): 引用描述符表指示位
; TI=0 指示从全局描述符表GDT中读取描述符;
; TI=1 指示从局部描述符表LDT中读取描述符。
3.3.3 head.s程序问题分析:
1).align 2: 表示下一条语句的地址偏移的对齐字节数,这里表示按2^2=4字节对齐
比如:
0x0000: ...(假设该条语句为3个字节)
0x0003: ...(假设该条语句为3个字节)
.align 2
text:
如果没有".align 2",则text的地址为0x0006
但是由于按4字节对齐,text的地址为 0x0008
2).org 0x1000: 表示下一条语句的地址偏移为0x1000
3)cld/stosl: 字符串操作
cld: 自增
std: 自减
stosl: movl %eax,(%edi){以%es为选择符}
习题解答:
1、请说明Linux内核引导启动的完整过程。系统是在什么时候开始进入386保护模式?
答:
在setup.s执行完后,执行head.s代码时进入保护模式。
2、在本章所描述的内核启动程序中,为什么不直接将system模块搬到0x00000处而是
先搬到0x10000处,在搬到0x00000处呢?
答:
由于在setup.s还要利用BIOS的中断向量,而BIOS中断向量保存在0x00000处。
3、setup.s和head.s中都分别设置了一次全局描述符表GDT和中断描述符表IDT,这是
为什么?能否只在head.s中设置一次?
答:
不能,因为setup.s在跳到head.s时已经处于保护模式,必须设置好GDT和IDT。
4、若不使用as86汇编器,而用gas来编译bootset可以么?为什么Linus当时要使用as86呢?
答:
这样可以将bootsect.s和setup.s运行于实模式的代码编译成16位的,而system的代码
是在保护模式下运行的。