ap_16.asm是AP1和AP2刚启动时执行的代码,它的作用同运行于BSP的mbr.asm一样,使AP1和AP2进入32位保护模式和页管理模式,再跳转到地址为0x80200000的ap_32.asm代码继续执行。其中,ap_32.asm为AP1、AP2执行的32位的主体代码,会完成使能中断、安装系统进程、安装用户进程的工作。
ap_16.asm执行的顺序为:
序号 | 功能 |
1 | 屏蔽NMI中断 |
2 | 屏蔽8259可能产生的所有中断,避免后面使能IO APIC后中断相互干扰 |
3 | 使能本AP的GDT段。这里要注意的是:
|
4 | 进入保护模式后,判断是AP1还是AP2,调整各自的esp栈指针指向不同的地址 |
5 | 页目录地址写入cr3。注意,BSP和两个AP使用相同的页目录,因此三个线程的地址映射完全相同 |
6 | AP重新调整GDP段的虚拟地址,因为在页表映射中,GDT段的地址被从0x00008000映射到了0x80008000 |
7 | 跳转到0x80200000开始执行ap_32.asm的代码 |
ap_16.asm的代码如下:
;-----------------------------常数定义-----------------------------------
page_dir_address equ 0x1000 ;常数,页目录硬件地址(第1页)
ap_lock_addr equ 0x00006000 ;AP启动信号量的地址
gdt_base_address equ 0x00008000 ;常数,gdt表的起始地址
ap_id_1_esp_addr equ (0x00028000+4092) ;常数,ap_1线程使用的esp地址
ap_id_2_esp_addr equ (0x00029000+4092) ;常数,ap_2线程使用的esp地址
;-----------------------------AP代码开始----------------------------------
SECTION ap_16 vstart=0x00020000
cli ;中断机制尚未工作
xor ax, ax ;初始化ds和ss段寄存器
mov ds, ax
mov ax, cs
mov ss, ax
mov sp, 0xA000 ;sp临时指向0xA000,进入保护模式后,AP根据不同的ID值设置不同的堆栈指针
;----------------------屏蔽NMI和8259可能产生的中断-----------------------
;屏蔽NMI中断
in al, 0x70 ; port 0x70
or al, 0x80 ; disable all NMI source
out 0x70, al
;屏蔽8259可能产生的所有中断
mov al, 0x0ff
out 0x21, al
;-----------------------初始化描述符表寄存器GDTR-----------------------
; 检测信号量,避免多AP搞乱GDT
_lock_for_ap1:
lock bts dword [ap_lock_addr], 0 ; 将信号量第0位放入CF,并设置信号量第0位为1
jc _lock_for_ap1
;初始化描述符表寄存器GDTR
sgdt [pgdt]
mov dword [pgdt+2], gdt_base_address
mov word [pgdt],23 ;描述符表的界限(三个描述符)
lgdt [pgdt] ;将gdt写入gdt寄存器
; 信号量复位
mov dword [ap_lock_addr], 0
in al,0x92 ;南桥芯片内的端口
or al,0000_0010B
out 0x92,al ;打开A20
mov eax,cr0
or eax,1
mov cr0,eax ;设置PE位
;以下进入保护模式... ...
jmp dword 0x0008:flush
;----------------------------以下是32位模式------------------------------------
[bits 32]
flush:
mov eax, 0x00010
mov ds,eax
mov es,eax
mov fs,eax
mov gs,eax
mov ss,eax
mov esp,ap_id_1_esp_addr ; 需要根据AP不同的ID来设置堆栈指针
; 如果APIC ID = 1, esp = 0x0x00028000
; 如果APIC ID = 2, esp = 0x0x00029000
; 获取APIC ID,如果ID!=1, 等待一段时间
mov eax, 1
cpuid
mov eax, ebx
shr eax, 24 ; 右移后,al中为ID值
cmp al, 0x01
jz _ap1_go2 ; 如果ID=1,跳过esp的修改
mov esp,ap_id_2_esp_addr ; APIC ID = 2, esp = 0x00029000
_ap1_go2:
;----------------------------进入页管理模式------------------------------------
;令CR3寄存器指向页目录,并正式开启页功能
mov eax, page_dir_address ;PCD=PWT=0
mov cr3,eax
mov eax,cr0
or eax,0x80000000
mov cr0,eax ;开启分页机制
;调整堆栈指针
add esp, 0x80000000
;测试一下页管理开启情况
mov eax, paged
add eax, 0x80000000
jmp eax
paged:
xor eax, eax
; 检测信号量,避免多AP搞乱GDT
_lock_for_ap2:
lock bts dword [ap_lock_addr], 0 ; 将信号量第0位放入CF,并设置信号量第0位为1
jc _lock_for_ap2
; 进入页管理模式,先将GDT的线性地址映射到从0x80000000开始的相同位置
sgdt [pgdt]
mov ebx,[pgdt+2]
add dword [pgdt+2],0x80000000 ;GDTR也用的是线性地址
lgdt [pgdt]
; 信号量复位
mov dword [ap_lock_addr], 0
;-------------------------进入系统Core开始执行--------------------------------
;直接跳到core代码起始处开始执行
mov eax, 0x80200000
jmp eax
hlt
;--------------------------系统使用的全局变量---------------------------------
align 4
pgdt dw 0
dd gdt_base_address ;GDT的物理地址:8K处开始