关于os的引导
当机器上电或者是硬件复位时,处理器工作在8086处理器的兼容模式即实时模式下,并且从物理地址的oxFFFF0出开始执行软件初始化代码(通常在EPROM中)。软件初始化代码首先必须设置基本系统功能做操必要的数据结构信息,列如处理中断和异常的实时模式IDT(即中断向量表),然后启动设备中读出零磁头,零磁道上的512B个数据放到从0x7c00处,然后跳转到ox7c00处,
一个合格的引导为512字节大小,且以aa55结束
预备知识
1.禁止中断,
2.加载GDTR表
3.设置CR0的PE位
4.jmp跳转到30位代码
使用的是lgdt加载GDT的基地址到gdtr寄存器中,
CR0的格式如下
位 | 英文 | 说明 |
0 | PE | 是否启用保护模式的标志,为1启用、为0实时模式 |
1 | MP | 监控协处理器,用于控制WAIT/FWAIT指令与TS标志交互作用,MP=1表示协处理器不存在,MP=0,则TS标志不会影响WAIT的执行,MP、EM、TS一般组合使用,见下解 |
2 | EM | 是仿真标志。当该位设置时,表示处理器内没有内部或者外部协处理器,执行协处理器指令时会引起设备不存在异常;当清除时,表示系统有协处理器。设置这个标志可以迫使所有的浮点指令使用软件模拟 |
3 | TS | 任务已切换,该标志用于推迟保存任务切换时协处理器的内容,知道新任务开始实际执行协处理器执行,处理器在每次的任务切换时都回设置该标志,并且在执行协处理器指令时测试该标志 |
4 | ET | 扩展类型标志,为1,表示系统有80387协助处理器存在,并使用32位的协处理器协议,ET=0指明使用80287协处理器,如果仿真位EM=1,则该位将被忽略。在处理器复位操作时,ET位会被初始化指明系统中使用的协处理器类型。如果系统中有80387,则ET=1,否则若有一个80287或者没有协处理器,则ET被设置成0 |
5 | NE | 对于intel80486或以上的CPU,错误位标志,当=1时,就启用了x87协处理器错误的内部报告机制, |
18 | AM |
|
31 | PG | 分页标志,=1;启用分页,=0未使用分页,只用在保护模式中可用,即PE=1时; |
EM、MP、TS的组合
EM | MP | TS | 浮点 | WAIT/FWAIT |
0 | 0 | 0 | 执行 | 执行 |
0 | 0 | 1 | 设备不存在(DNA)异常 | 执行 |
0 | 1 | 0 | 执行 | 执行 |
0 | 1 | 1 | DNA异常 | DNA异常 |
1 | 0 | 0 | DNA异常 | 执行 |
1 | 0 | 1 | DNA异常 | 执行 |
1 | 1 | 0 | DNA异常 | 执行 |
1 | 1 | 1 | DNA异常 | DNA异常 |
关于全局描述符表的寄存器GDTR的知识
1.为什么要使用GDTR
GDTR主要是将每一个任务的数据、代码给分割开,从而使在每一个任务的数据和代码不会出现混乱。使每一个任务只能操作自己的数据和执行自己的代码,为实现多人做准备。
GDTR的知识
GDTR寄存器中用来存放的是GDT描述符表的基地址,
每一个GDT描述符逻辑上有三部分组成段基址(Base address)、段限长(limit)、段属性(Attributes)共64位,GDT主要有三种类型的描述符代码段、数据段、系统段;
下图给出的是GDT的通用格式
上面的表示的是高四个字节、下面的表示第四个字节
基地址=基地址31-24 +基地址23-16 +基地址15-0
段限长=段限长19-16 +段限长址15-0
注意这里面的+号不是将数值相加,而是两节起来的,如101b+11b!=1000b而是101b+11b=10111b
AVL | 系统可用 | P | 段存在 |
B/D | 默认大小 | S | 描述符的类型 0:系统;1:代码或数据 |
DPL | 描述符的特权级别 | TYPE | 段类型 |
G | 颗粒度 |
|
|
G颗粒度,该字段用来决定段限长的单位,如果颗粒度标志为0,则段限长的单位字节,如果颗粒度标志为1,则段限长的单位为4k,即使段限长*4k
DPL字段指明描述符的特权级别,特权范围从0-3。0级最高,3最低。
P段存标志当P=1段在内存中,P=0段不在内存中
D/B该位不理解。
S描述符类型:0:系统段比如GDTR,IDTR,TSS,门,LDTR等
1代码或者数据段,堆栈,程序代码,数据。
TYPE值与S有关系的,
当s=0,TYPE的类型
十进制 | 位11 | 位10 | 位9 | 位8 | 说明 |
0 | 0 | 0 | 0 | 0 | Reserved |
1 | 0 | 0 | 0 | 1 | 16-bit-TSS(Available) |
2 | 0 | 0 | 1 | 0 | LDT |
3 | 0 | 0 | 1 | 1 | 16-Bit-TSS(Busy) |
4 | 0 | 1 | 0 | 0 | 16-bit-Call Gate |
5 | 0 | 1 | 0 | 1 | Task Gate |
6 | 0 | 1 | 1 | 0 | 16-bit Interrupt Gate |
7 | 0 | 1 | 1 | 1 | 16-bit-Trap-Gate |
8 | 1 | 0 | 0 | 0 | Reserved |
9 | 1 | 0 | 0 | 1 | 32-bit-TSS(Available) |
10 | 1 | 0 | 1 | 0 | Reserved |
11 | 1 | 0 | 1 | 1 | 32-bit TSS(Busy) |
12 | 1 | 1 | 0 | 0 | 32-bit Call Gate |
13 | 1 | 1 | 0 | 1 | Reserved |
14 | 1 | 1 | 1 | 0 | 32-bit interrupt Gate |
15 | 1 | 1 | 1 | 1 | 32-bit Trap Gate |
当S=1,TYPE的类型有代码段或者数据段描述符两种,第十一位表示是代码段还是数据段的。当位11的值为1时,位10、位9、位8分别表示的是E、W、A;
位 | 作用 | 说明 |
位10(E) | 表示的扩展方向 | 值为1:表示向下扩展。 |
位9(W) | 是否可写 |
|
位8(A) | 已访问 |
|
当位11值为0时,位10、位9、位8分别表示的是C、R、A;
位 | 作用 | 说明 |
位10(C) | 一直代码 | 特权级别,不是很了解的 |
位9(R) | 可读 | 0:只读,仅可以执行 1:执行/可写 |
位8(A) | 已访问 |
|
根据上面的规则写出对应的GDT就可以了,
下面是代码
View Code
源代码
[BITS 16];表示十六位的代码,即使实模式下的
[org 0x7c00];伪操作,将程序放到0x7c00的内存位置,即使引导
mov ax,cs
mov ds,ax
mov es,ax
call DisplayMsg;调用显示字符的程序,使用中断
xor ax, ax
mov ds, ax
cli;关闭中断,跳转到保护模式前,要禁止中断
lgdt [gdtr];加载GDT
mov eax, cr0 ;获取当前CR0中的纸
or eax, 1 ;修改PE位为1
mov cr0, eax ;将修改后的的值放到CR0中,进入实时模式
jmp 0x10:Code32;这里使用的是32为的代码,所以与常见的16位实时模式下的jmg跳转代码不一样,16位实时模式使用的是实地址,即使物理地址,而32位的保护模式使用得也是地址跳转,但是使用的是段+偏移地址,0x10表示GDTR表中的第三项,
[BITS 32]
Code32:
mov ax,0x8
mov gs,ax
mov edi,(80*8+9)*2
mov ah,0ch
mov al, 'p'
mov [gs:edi],ax
a:
jmp a
[BITS 16]
DisplayMsg:
mov ax,LoadMsg
mov bp,ax
mov cx,18
mov bx,000ch
mov dl,010h
mov dh,dl
mov ax,01301h
int 10h
ret
LoadMsg: db "Load system ...... "
gdt:
dd 0
dd 0
dw 0x0002
dw 0x8000
dw 0x920b
dw 0x00c0
dw 0x07ff
dw 0
dw 0x9a00
dw 0x00c0
gdt_end:
gdtr:
dw gdt_end - gdt - 1 ;计算gdt的长度
dd gdt
times 510 - ( $ - $$) db 1
dw 0xaa55;引导的结束标志