ap_32.asm是两个AP在32位保护模式下执行的主要代码,其执行顺序如下:
序号 | 功能 |
1 | 使能中断功能:
|
2 | 获取AP的ID号,并按照不同的AP开始分支执行。 |
3 | AP1或者AP2的分支都是先设置系统TSS。这里要注意:因为设置TSS会操作GDT,所以Load_Sys_Task函数是互斥执行的 |
4 | AP1或者AP2的分支再安装用户进程的TSS,并记录该TSS在GDT中的偏移。这里需要注意的是:
|
5 | AP1和AP2在各自的循环中,不断获取RTC时钟当前的秒值,并在界面上显示 |
对了,用户进程使用的内存页面是事先确定好的,这样做虽然灵活性差,但是方便快捷,用于做示例是完全够了。
;--------------------合成一个 PCI 地址放在 eax 寄存器--------------------------
%macro MAKE_PCI_ADDRESS 4
mov eax, DWORD (0x80000000|(%1<<16)|(%2<<11)|(%3<<8)|(%4&0xfc))
%endmacro
CONFIG_ADDRESS equ 0xcf8
CONFIG_DATA equ 0xcfc
;-----------------------从PCI上读取一个双字的宏定义----------------------------
%macro READ_PCI_DWORD 4
mov dx, CONFIG_ADDRESS
MAKE_PCI_ADDRESS %1, %2, %3, %4
out dx, eax
mov dx, CONFIG_DATA
in eax, dx
%endmacro
;-----------------------------常数定义-----------------------------------
gdt_base_address equ 0x80008000 ;常数,gdt表的起始地址
idt_base_address equ 0x80009000 ;常数,idt表的起始地址
flat_mode_code equ 0x0008 ;平坦模式的代码段选择子
flat_mode_data equ 0x0010 ;平坦模式的数据段选择子
;-----------------------为用户线程1定义的常数----------------------------
app1_page_dir_addr equ 0x0002A000 ;常数,APP1使用的页目录硬地址
app1_page_tb1_addr equ 0x0002B000 ;常数,APP1使用的页表1硬地址
app1_usr_code_addr equ 0x0002C000 ;常数,APP1使用的用户代码页硬地址
app1_usr_stack_addr equ 0x0002D000 ;常数,APP1使用的用户堆栈页硬地址
app1_sys_dir_addr equ 0x0002E000 ;常数,APP1使用的系统堆栈页硬地址
app1_usr_disc_start equ 0x00000064 ;常数,APP1应用程序开始于硬盘扇区100
app1_sys_stack_addr equ 0x80034000 ;常数,APP1使用的系统堆栈的虚拟地址
;-----------------------为用户线程2定义的常数----------------------------
app2_page_dir_addr equ 0x0002F000 ;常数,APP2使用的页目录硬地址
app2_page_tb1_addr equ 0x00030000 ;常数,APP2使用的页表1硬地址
app2_usr_code_addr equ 0x00031000 ;常数,APP2使用的用户代码页硬地址
app2_usr_stack_addr equ 0x00032000 ;常数,APP2使用的用户堆栈页硬地址
app2_sys_dir_addr equ 0x00033000 ;常数,APP2使用的系统堆栈页硬地址
app2_usr_disc_start equ 0x000000C8 ;常数,APP2应用程序开始于硬盘扇区200
app2_sys_stack_addr equ 0x80035000 ;常数,APP1使用的系统堆栈的虚拟地址
;-----------------------系统调用接口定义的常数----------------------------
hpet_global_tick equ 0x80036008 ; 高精度时钟timer0保存ticks的地址
ap_lock_addr equ 0x00006000 ; AP启动信号量的地址
;-----------------------创建用户进程的结构定义----------------------------
struc usr_task_confg
.page_dir: resd 1 ; 页目录对应页面的硬件地址
.page_table_0: resd 1 ; 第0号页表对应页面的硬件地址
.usr_code_page: resd 1 ; 用户进程code页对应页面的硬件地址
.usr_stack_page: resd 1 ; 用户进程堆栈页对应页面的硬件地址
.sys_stack_0: resd 1 ; 用户进程中断时系统堆栈页对应的硬件地址
.ldt_sel: resw 1 ; ldt在gdt中的选择子(GDT中的偏移)
.tss_sel: resw 1 ; 进程在gdt中的选择子(GDT中的偏移)
.disc_start: resd 1 ; 进程代码的起始扇区号
.ldt_addr: resd 1 ; 进程对应LDT段地址
.tss_addr: resd 1 ; 进程对应TSS段地址
endstruc
;-----------------------创建系统进程的结构定义----------------------------
struc sys_task_confg
.tss_sel: resw 1 ; 进程在gdt中的选择子(GDT中的偏移)
.word_resrv: resw 1 ; 保留位,用于4字节对齐
.tss_addr: resd 1 ; 进程对应TSS段地址
endstruc
;----------------------------LDT段设置参数-------------------------------
; 常数,ldt表中代码段的高双字 base-0x0;TYPY-8 只执行;P-1;DPL-3;DT-1;G-1;D-1;limit-0xf;base-0;
ldt_code_high_dd equ 0x00cff800
; 常数,ldt表中代码段的低双字 limit-0xffff;base-0x0;
ldt_code_low_dd equ 0x0000ffff
; 常数,ldt表中数据段的高双字 base-0x0;TYPY-2 读/写;P-1;DPL-3;DT-1;G-1;D-1;limit-0xf;base-0;
ldt_data_high_dd equ 0x00cff200
; 常数,ldt表中数据段的低双字 limit-0xffff;base-0x0;
ldt_data_low_dd equ 0x0000ffff
;------------------------------AP代码开始------------------------------------
SECTION ap_32 vstart=0x80200000
[bits 32]
; 安装各类中断
call init_interrupt_function ; 调用中断初始化函数
sti ; 开启中断功能
; 给BSP发送启动成功的消息
mov DWORD [0xFEE00310], 0x00000000 ; APIC ID = 00h, Fixed交付模式,vector为0x30
mov DWORD [0xFEE00300], 0x00004030
call Get_APIC_ID
cmp al, 0x01
jnz _handle_ap_2
; 初始化AP1的系统进程和用户进程配置参数
call init_ap1_configs
; 创建AP1的系统进程,并写入TR寄存器
mov eax, _SYS_Task_Confg_1
call Load_Sys_Task
; 创建AP1的用户进程
mov eax, _Usr_Task_Confg_1
call Load_Usr_Task
; 打印AP1启动的信息
mov eax, ap1_up_str
mov ebx, 320
call disp_string
; 打印Usr1显示的信息
mov eax, usr1_up_str
mov ebx, 360
call disp_string
jmp _ap_sleep
_handle_ap_2: ; 以下是AP2的运行代码
; 初始化AP2的系统进程和用户进程配置参数
call init_ap2_configs
; 创建AP2的系统进程,并写入TR寄存器
mov eax, _SYS_Task_Confg_2
call Load_Sys_Task
; 创建AP2的用户进程
mov eax, _Usr_Task_Confg_2
call Load_Usr_Task
; 打印AP2启动的信息
mov eax, ap2_up_str
mov ebx, 480
call disp_string
; 打印Usr2显示的信息
mov eax, usr2_up_str
mov ebx, 520
call disp_string
_ap_sleep:
call Get_APIC_ID
cmp al, 0x01
jnz _do_ap2_work
_do_ap1_work:
nop
mov al, 0x80
cli ; 关中断
out 0x70, al
in al, 0x71
sti ; 开中断
and eax, 0x0ff
mov ebx, 342
call disp_word
nop
jmp _ap_sleep
_do_ap2_work:
nop
mov al, 0x80
cli ; 关中断
out 0x70, al
in al, 0x71
sti ; 开中断
and eax, 0x0ff
mov ebx, 502
call disp_word
nop
jmp _ap_sleep
;-----------------------------打印单字的函数-----------------------------------
disp_word: ; 打印单字的接口
; 输入:eax -- 打印的数值放在eax中
; ebx -- 打印位置(就是对于0x800b8000地址的偏移)
; 输出:无
pusha
mov ebp, eax ; 保存被打印数值
mov esi, 0x800b8000
add esi, ebx ; 计算打印位置
mov ecx, 16
_disp_next:
mov eax, ebp
mov edx, disp_chars
sub ecx, 4
shr eax, cl ; 位移指令的第二个参数只能是CL或者具体的数字
and eax, 0x0f ; 取最右边的4位
add edx, eax
mov al, [edx]
mov [esi], al
inc esi
mov byte [esi], 0x0e ; 显示亮黄色字体
inc esi
cmp cl, 0
ja _disp_next
popa
ret
;-----------------------------打印字符串的函数---------------------------------
disp_string: ; 打印字符串的函数
; 输入:eax -- 打印字符串的地址
; ebx -- 打印位置(就是对于0x800b8000地址的偏移)
; 输出:无
pusha
mov esi, 0x800b8000
add esi, ebx ; 计算打印位置
_disp_string_loop:
mov cl, [eax] ; 取字符串中的字符
cmp cl, 0 ; 如果字符为0,结束打印
jz _disp_string_finished
mov [esi], cl ; 打印取出的字符
inc esi
mov byte [esi], 0x71 ; 蓝色字体
inc esi
inc eax
jmp _disp_string_loop
_disp_string_finished:
popa
ret
;------------------------在GDT中安装用户进程LDT段------------------------------
Set_LDT_sel: ; 安装LDT段
; 输入参数: eax-—LDT段基地址
; ecx—-cx为在gdt中的选择子
; 输出参数:无
push edx ; 保存edx
sgdt [pgdt] ; 获取gdt表长度和基址
mov edx, dword [pgdt+2] ; 段基址
add edx, ecx ; 安装地址
mov word [edx], 15 ; LDT段长度的低16位
mov word [edx+2], ax ; LDT段基址的低16位
shr eax, 16 ; LDT段基址右移16位
mov byte [edx+4], al ; LDT段基址的中8位
mov byte [edx+7], ah ; LDT段基址的高8位
mov byte [edx+5], 1_11_0_0010B ; P-1,DPL=3,TYPE-2(LDT段)
mov byte [edx+6], 0_1_0_0_0000B ; G-0,X-1,limit-0
mov word [pgdt], 255 ; gdt表长度定死为255字节
lgdt [pgdt] ; 重新安装gdt表到系统
pop edx ; 弹出edx
ret
;---------------------------在GDT中安装TSS段-----------------------------------
Set_TSS_sel: ; 安装TSS段
; 输入参数: eax-—TSS段基地址
; ecx—-cx为在gdt中的选择子
; 输出参数: 无
push edx ; 保存edx
sgdt [pgdt] ; 获取gdt表长度和基址
mov edx, dword [pgdt+2] ; 段基址
add edx, ecx ; 安装地址
mov word [edx], 103 ; TSS段长度的低16位
mov word [edx+2], ax ; TSS段基址的低16位
shr eax, 16 ; TSS段基址右移16位
mov byte [edx+4], al ; TSS段基址的中8位
mov byte [edx+7], ah ; TSS段基址的高8位
mov byte [edx+5], 1_00_0_1001B ; P-1,TYPE-9(可用386TSS段)
mov byte [edx+6], 0_1_0_0_0000B ; G-0,X-1,limit-0
mov word [pgdt], 255 ; gdt表长度定死为255字节
lgdt [pgdt] ; 重新安装gdt表到系统
pop edx ; 弹出edx
ret
;----------------------------获取本AP对应的APIC ID-----------------------------
Get_APIC_ID: ; 获取本线程对应的APIC ID
; 输入参数:无
; 输出参数:eax -- al返回本线程的APIC ID
push ebx
mov eax, 1
cpuid
mov eax, ebx
shr eax, 24
pop ebx
ret
;------------------------------创建系统进程------------------------------------
Load_Sys_Task: ; 创建系统进程(权限0)
; 输入:eax -- 系统进程配置参数结构地址
; 返回:NULL
pushad
; 检测信号量,避免多AP搞乱GDT
_lock_for_ap2:
lock bts dword [ap_lock_addr], 0 ; 将信号量第0位放入CF,并设置信号量第0位为1
jc _lock_for_ap2
mov ebp, eax
;内核任务的TSS段填写,清空缓冲区,使段内所有保留位置0
mov eax, [ebp + sys_task_confg.tss_addr]
mov ecx, 104
_init_tss:
mov byte [eax], 0x0
inc eax
loop _init_tss
mov eax, [ebp + sys_task_confg.tss_addr]
mov ecx, cr3
mov dword [eax + 0x1c], ecx ; 此处的CR3的写入必须要有,因为Set_TSS_sel不会主动加入页目录!!!
;在gdt中写入tss段描述符
mov eax, [ebp + sys_task_confg.tss_addr]
xor ecx, ecx
mov cx, [ebp + sys_task_confg.tss_sel]
call Set_TSS_sel ; 安装TSS,段首地址在eax中
ltr cx ; 将内核任务写入系统
; 信号量复位
mov dword [ap_lock_addr], 0
popad
ret
;----------------------------安装用户进程的接口--------------------------------
Load_Usr_Task: ; 安装用户进程的接口
; 输入:eax -- 用户进程配置参数结构地址
; 返回:NULL
push ebp
mov ebp, eax
; 检测信号量,避免多AP搞乱GDT
_lock_for_ap3:
lock bts dword [ap_lock_addr], 0 ; 将信号量第0位放入CF,并设置信号量第0位为1
jc _lock_for_ap3
; 拷贝系统页目录到用户进程的页目录
mov ecx, 1024
mov ebx, [ebp + usr_task_confg.page_dir]
xor edx, edx
_ap_dir_cpy:
mov eax, [0xfffff000 + edx]
mov dword [ebx + edx], eax
add edx, 4
loop _ap_dir_cpy
; 用户页目录第一项指向页表
mov edx, [ebp + usr_task_confg.page_dir]
mov eax, [ebp + usr_task_confg.page_table_0]
or eax, 0x07
mov dword [edx], eax
; 用户页表1第一项指向用户app页(对应线性地址:0x0-0x1000)
mov edx, [ebp + usr_task_confg.page_table_0]
mov eax, [ebp + usr_task_confg.usr_code_page]
or eax, 0x07
mov dword [edx], eax
; 用户页表1第二项指向权限3堆栈(对应线性地址:0x1000-0x2000)
mov edx, [ebp + usr_task_confg.page_table_0]
mov eax, [ebp + usr_task_confg.usr_stack_page]
or eax, 0x07
mov dword [edx+4], eax
; 将用户进程数据从逻辑扇区拷贝到到用户app页中
mov eax, [ebp + usr_task_confg.disc_start] ; AP用户代码的逻辑扇区号
mov ebx, [ebp + usr_task_confg.usr_code_page] ; 将用户代码拷贝进用户代码页
mov ecx, 2 ; 拷贝2个扇区,1K
_ap_cpy_disk:
call read_hard_disk_0
inc eax
loop _ap_cpy_disk
; 初始化用户进程的LDT段
mov eax, [ebp + usr_task_confg.ldt_addr]
mov dword [eax], ldt_code_low_dd
mov dword [eax + 4], ldt_code_high_dd
mov dword [eax + 8], ldt_data_low_dd
mov dword [eax + 0x0c], ldt_data_high_dd
;将用户ldt段加入gdt
xor ecx, ecx
mov cx, [ebp + usr_task_confg.ldt_sel] ; 记录AP用户进程的LDT段选择子
call Set_LDT_sel
;初始化用户进程TSS页,段内所有保留位都清空
mov eax, [ebp + usr_task_confg.tss_addr]
push eax
mov ecx, 26
xor edx, edx
_ap_init_usr_tss:
mov dword [eax + edx], 0x0
add edx, 4
loop _ap_init_usr_tss
;逐项填写TSS段
mov edx, [ebp + usr_task_confg.sys_stack_0] ; 设置用户进程使用的R0权限下的堆栈页
add edx, 4092 ; esp0指针指向页的最后
pop eax ; 恢复指向tss段的首地址
mov dword [eax+0x4], edx ; esp0
mov word [eax+0x8], flat_mode_data ; ss0
mov ebx, [ebp + usr_task_confg.page_dir] ; cr3,页目录硬地址
mov dword [eax+0x1c], ebx
mov dword [eax+0x20], 12 ; eip,这里需要按照进程结构写入执行代码在code中的偏移量
pushfd
pop ebx
mov dword [eax+0x24], ebx ; eflag
mov dword [eax+0x38], 0x2000-4 ; esp
mov dword [eax+0x4c], 0x7 ; cs(list-0,TI-1,RPL-3)
mov dword [eax+0x50], 0x0f ; ss(list-1,TI-1,RPL-3)
mov dword [eax+0x54], 0x0f ; ds(list-1,TI-1,RPL-3)
mov cx, [ebp + usr_task_confg.ldt_sel]
mov word [eax+0x60], cx ; ldt选择子
mov word [eax+0x66], 103 ; TSS段长度
;将用户tss写入gdt
mov eax, [ebp + usr_task_confg.tss_addr]
xor ecx, ecx
mov cx, [ebp + usr_task_confg.tss_sel]
call Set_TSS_sel
; 信号量复位
mov dword [ap_lock_addr], 0
pop ebp
ret
;-------------------初始化AP1的系统进程和用户进程的配置参数--------------------
init_ap1_configs: ; 初始化AP1系统进程和用户进程的配置参数
; 输入:无
; 返回:NULL
pusha
; 初始化系统进程配置参数
mov eax, _SYS_Task_Confg_1
mov dword [eax + sys_task_confg.tss_addr], sys1_tss
mov dword [eax + sys_task_confg.tss_sel], 96
; 初始化用户进程配置参数
mov eax, _Usr_Task_Confg_1
mov dword [eax + usr_task_confg.page_dir], app1_page_dir_addr
mov dword [eax + usr_task_confg.page_table_0], app1_page_tb1_addr
mov dword [eax + usr_task_confg.usr_code_page], app1_usr_code_addr
mov dword [eax + usr_task_confg.usr_stack_page], app1_usr_stack_addr
mov dword [eax + usr_task_confg.sys_stack_0], app1_sys_stack_addr
mov dword [eax + usr_task_confg.ldt_sel], 64
mov dword [eax + usr_task_confg.tss_sel], 72
mov dword [eax + usr_task_confg.disc_start], app1_usr_disc_start
mov dword [eax + usr_task_confg.ldt_addr], app1_ldt
mov dword [eax + usr_task_confg.tss_addr], app1_tss
popa
ret
;-------------------初始化AP2的系统进程和用户进程的配置参数--------------------
init_ap2_configs: ; 初始化AP2系统进程和用户进程的配置参数
; 输入:无
; 返回:NULL
pusha
; 初始化系统进程配置参数
mov eax, _SYS_Task_Confg_2
mov dword [eax + sys_task_confg.tss_addr], sys2_tss
mov dword [eax + sys_task_confg.tss_sel], 104
; 初始化用户进程配置参数
mov eax, _Usr_Task_Confg_2
mov dword [eax + usr_task_confg.page_dir], app2_page_dir_addr
mov dword [eax + usr_task_confg.page_table_0], app2_page_tb1_addr
mov dword [eax + usr_task_confg.usr_code_page], app2_usr_code_addr
mov dword [eax + usr_task_confg.usr_stack_page], app2_usr_stack_addr
mov dword [eax + usr_task_confg.sys_stack_0], app2_sys_stack_addr
mov dword [eax + usr_task_confg.ldt_sel], 80
mov dword [eax + usr_task_confg.tss_sel], 88
mov dword [eax + usr_task_confg.disc_start], app2_usr_disc_start
mov dword [eax + usr_task_confg.ldt_addr], app2_ldt
mov dword [eax + usr_task_confg.tss_addr], app2_tss
popa
ret
;---------------------------初始化并启动系统中断-------------------------------
init_interrupt_function: ; 初始化系统中断
; 输入:NULL
; 返回:NULL
; 检测信号量,避免多AP搞乱GDT
_lock_for_ap1:
lock bts dword [ap_lock_addr], 0 ; 将信号量第0位放入CF,并设置信号量第0位为1
jc _lock_for_ap1
; 初始化IO APIC寄存器,使能IO APIC功能
READ_PCI_DWORD 0, 31, 0, 0x0f0
and eax, 0xFFFFC000 ; 读取RCBA寄存器的地址
mov esi, [eax + 0x31fe] ; RCBA寄存器地址偏移0x31fe为OIC寄存器地址
bts esi, 8 ; IO APIC使能
and esi, 0xFFFFFF00 ; IO AIC寄存器的基地址设置为0
mov [eax + 0x31fe], esi ; 写入OIC寄存器
; 设置IO APIC的ID。这个ID每个线程都必须不同,不然系统不知道把中断发送给哪个线程
mov dword [0xFEC00000], 0x0
mov dword [0xFEC00010], 0x0E000000 ; ID=1的AP,IO APIC的ID设置为0x0E
call Get_APIC_ID
cmp al, 0x01
jz _IO_APIC_ID_END
mov dword [0xFEC00000], 0x0
mov dword [0xFEC00010], 0x0C000000 ; ID=2的AP,IO APIC的ID设置为0x0C
_IO_APIC_ID_END:
; 屏蔽 LINT0
bts DWORD [0xFEE00000 + 0x350], 16
; 打开SVR寄存器的APIC enable位(第8位)。该位在BSP中主动使能,AP中默认关闭
mov eax, 0x10F
mov [0xFEE000F0], eax
; 设置中断向量表并使能中断
mov word [pidt],256*8-1 ; IDT的界限
mov dword [pidt+2], idt_base_address ; IDT表的地址设为0x80009000
lidt [pidt]
mov eax, do_int31h_handler ; 安装0x31号中断向量
mov ebx, 0x31
call install_interrupt_handle
mov eax, do_int32h_handler ; 安装0x32号中断向量
mov ebx, 0x32
call install_interrupt_handle
; 信号量复位
mov dword [ap_lock_addr], 0
ret
;------------------------线程1消息接收中断处理接口----------------------------
do_int31h_handler: ; AP1的消息接收处理函数
; 输入:无
; 返回:NULL
mov dword [0xFEE000B0], 0
inc dword [ap1_switch]
mov eax, [ap1_switch]
and eax, 0x1
cmp eax, 0x1
jnz _ap1_do_sys
; 跳转到用户进程1
jmp far [app1_task_rsvr]
jmp _out1
_ap1_do_sys:
; 跳转到系统进程1
jmp far [sys1_task_rsvr]
_out1:
iret
;------------------------线程2消息接收中断处理接口----------------------------
do_int32h_handler: ; AP2的消息接收处理函数
; 输入:无
; 返回:NULL
mov dword [0xFEE000B0], 0
inc dword [ap2_switch]
mov eax, [ap2_switch]
and eax, 0x1
cmp eax, 0x1
jnz _ap2_do_sys
; 跳转到用户进程2
jmp far [app2_task_rsvr]
jmp _out2
_ap2_do_sys:
; 跳转到系统进程2
jmp far [sys2_task_rsvr]
_out2:
iret
;--------------------------在IDT中安装中断处理接口-----------------------------
install_interrupt_handle: ; 安装中断处理接口
; 输入:eax = 中断处理程序入口地址
; ebx = 中断向量号
; 返回:NULL
pusha
sidt [pidt] ; 获取IDT中断描述符表信息
xor edx, edx
mov edx, flat_mode_code << 16 ; 中断程序所述段的选择子
mov dx, ax ; 中断程序入口地址低16位
shl ebx, 3 ; 中断向量号左移3位(*8)
mov ecx, [pidt + 2] ; 中断描述符表的线性地址
mov [ecx + ebx], edx ; 中断描述符低32位
xor edx, edx
and eax, 0xffff0000
mov edx, eax ; 中断程序入口地址高16位
mov dx, 0x8e00 ; 32位中断门,0特权级, P=1, PDL=0
mov [ecx + ebx + 4], edx ; 中断描述符高32位
lidt [pidt] ; 重新写入IDT中断描述符表
popa
ret
;-----------------------------从硬盘读取一个扇区-------------------------------
read_hard_disk_0: ; 从硬盘读取一个逻辑扇区(平坦模型)
; EAX=逻辑扇区号
; EBX=目标缓冲区线性地址
; 返回:EBX=EBX+512
cli
push eax
push ecx
push edx
push eax
mov dx,0x1f2
mov al,1
out dx,al ;读取的扇区数
inc dx ;0x1f3
pop eax
out dx,al ;LBA地址7~0
inc dx ;0x1f4
mov cl,8
shr eax,cl
out dx,al ;LBA地址15~8
inc dx ;0x1f5
shr eax,cl
out dx,al ;LBA地址23~16
inc dx ;0x1f6
shr eax,cl
or al,0xe0 ;第一硬盘 LBA地址27~24
out dx,al
inc dx ;0x1f7
mov al,0x20 ;读命令
out dx,al
.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬盘已准备好数据传输
mov ecx,256 ;总共要读取的字数
mov dx,0x1f0
.readw:
in ax,dx
mov [ebx],ax
add ebx,2
loop .readw
pop edx
pop ecx
pop eax
sti
ret
;----------------------------- 全局变量定义 ----------------------------------
align 4
disp_chars db "0123456789ABCDEF" ; 用于打印字符
ap1_up_str db "AP 1 is UP", 0x0, 0x0 ; AP1启动信息
ap2_up_str db "AP 2 is UP", 0x0, 0x0 ; AP2启动信息
usr1_up_str db "Usr 1 is Running:", 0x0, 0x0, 0x0 ; Usr1启动信息
usr2_up_str db "Usr 2 is Running:", 0x0, 0x0, 0x0 ; Usr2启动信息
;-------------------------------------------------------------------------------
align 4
sys1_tss times 104 db 0 ; APP1对应的系统进程使用的TSS段
sys2_tss times 104 db 0 ; APP2对应的系统进程使用的TSS段
app1_tss times 104 db 0 ; APP1使用的用户进程TSS段
app2_tss times 104 db 0 ; APP2使用的用户进程TSS段
app1_ldt times 16 db 0 ; APP1使用的LDT段
app2_ldt times 16 db 0 ; APP2使用的LDT段
app1_task_rsvr dd 0 ; APP1的保留字,用于进程跳转
app1_tss_sel dw 72 ; APP1的TSS段在GDT中的选择子
app2_task_rsvr dd 0 ; APP2的保留字,用于进程跳转
app2_tss_sel dw 88 ; APP2的TSS段在GDT中的选择子
app1_ldt_sel dw 64 ; APP1的LDT段在GDT中的选择子
app2_ldt_sel dw 80 ; APP2的LDT段在GDT中的选择子
sys1_task_rsvr dd 0
sys1_tss_sel dw 96 ; AP1系统进程的TSS段在GDT中的偏移
sys2_task_rsvr dd 0
sys2_tss_sel dw 104 ; AP2系统进程的TSS段在GDT中的偏移
;-------------------------------------------------------------------------------
align 4
;这个结构用于配置AP1的用户进程
_Usr_Task_Confg_1:
istruc usr_task_confg
at usr_task_confg.page_dir, dd 0
at usr_task_confg.page_table_0, dd 0
at usr_task_confg.usr_code_page, dd 0
at usr_task_confg.usr_stack_page, dd 0
at usr_task_confg.sys_stack_0, dd 0
at usr_task_confg.ldt_sel, dw 0
at usr_task_confg.tss_sel, dw 0
at usr_task_confg.disc_start, dd 0
at usr_task_confg.ldt_addr, dd 0
at usr_task_confg.tss_addr, dd 0
iend
;这个结构用于配置AP2的用户进程
_Usr_Task_Confg_2:
istruc usr_task_confg
at usr_task_confg.page_dir, dd 0
at usr_task_confg.page_table_0, dd 0
at usr_task_confg.usr_code_page, dd 0
at usr_task_confg.usr_stack_page, dd 0
at usr_task_confg.sys_stack_0, dd 0
at usr_task_confg.ldt_sel, dw 0
at usr_task_confg.tss_sel, dw 0
at usr_task_confg.disc_start, dd 0
at usr_task_confg.ldt_addr, dd 0
at usr_task_confg.tss_addr, dd 0
iend
;-------------------------------------------------------------------------------
align 4
;这个结构用于配置AP1的系统进程
_SYS_Task_Confg_1:
istruc sys_task_confg
at sys_task_confg.tss_sel, dw 0
at sys_task_confg.word_resrv, dw 0
at sys_task_confg.tss_addr, dd 0
iend
;这个结构用于配置AP2的系统进程
_SYS_Task_Confg_2:
istruc sys_task_confg
at sys_task_confg.tss_sel, dw 0
at sys_task_confg.word_resrv, dw 0
at sys_task_confg.tss_addr, dd 0
iend
;-------------------------------------------------------------------------------
align 4
ap1_switch dd 0
ap2_switch dd 0
;-------------------------------------------------------------------------------
align 4
pidt dw 0 ; IDT limit 值
dd idt_base_address ; IDT base 值(其实对应的就是硬件0x9000页面)
pgdt dw 0
dd gdt_base_address ;GDT的物理/线性地址:8K处开始
下面,我们最后再讲一下两个用户进程的功能。