程序:记录在载体上的指令和数据,正在执行的一个副本叫做任务(task
)
- 为了有效在任务间实施隔离,每一个任务具有自己的描述符表,称为局部描述符表
LDT(local)
:存放自己的段那么:
- 类似
GDTR
,处理器中LDTR
也是用来追踪LDT的LDT
的数量依据任务的多少/
- 段选择子中的
TI(table indicator)
0=GDT 1=LDT
多任务环境下,任务切换保护现场:通用寄存器,段寄存器,ESP,EIP,EFLAGS 等,每个任务都用额外的内存区域保护相关的信息,叫做 任务状态段(Task State segment: TSS
),
处理器用TR
寄存器指向当前任务的TSS
,和GDTR,LDTR
寄存器一样只有一个,但任务有多个,随着任务改变
流程:处理器将当前任务现场信息保存到TR指向的TSS,再使TR指向新任务的TSS,并从新任务的TSS中恢复现场
每个任务包含2个部分:全局+私有,全局是所有任务共有的(包含OS的软件和例程)
处理器正在一个代码段中取指令和执行指令时,当前代码段的特权级叫做当前特权级(Current Privilege Level:CPL
)
只有在特权级CPL=0才能执行的指令称为特权指令,
处理器的EFLAGS中,位13,12是IOPL位(输入输出特权级),代表当前任务的IO特权级别,TSS中有EFLAGS的副本
处理器不限制0特权级别的程序的IO访问,但是可以限制特权级别低的程序IO访问权限,
控制转移只允许发生在2个特权级别相同的代码段之间,例如CPL=2时,只能转移到DPL=2的代码段接着执行,不能转移到DPL=0,1,3的代码段执行,
特权级别低的应用程序如何调用特权级别高的操作系统例程?
Type
子段中的C
位,C=0
:代码段只能供同特权级别的程序使用,C=1
:代码段可以从特权级比自己低的程序调用并且进入调用条件:
CPL >= 目标代码段的DPL
:意思是当前执行的程序的特权级别只能比它低或者等于,不能将控制从高特权级别转移到低特权级别并且:依从的代码段不是在自己的DPL
特权级上运行,而是在原来的调用程序的特权级别上运行,意思是当前代码段调用了比自己特权级别高的代码段,并不改变当前特权级别CPL,(被调用过程的特权级别依从)
- 任何时候都不允许将控制从较高的特权级别转移到较低的特权级.下面的调用门是特例
门:另一种形式的描述符,称为门描述符,简称门,门描述符描述可执行的代码,
根据不同的用途分为:中断门/陷阱门:中断过程使用,任务门:执行任务切换.
通过调用门进行控制转移:
jmp far
:转移到比当前特权级别高的代码段,不改变当前特权级别
call far
:当前特权级别提升到目标代码段的特权级别
特权级保护机制只在保护模式下启用,进入保护模式后,处理器自动将当前特权级CPL设定为0,
不管是实施控制转移和访问数据段,都是一个请求,RPL:指请求者的特权级别
段选择子的RPL指请求者的特权级别(多数情况下请求者=自己,所以RPL=CPL),RPL由编程人员决定然后给处理器看
直接转移到非依从的代码段,要求
CPL = DPL
RPL = DPL
直接转移到依从的代码段
CPL >= DPL
RPL >= DPL
高特权级别程序可以访问低特权级别的数据段,反之不行,访问数据段要修改(DS ES FS GS
)寄存器
CPL <= DPL
RPL <= DPL
任何时候栈段特权级别必须等于CPL,对SS进行修改,必须
CPL = DPL
RPL = DPL
接13章的core程序,将用户程序的特权级定为3
调用门(Call Gate
)格式(可安装到GDT或者LDT中):
高32bit: | |||||||
---|---|---|---|---|---|---|---|
31 | 16 | 15 | 14 - 13 | 12 | 11 - 8 | 7 6 5 | 4 - 0 |
段内偏移量31-16 | P | DPL | 0 | TYPE | 0 0 0 | 参数个数 | |
低32bit: | |||||||
31 | 16 | 15 | 0 | ||||
例程所在代码段的段选择子 | 段内偏移量15-0 | ||||||
TYPE:标识门的类型,1100
=调用门
P位:有效位,1=有效,0=调用门时产生中断(故障中断,中断返回后处理器会重新执行)
通过调用门实施特权级之间的控制转移时,注意jmp far
和call far
指令,CPL是否改变,
因为栈的特权级别必须和当前特权级别一致,控制转移时栈也要切换,供对应的特权级使用,
每个任务除了固定的栈外,需要定义几套栈,特权级1需要定义DPL=0的栈,特权级2可能需要定义0,1级别的栈,同理特权级3可能需要定义0,1,2级别的栈.
这些任务的不同级别的栈都需要登记在当前任务的TSS中(意思是的使用的当前TSS的r0,1,2栈),在控制转移后使用,并且栈切换由处理器固件自动完成的,在通过调用门使用高特权级别例程时,传递的参数可能用当前的栈(旧栈),处理器会根据调用门描述符中的参数个数来复制到新的栈中,切换后处理器自动替换SS和ESP的内容
调用门规则:
当前CPL和RPL权限大于等于调用门描述符DPL,(我现在权限比你调用门描述符权限大才行)
数值上:
CPL <= 调用门描述符的DPL
RPL <= 调用门描述符的DPL
当前特权级CPL低于或者等于目标代码段描述符的DPL,
数值上:
CPL >= DPL
LDT的格式和GDT一样,而且某些位是固定的
- D/B位置,操作数大小 = 0,
- L位
- S固定=0,表示系统的段描述符or门描述符, 因为LDT属于系统的段描述符.
- TYPE固定=0010
TSS描述符和LDT差不多:
BUSY
)位,B=0,表示刚刚创建不忙,B=1表示任务在执行或者处于挂起状态arpl
指令(adjust RPL field of segment selector
):
因为程序编写者可以将RPL写成高特权级,在通过调用门控制转移到高特权级别后,如果有某些数段选择子作为参数传过来使用,CPL<=选择子对应描述符DPL,又因为RPL被认为改成高特权级,则RPL<=DPL,那么可以使坏传入OS的数据段来修改
arpl a,b
:
RPL(a) < RPL(b)
:则a的RPL增加到b的RPL,并且设置ZF
ZF
清零 core_code_seg_sel equ 0x38
core_data_seg_sel equ 0x30
sys_routine_seg_sel equ 0x28
video_ram_seg_sel equ 0x20
core_stack_seg_sel equ 0x18
mem_0_4_gb_seg_sel equ 0x08
core_length dd core_end
sys_routine_seg dd section.sys_routine.start
core_data_seg dd section.core_data.start
core_code_seg dd section.core_code.start
core_entry dd start
dw core_code_seg_sel
[bits 32]
SECTION sys_routine vstart=0
put_string: ;显示0终止的字符串并移动光标
;输入:DS:EBX=串地址
push ecx
.getc:
mov cl,[ebx]
or cl,cl
jz .exit
call put_char
inc ebx
jmp .getc
.exit:
pop ecx
retf ;段间返回
;-------------------------------------------------------------------------------
put_char: ;在当前光标处显示一个字符,并推进
;光标。仅用于段内调用
;输入:CL=字符ASCII码
pushad
;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
inc dx ;0x3d5
in al,dx ;高字
mov ah,al
dec dx ;0x3d4
mov al,0x0f
out dx,al
inc dx ;0x3d5
in al,dx ;低字
mov bx,ax ;BX=代表光标位置的16位数
cmp cl,0x0d ;回车符?
jnz .put_0a
mov ax,bx
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor
.put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other
add bx,80
jmp .roll_screen
.put_other: ;正常显示字符
push es
mov eax,video_ram_seg_sel ;0xb8000段的选择子
mov es,eax
shl bx,1
mov [es:bx],cl
pop es
;以下将光标位置推进一个字符
shr bx,1
inc bx
.roll_screen:
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor
push ds
push es
mov eax,video_ram_seg_sel
mov ds,eax
mov es,eax
cld
mov esi,0xa0 ;小心!32位模式下movsb/w/d
mov edi,0x00 ;使用的是esi/edi/ecx
mov ecx,1920
rep movsd
mov bx,3840 ;清除屏幕最底一行
mov ecx,80 ;32位程序应该使用ECX
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls
pop es
pop ds
mov bx,1920
.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
inc dx ;0x3d5
mov al,bh
out dx,al
dec dx ;0x3d4
mov al,0x0f
out dx,al
inc dx ;0x3d5
mov al,bl
out dx,al
popad
ret
;-------------------------------------------------------------------------------
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;EAX=逻辑扇区号
;DS:EBX=目标缓冲区地址
;返回:EBX=EBX+512
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
retf ;段间返回
;-------------------------------------------------------------------------------
;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助
put_hex_dword: ;在当前光标处以十六进制形式显示
;一个双字并推进光标
;输入:EDX=要转换并显示的数字
;输出:无
pushad
push ds
mov ax,core_data_seg_sel ;切换到核心数据段
mov ds,ax
mov ebx,bin_hex ;指向核心数据段内的转换表
mov ecx,8
.xlt:
rol edx,4
mov eax,edx
and eax,0x0000000f
xlat
push ecx
mov cl,al
call put_char
pop ecx
loop .xlt
pop ds
popad
retf
;-------------------------------------------------------------------------------
allocate_memory: ;分配内存
;输入:ECX=希望分配的字节数
;输出:ECX=起始线性地址
push ds
push eax
push ebx
mov eax,core_data_seg_sel
mov ds,eax
mov eax,[ram_alloc]
add eax,ecx ;下一次分配时的起始地址
;这里应当有检测可用内存数量的指令
mov ecx,[ram_alloc] ;返回分配的起始地址
mov ebx,eax
and ebx,0xfffffffc
add ebx,4 ;强制对齐
test eax,0x00000003 ;下次分配的起始地址最好是4字节对齐
cmovnz eax,ebx ;如果没有对齐,则强制对齐
mov [ram_alloc],eax ;下次从该地址分配内存
;cmovcc指令可以避免控制转移
pop ebx
pop eax
pop ds
retf
;-------------------------------------------------------------------------------
set_up_gdt_descriptor: ;在GDT内安装一个新的描述符
;输入:EDX:EAX=描述符
;输出:CX=描述符的选择子
push eax
push ebx
push edx
push ds
push es
mov ebx,core_data_seg_sel ;切换到核心数据段
mov ds,ebx
sgdt [pgdt] ;以便开始处理GDT
mov ebx,mem_0_4_gb_seg_sel
mov es,ebx
movzx ebx,word [pgdt] ;GDT界限
inc bx ;GDT总字节数,也是下一个描述符偏移
add ebx,[pgdt+2] ;下一个描述符的线性地址
mov [es:ebx],eax
mov [es:ebx+4],edx
add word [pgdt],8 ;增加一个描述符的大小
lgdt [pgdt] ;对GDT的更改生效
mov ax,[pgdt] ;得到GDT界限值
xor dx,dx
mov bx,8
div bx ;除以8,去掉余数
mov cx,ax
shl cx,3 ;将索引号移到正确位置
pop es
pop ds
pop edx
pop ebx
pop eax
retf
;-------------------------------------------------------------------------------
make_seg_descriptor: ;构造存储器和系统的段描述符
;输入:EAX=线性基地址
; EBX=段界限
; ECX=属性。各属性位都在原始
; 位置,无关的位清零
;返回:EDX:EAX=描述符
mov edx,eax
shl eax,16
or ax,bx ;描述符前32位(EAX)构造完毕
and edx,0xffff0000 ;清除基地址中无关的位
rol edx,8
bswap edx ;装配基址的31~24和23~16 (80486+)
xor bx,bx
or edx,ebx ;装配段界限的高4位
or edx,ecx ;装配属性
retf
;-------------------------------------------------------------------------------
make_gate_descriptor: ;构造门的描述符(调用门等)
;输入:EAX=门代码在段内偏移地址
; BX=门代码所在段的选择子
; CX=段类型及属性等(各属
; 性位都在原始位置)
;返回:EDX:EAX=完整的描述符
push ebx
push ecx
mov edx,eax
and edx,0xffff0000 ;得到偏移地址高16位
or dx,cx ;组装属性部分到EDX
and eax,0x0000ffff ;得到偏移地址低16位
shl ebx,16
or eax,ebx ;组装段选择子部分
pop ecx
pop ebx
retf
sys_routine_end:
SECTION core_data vstart=0
pgdt dw 0 ;用于设置和修改GDT
dd 0
ram_alloc dd 0x00100000 ;下次分配内存时的起始地址
;符号地址检索表
salt:
salt_1 db '@PrintString'
times 256-($-salt_1) db 0
dd put_string
dw sys_routine_seg_sel
salt_2 db '@ReadDiskData'
times 256-($-salt_2) db 0
dd read_hard_disk_0
dw sys_routine_seg_sel
salt_3 db '@PrintDwordAsHexString'
times 256-($-salt_3) db 0
dd put_hex_dword
dw sys_routine_seg_sel
salt_4 db '@TerminateProgram'
times 256-($-salt_4) db 0
dd return_point
dw core_code_seg_sel
salt_item_len equ $-salt_4
salt_items equ ($-salt)/salt_item_len
message_1 db ' If you seen this message,that means we '
db 'are now in protect mode,and the system '
db 'core is loaded,and the video display '
db 'routine works perfectly.',0x0d,0x0a,0
message_2 db ' System wide CALL-GATE mounted.',0x0d,0x0a,0
message_3 db 0x0d,0x0a,' Loading user program...',0
do_status db 'Done.',0x0d,0x0a,0
message_6 db 0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
db ' User program terminated,control returned.',0
bin_hex db '0123456789ABCDEF'
;put_hex_dword子过程用的查找表
core_buf times 2048 db 0 ;内核用的缓冲区
esp_pointer dd 0 ;内核用来临时保存自己的栈指针
cpu_brnd0 db 0x0d,0x0a,' ',0
cpu_brand times 52 db 0
cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0
;任务控制块链
tcb_chain dd 0
core_data_end:
SECTION core_code vstart=0
fill_descriptor_in_ldt:
push eax
push edx
push edi
push ds
mov ecx,mem_0_4_gb_seg_sel ;全局段
mov ds,ecx
mov edi,[ebx+0x0c] ;获得LDT基地址
xor ecx,ecx
mov cx,[ebx+0x0a] ;LDT界限
inc cx ;长度
mov [edi+ecx+0x00],eax
mov [edi+ecx+0x04],edx ;在LDT末尾安装描述符
add cx,8
dec cx ;新界限
mov [ebx+0x0a],cx ;更新新界限
mov ax,cx
xor dx,dx
mov cx,8
div cx
mov cx,ax ;ax=第几个
shl cx,3
or cx,0000_0000_0000_0100B ;T1=1 RPL=0 (默认,程序返回后自己修改)
pop ds
pop edi
pop edx
pop eax
ret
load_relocate_program:
pushad
push ds
push es
mov ebp,esp
mov ecx,mem_0_4_gb_seg_sel
mov es,ecx
;ESI=TCB的基地址
mov esi,[ebp+11*4]
;分配LDT空间
mov ecx,160
call sys_routine_seg_sel:allocate_memory
mov [es:esi+0x0c],ecx ;登记LDT基地址在TCB中
mov word [es:esi+0x0a],0xffff ;登记LDT界限到TCB中
;ds=内核数据段
mov eax,core_data_seg_sel
mov ds,eax
mov eax,[ebp+12*4] ;从栈中得到程序逻辑扇区号
mov ebx,core_buf ;读入内核数据中的buffer
call sys_routine_seg_sel:read_hard_disk_0
;
mov eax,[core_buf] ;程序大小
mov ebx,eax
and ebx,0xfffffe00
add ebx,512
test eax,0x000001ff
cmovnz eax,ebx
mov ecx,eax
call sys_routine_seg_sel:allocate_memory
mov [es:esi+0x06],ecx ;登记程序起始地址到TCB
mov ebx,ecx ;ebx=程序起始地址
xor edx,edx
mov ecx,512
div ecx
mov ecx,eax ;总扇区数量
mov eax,mem_0_4_gb_seg_sel
mov ds,eax ;ds指向全局段描述符
mov eax,[ebp+12*4] ;eax=程序起始扇区号
.b1:
call sys_routine_seg_sel:read_hard_disk_0
inc eax
loop .b1
mov edi,[es:esi+0x06] ;从TCB中取到程序要加载到的地址
;建立程序head描述符(信息)
mov eax,edi ;
mov ebx,[edi+0x04] ;段长度
dec ebx ;段界限
mov ecx,0x0040f200 ;DPL=3
call sys_routine_seg_sel:make_seg_descriptor
;安装程序head描述符到LDT中
mov ebx,esi ;esi=TCB基础地址
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0011B ;修改RPL=3
mov [es:esi+0x44],cx ;登记head选择子到TCB中
mov [edi+0x04],cx ;写回程序头部
;建立程序code段描述符在LDT中
mov eax,edi
add eax,[edi+0x14] ;eax=程序起始地址+代码段偏移地址
mov ebx,[edi+0x18] ;段长度
dec ebx ;界限
mov ecx,0x0040f800 ;RPL=3
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi ;TCB基础地址
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0011B ;RPL=3
mov [edi+0x14],cx ;写回到程序head
;建立程序数据段描述符
mov eax,edi ;程序加载地址
add eax,[edi+0x1c] ;程序起始地址+数据偏移地址
mov ebx,[edi+0x20] ;段长度
dec ebx ;
mov ecx,0x0040f200 ;RPL=3
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0011B ;RPL=3
mov [edi+0x1c],cx
;建立程序栈描述符
mov ecx,[edi+0x0c]
mov ebx,0x000fffff
sub ebx,ecx
mov eax,4096
mul ecx
mov ecx,eax ;eax=实际byte数量
call sys_routine_seg_sel:allocate_memory
add eax,ecx ;栈的高地址
mov ecx,0x00c0f600 ;RPL=3
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi ;TCB基地址
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0011B ;RPL=3
mov [edi+0x08],cx ;写回到程序head
;重定位程序的salt
mov eax,mem_0_4_gb_seg_sel
mov es,eax
mov eax,core_data_seg_sel
mov ds,eax
cld
mov ecx,[es:edi+0x24] ;从程序head得用户salt条数
add edi,0x28 ;edi = 从u-salt开始
.b2:
push ecx
push edi
mov ecx,salt_items
mov esi,salt
.b3:
push edi
push esi
push ecx
mov ecx,64
repe cmpsd
jnz .b4
mov eax,[esi] ;偏移地址(有了门描述符用不到了)
mov [es:edi-256],eax ;写到用户salt中
mov ax,[esi+4] ;内核例程的门描述符
or ax,0000000000000011B ;RPL=3 门代表下限
mov [es:edi-252],ax ;写到用户salt中
.b4:
pop ecx
pop esi
add esi,salt_item_len
pop edi
loop .b3
pop edi
add edi,256
pop ecx
loop .b2
mov esi,[ebp+11*4] ;TCB地址
;创建0特权级栈
mov ecx,4096
mov eax,ecx
mov [es:esi+0x1a],ecx ;登记0栈大小到TCB
shr dword [es:esi+0x1a],12 ;4KB为单位
call sys_routine_seg_sel:allocate_memory
add eax,ecx ;栈的高地址
mov [es:esi+0x1e],eax ;0级栈地址登记到TCB
mov ebx,0xffffe ;段长度
mov ecx,0x00c09600 ;4KB,读写,0
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
;or cx,0000_ ;当前本来就是0特权级
mov [es:esi+0x22],cx ;登记栈选择子到TCB
mov dword [es:esi+0x24],0 ;登记ESP
;1
mov ecx,4096
mov eax,ecx
mov [es:esi+0x28],ecx
shr word [es:esi+0x28],12
call sys_routine_seg_sel:allocate_memory
add eax,ecx
mov [es:esi+0x2c],eax
mov ebx,0xffffe
mov ecx,0x00c0b600
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0001
mov [es:esi+0x30],cx
mov dword [es:esi+0x32],0
;2
mov ecx,4096
mov eax,ecx
mov [es:esi+0x36],ecx
shr word [es:esi+0x36],12
call sys_routine_seg_sel:allocate_memory
add eax,ecx
mov [es:esi+0x3a],ecx
mov ebx,0xffffe
mov ecx,0x00c0d600
call sys_routine_seg_sel:make_seg_descriptor
or cx,0000_0000_0000_0010
mov [es:esi+0x3e],cx
mov dword [es:esi+0x40],0
;在GDT中登记LDT
mov eax,[es:esi+0x0c] ;LDT起始地址
movzx ebx,word [es:esi+0x0a] ;LDT段界限
mov ecx,0x00408200 ;LDT描述符 RPL=0
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [es:esi+0x10],cx ;登记LDT选择子到TCB中
;创建用户程序TSS
mov ecx,104 ;最小大小
mov [es:esi+0x12],cx ;
dec word [es:esi+0x12] ;登记TCB中TSS界限
call sys_routine_seg_sel:allocate_memory
mov [es:esi+0x14],ecx ;登记TSS起始地址到TCB
;登记TSS的基本内容
mov word [es:ecx+0],0 ;上一个任务TSS=0
mov edx,[es:esi+0x24] ;0 esp
mov [es:ecx+4],edx
mov dx,[es:esi+0x22] ;0 段选择子
mov [es:ecx+8],dx
mov edx,[es:esi+0x32] ;1 esp
mov [es:ecx+12],edx
mov dx,[es:esi+0x30] ;1 段选择子
mov [es:ecx+16],dx
mov edx,[es:esi+0x40] ;2 esp
mov [es:ecx+20],edx
mov dx,[es:esi+0x3e] ;2 段选择子
mov [es:ecx+24],dx
mov dx,[es:esi+0x10] ;登记任务的LDT选择子到TSS
mov [es:ecx+96],dx
mov dx,[es:esi+0x12] ;登记任务的IO bit string
mov [es:ecx+102],dx ;TSS界限值
mov word [es:ecx+100],0 ;T = 0(任务切换不产生中断)
;在GDT中登记TSS描述符
mov eax,[es:esi+0x14] ;TSS起始地址
movzx ebx,word [es:esi+0x12] ;段长度
mov ecx,0x00408900
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor ;在GDT中安装TSS描述符
mov [es:esi+0x18],cx ;在TCB中登记TSS选择子
pop es
pop ds
popad
ret 8 ;esp+8 丢弃参数
;输入:ECX=当前线性地址
append_to_tcb_link:
push eax
push edx
push ds
push es
mov eax,core_data_seg_sel
mov ds,eax
mov eax,mem_0_4_gb_seg_sel
mov es,eax
mov dword [es:ecx+0x00],0
mov eax,[tcb_chain] ;TCB表头指针
or eax,eax
jz .notcb
.searc:
mov edx,eax
mov eax,[es:edx+0x00]
or eax,eax
jnz .searc
mov [es:edx+0x00],ecx
jmp .retpc
.notcb:
mov [tcb_chain],ecx
.retpc:
pop es
pop ds
pop edx
pop eax
ret
start:
mov ecx,core_data_seg_sel
mov ds,ecx
mov ebx,message_1
call sys_routine_seg_sel:put_string
;显示处理器品牌信息
mov eax,0x80000002
cpuid
mov [cpu_brand + 0x00],eax
mov [cpu_brand + 0x04],ebx
mov [cpu_brand + 0x08],ecx
mov [cpu_brand + 0x0c],edx
mov eax,0x80000003
cpuid
mov [cpu_brand + 0x10],eax
mov [cpu_brand + 0x14],ebx
mov [cpu_brand + 0x18],ecx
mov [cpu_brand + 0x1c],edx
mov eax,0x80000004
cpuid
mov [cpu_brand + 0x20],eax
mov [cpu_brand + 0x24],ebx
mov [cpu_brand + 0x28],ecx
mov [cpu_brand + 0x2c],edx
mov ebx,cpu_brnd0 ;显示处理器品牌信息
call sys_routine_seg_sel:put_string
mov ebx,cpu_brand
call sys_routine_seg_sel:put_string
mov ebx,cpu_brnd1
call sys_routine_seg_sel:put_string
;安装调用门
mov edi,salt ;salt表起始位置
mov ecx,salt_items ;salt表条目数量\
;将core_data中的salt的段选择子+偏移地址换成门描述符,登记在GDT中
.b3:
push ecx
mov eax,[edi+256]
mov bx,[edi+260]
mov cx,1_11_0_1100_000_00000B
call sys_routine_seg_sel:make_gate_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [edi+260],cx
add edi,salt_item_len
pop ecx
loop .b3
mov ebx,message_2
call far [salt_1+256]
mov ebx,message_3
call sys_routine_seg_sel:put_string
;创建TCB(Task control block) 我理解为OS层面上的东西(非处理器和存储器)
mov ecx,0x46
call sys_routine_seg_sel:allocate_memory
call append_to_tcb_link ;加入到TCB链
push dword 50 ;程序位于50扇区
push ecx ;TCB起始地址
call load_relocate_program ;加载和重定位用户程序
mov ebx,do_status
call sys_routine_seg_sel:put_string
mov eax,mem_0_4_gb_seg_sel
mov ds,eax
ltr [ecx+0x18] ;加载TSS register 16 bit 选择子
lldt [ecx+0x10] ;加载LDT
mov eax,[ecx+0x44]
mov ds,eax ;切换到用户head
;假装从调用门返回,r0 -> r3
push dword [0x08] ;ss
push dword 0
push dword [0x14] ;cs
push dword [0x10] ;eip=程序code开始
retf ;
return_point:
mov eax,core_data_seg_sel
mov ds,eax;由用户程序jmp来CPL=3导致中断
mov ebx,message_6
call sys_routine_seg_sel:put_string
hlt
core_code_end:
SECTION core_trail
core_end: