1.引导程序
core_base_address equ 0x00040000
core_start_sector equ 0x00000001
SECTION mbr vstart=0x00007c00
; 此刻cs内容是0x0000
mov ax,cs
mov ss,ax
; 设置sp
mov sp,0x7c00
; gdt基地址
mov eax,[cs:pgdt+0x02]
xor edx,edx
mov ebx,16
div ebx
mov ds,eax
mov ebx,edx
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16
; 段基地址15~0 段界限15~0
; 0x00 cf 98 00
; 0x00 00 ff ff
; 段基地址:0x0000 0000
; 段界限:f ffff
; 段属性:4kb计量 32位 段在内存 0特权级 非系统段 代码段
mov dword [ebx+0x08],0x0000ffff
mov dword [ebx+0x0c],0x00cf9800
; 段基地址:0x0000 0000
; 段界限:f ffff
; 段属性:4kb计量 32位 段在内存 0特权级 非系统段 可读可写数据段
mov dword [ebx+0x10],0x0000ffff
mov dword [ebx+0x14],0x00cf9200
mov word [cs: pgdt],23
; 通知处理器gdt界限,基地址
lgdt [cs: pgdt]
; 开启a20地址线
in al,0x92
or al,0000_0010B
out 0x92,al
; 禁止中断
cli
; 进入保护模式
mov eax,cr0
or eax,1
mov cr0,eax
; 执行指令跳转
jmp dword 0x0008:flush
[bits 32]
flush:
; 统一采用全局数据段
mov eax,0x00010
mov ds,eax
mov es,eax
mov fs,eax
mov gs,eax
mov ss,eax
; 设置esp
mov esp,0x7000
; 内核基础地址
mov edi,core_base_address
; 内核任务逻辑扇区
mov eax,core_start_sector
; 内核基础地址
mov ebx,edi
; 读取磁盘扇区到物理内存
call read_hard_disk_0
; 读取内核任务头4字节
mov eax,[edi]
xor edx,edx
mov ecx,512
div ecx
or edx,edx
jnz @1
dec eax
@1:
or eax,eax
jz pge
mov ecx,eax
mov eax,core_start_sector
inc eax
@2:
call read_hard_disk_0
inc eax
loop @2
; 到这里已经把内核任务完整的读取到了物理内存
pge:
; 这是用作页目录表的起始位置
mov ebx,0x00020000
; 设置页目录表最后一项指向页目录表自己
mov dword [ebx+4092],0x00020003
; 这个是页表的起始位置
mov edx,0x00021003
; 设置页目录表索引0项和索引512项指向页表
mov [ebx+0x000],edx
mov [ebx+0x800],edx
; 设置页表前256项
; 这样设置后,虚拟空间前1mb内的线性地址的物理地址就是线性地址自身
mov ebx,0x00021000
xor eax,eax
xor esi,esi
.b1:
mov edx,eax
or edx,0x00000003
mov [ebx+esi*4],edx
add eax,0x1000
inc esi
cmp esi,256
jl .b1
; 通知处理器页目录表位置
mov eax,0x00020000
mov cr3,eax
; 从处理器获取gdt界限,基地址信息
sgdt [pgdt]
mov ebx,[pgdt+2]
add dword [pgdt+2],0x80000000
; 通知处理器gdt界限,基地址信息[线性地址改成虚拟地址高区间的]
lgdt [pgdt]
; 开启分页机制
mov eax,cr0
or eax,0x80000000
mov cr0,eax
; 令esp指向虚拟地址高区间的
add esp,0x80000000
; 指令跳转
; cs指向全局代码段,从内存取出4字节段内偏移
; 这个段内偏移是线性地址,按页目录表-页表转换后得到的物理地址将是
; 内核任务过程start起始物理位置
jmp [0x80040004]
read_hard_disk_0:
push eax
push ecx
push edx
push eax
mov dx,0x1f2
mov al,1
out dx,al
inc dx
pop eax
out dx,al
inc dx
mov cl,8
shr eax,cl
out dx,al
inc dx
shr eax,cl
out dx,al
inc dx
shr eax,cl
or al,0xe0
out dx,al
inc dx
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
ret
pgdt dw 0
dd 0x00008000
times 510-($-$$) db 0
db 0x55,0xaa
2.内核任务
flat_4gb_code_seg_sel equ 0x0008
flat_4gb_data_seg_sel equ 0x0018
idt_linear_address equ 0x8001f000
%macro alloc_core_linear 0
mov ebx,[core_tcb+0x06]
add dword [core_tcb+0x06],0x1000
call flat_4gb_code_seg_sel:alloc_inst_a_page
%endmacro
%macro alloc_user_linear 0
mov ebx,[esi+0x06]
add dword [esi+0x06],0x1000
call flat_4gb_code_seg_sel:alloc_inst_a_page
%endmacro
SECTION core vstart=0x80040000
core_length dd core_end
core_entry dd start
[bits 32]
; ds:ebx指向首个待显示字符的线性地址
put_string:
push ebx
push ecx
cli
.getc:
mov cl,[ebx]
or cl,cl
jz .exit
call put_char
inc ebx
jmp .getc
.exit:
sti
pop ecx
pop ebx
retf
put_char:
pushad
mov dx,0x3d4
mov al,0x0e
; 向0x3d4写入0x0e
out dx,al
inc dx
; 从0x3d5读取1字节
in al,dx
; 读取的是16位光标位置高8位
mov ah,al
dec dx
mov al,0x0f
; 向0x3d4写入0x0f
out dx,al
inc dx
; 从0x3d5读取1字节
in al,dx
; bx就是16位光标位置
mov bx,ax
and ebx,0x0000ffff
cmp cl,0x0d
jnz .put_0a
; 字符是0x0d时的处理
mov ax,bx
mov bl,80
div bl
mul bl
; 这是将光标位置设置到行首---回车
mov bx,ax
jmp .set_cursor
.put_0a:
cmp cl,0x0a
jnz .put_other
; 字符是0x0a时的处理,这是换行
add bx,80
jmp .roll_screen
.put_other:
; 由光标位置得到显存位置
shl bx,1
; 字符存入显存
mov [0x800b8000+ebx],cl
shr bx,1
; 更新光标位置
inc bx
.roll_screen:
cmp bx,2000
jl .set_cursor
; 将显存内每一行的内容拷贝到前面一行
; 最后一行全部显示空格
; 光标位置设置为最后一行的行首
cld
mov esi,0x800b80a0
mov edi,0x800b8000
mov ecx,1920
rep movsd
mov bx,3840
mov ecx,80
.cls:
mov word [0x800b8000+ebx],0x0720
add bx,2
loop .cls
mov bx,1920
.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
inc dx
mov al,bh
out dx,al
dec dx
mov al,0x0f
out dx,al
inc dx
mov al,bl
out dx,al
popad
ret
; eax:磁盘逻辑扇区
; ds:ebx:物理内存起始位置线性地址
read_hard_disk_0:
cli
push eax
push ecx
push edx
push eax
mov dx,0x1f2
mov al,1
; 向0x1f2写入1---磁盘要传输的扇区数
out dx,al
inc dx
pop eax
; 向0x1f3写入28位逻辑扇区号的最低8位
out dx,al
inc dx
mov cl,8
shr eax,cl
; 向0x1f4写入28位逻辑扇区号的次低8位
out dx,al
inc dx
shr eax,cl
; 向0x1f5写入28位逻辑扇区号的次次低8位
out dx,al
inc dx
shr eax,cl
or al,0xe0
; 向0x1f6写入28位逻辑扇区号的高4位,同时高4位是1110
out dx,al
inc dx
mov al,0x20
; 向0x1f7写入0x20---通知磁盘开始数据传输
out dx,al
.waits:
; 从0x1f7读取1字节
in al,dx
and al,0x88
; 如果磁盘不忙且准备就绪
cmp al,0x08
; 否则,继续等待
jnz .waits
; 向物理内存写入512字节
mov ecx,256
mov dx,0x1f0
.readw:
; 取出2字节就绪数据
in ax,dx
; 存储到ds:ebx
mov [ebx],ax
add ebx,2
loop .readw
pop edx
pop ecx
pop eax
sti
retf
; edx存储了32位数值,将此数值按16进制显示
put_hex_dword:
pushad
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
popad
retf
; 在gdt中安装8字节描述符
set_up_gdt_descriptor:
push eax
push ebx
push edx
; 从处理器获得gdt的界限,基地址(线性地址)
sgdt [pgdt]
movzx ebx,word [pgdt]
inc bx
; ebx此时是区域尾后位置
add ebx,[pgdt+2]
; 8字节描述符存入区域尾部
mov [ebx],eax
mov [ebx+4],edx
add word [pgdt],8
; 通知处理器gdt区域界限,基地址
lgdt [pgdt]
mov ax,[pgdt]
xor dx,dx
mov bx,8
div bx
mov cx,ax
; 刚刚安装描述符的选择子
shl cx,3
pop edx
pop ebx
pop eax
retf
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16
; 段基地址15~0 段界限15~0
; 传入:eax段基地址,ebx段界限,ecx属性信息
; 传出:edx:eax 8位描述符
make_seg_descriptor:
mov edx,eax
shl eax,16
or ax,bx
and edx,0xffff0000
rol edx,8
bswap edx
xor bx,bx
or edx,ebx
or edx,ecx
retf
; 段内偏移31~16 P DPL 0 TYPE(1100) 000 参数个数(5位)
; 例程所在代码段选择子 段内偏移15~0
; eax:32位段偏移
; ebx:16位例程所在代码段选择子
; cx:16位属性位
make_gate_descriptor:
push ebx
push ecx
mov edx,eax
and edx,0xffff0000
or dx,cx
and eax,0x0000ffff
shl ebx,16
or eax,ebx
pop ecx
pop ebx
retf
; 物理页寻找与分配
; eax返回时存储了物理页物理地址
allocate_a_4k_page:
push ebx
push ecx
push edx
xor eax,eax
.b1:
; 将线性地址指向位置指定偏移的比特位从内存送入寄存器,然后对应位置设置为1
bts [page_bit_map],eax
jnc .b2
inc eax
cmp eax,page_map_len*8
jl .b1
mov ebx,message_3
call flat_4gb_code_seg_sel:put_string
hlt
.b2:
shl eax,12
pop edx
pop ecx
pop ebx
ret
alloc_inst_a_page:
push eax
push ebx
push esi
; 保留高10位
mov esi,ebx
and esi,0xffc00000
; 成为低10位
shr esi,20
; 高20位采用0xfffff
or esi,0xfffff000
; 线性地址ds:esi,esi高20位得到物理页---页目录表自己
; 原始ebx的高10位构成索引
test dword [esi],0x00000001
jnz .b1
; 表示索引对应的目录项指向的页表不存在
; 分配4kb物理页
call allocate_a_4k_page
or eax,0x00000007
; 在页目录表对应项设置页表
mov [esi],eax
.b1:
; 0b [0000000000] {0000000000} '000000000000'
mov esi,ebx
; 0b 0000000000 [0000000000] {0000000000} '00'
shr esi,10
; 0b 0000000000 [0000000000] 0000000000 00
and esi,0x003ff000
; 0b 1111111111 [0000000000] 0000000000 00
or esi,0xffc00000
; 0b [0000000000] {0000000000} '000000000000'
; -->0b 0000000000 {0000000000} 000000000000
and ebx,0x003ff000
; 0b 0000000000 0000000000 {0000000000} 00
shr ebx,10
; 0b 1111111111 [0000000000] 0000000000 00
; 0b 0000000000 0000000000 {0000000000} 00
;->0b 1111111111 [0000000000] {0000000000} 00
or esi,ebx
; 搜索并分配4kb物理页
call allocate_a_4k_page
or eax,0x00000007
; 用高10位寻找页目录表得到页表(其实是页目录表自身)
; 用传入线性地址高10位在页表定位到物理页(其实是页表)
; 用 传入线性地址次高10位+00 在物理页定位偏移
; 访问的其实是页表索引项--》索引号是传入线性地址次高10位
; 修改索引项让其指向传入线性地址对应的物理页
mov [esi],eax
pop esi
pop ebx
pop eax
retf
; 返回时eax是新分配的4kb物理页的物理地址
; 这个物理页拷贝了当前页目录表的1024项
create_copy_cur_pdir:
push esi
push edi
push ebx
push ecx
; 分配4kb物理页
call allocate_a_4k_page
mov ebx,eax
or ebx,0x00000007
; 这是设置页目录表倒数第2项指向刚刚分配的4kb物理页
mov [0xfffffff8],ebx
; 给定线性地址,通知处理器检查此线性地址是否在tlb(快表中)
; 如在,更新块表中对应的线性地址--物理地址映射
invlpg [0xfffffff8]
mov esi,0xfffff000
mov edi,0xffffe000
mov ecx,1024
; 将页目录表1024项,依次拷贝到新分配的4kb物理页
cld
; 每次拷贝4字节
repe movsd
pop ecx
pop ebx
pop edi
pop esi
retf
; 通用中断处理
general_interrupt_handler:
push eax
mov al,0x20
; 向0xa0写入0x20
out 0xa0,al
; 向0x20写入0x20
out 0x20,al
pop eax
iretd
; 通用异常处理
general_exception_handler:
mov ebx,excep_msg
call flat_4gb_code_seg_sel:put_string
hlt
; 0x70中断处理
rtm_0x70_interrupt_handle:
pushad
mov al,0x20
; 向0xa0写入0x20
out 0xa0,al
; 向0x20写入0x20
out 0x20,al
mov al,0x0c
; 向0x70写入0x0c
out 0x70,al
; 从0x71读取1字节
in al,0x71
mov eax,tcb_chain
.b0:
; 首次:取得tcb链表首个对象线性地址
; 非首次:取得eax指向tcb对象下一个tcb对象线性地址
mov ebx,[eax]
or ebx,ebx
; 链表空
jz .irtn
; 判断当前指向tcb对象状态
cmp word [ebx+0x04],0xffff
; 状态忙
je .b1
; 更新eax
mov eax,ebx
jmp .b0
.b1:
; eax指向上一个tcb对象线性地址(初始指向tcb_chain)
; ebx指向当前tcb对象,它的状态为忙
; 取得下一个tcb对象线性地址
mov ecx,[ebx]
; 完成忙对象从链表删除
; 设置上一tcb对象下一个tcb对象为下下tcb对象
mov [eax],ecx
.b2:
; 取得下一tcb对象
mov edx,[eax]
or edx,edx
; 下一tcb对象不存在
jz .b3
mov eax,edx
jmp .b2
; 将状态忙的tcb对象加入链式结构尾部
.b3:
; 设置下一tcb对象为忙的tcb对象
mov [eax],ebx
; 设置忙tcb对象下一tcb对象为0x0000 0000
mov dword [ebx],0x00000000
; 链式结构中无tcb对象
mov eax,tcb_chain
; 从链式结构寻找状态空闲的任务
.b4:
mov eax,[eax]
or eax,eax
jz .irtn
; 如果eax指向tcb对象状态是空闲
cmp word [eax+0x04],0x0000
jnz .b4
; 不忙的tcb对象状态修改为忙
not word [eax+0x04]
; 状态忙的tcb对象的状态修改为不忙
not word [ebx+0x04]
; 通过jmp实现任务切换
jmp far [eax+0x14]
.irtn:
popad
iretd
terminate_current_task:
mov eax,tcb_chain
.b0:
mov ebx,[eax]
cmp word [ebx+0x04],0xffff
je .b1
mov eax,ebx
jmp .b0
.b1:
; 修改任务状态为0x3333
mov word [ebx+0x04],0x3333
.b2:
hlt
jmp .b2
pgdt dw 0
dd 0
pidt dw 0
dd 0
; 属于内核自己出于逻辑实现需要弄处理的东西。处理器层面无
tcb_chain dd 0
core_tcb times 32 db 0
page_bit_map db 0xff,0xff,0xff,0xff,0xff,0xff,0x55,0x55
db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
db 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
page_map_len equ $-page_bit_map
salt:
salt_1 db '@PrintString'
times 256-($-salt_1) db 0
dd put_string
dw flat_4gb_code_seg_sel
salt_2 db '@ReadDiskData'
times 256-($-salt_2) db 0
dd read_hard_disk_0
dw flat_4gb_code_seg_sel
salt_3 db '@PrintDwordAsHexString'
times 256-($-salt_3) db 0
dd put_hex_dword
dw flat_4gb_code_seg_sel
salt_4 db '@TerminateProgram'
times 256-($-salt_4) db 0
dd terminate_current_task
dw flat_4gb_code_seg_sel
salt_item_len equ $-salt_4
salt_items equ ($-salt)/salt_item_len
excep_msg db '********Exception encounted********',0
message_0 db ' Working in system core with protection '
db 'and paging are all enabled.System core is mapped '
db 'to address 0x80000000.',0x0d,0x0a,0
message_1 db ' System wide CALL-GATE mounted.',0x0d,0x0a,0
message_3 db '********No more pages********',0
core_msg0 db ' System core task running!',0x0d,0x0a,0
bin_hex db '0123456789ABCDEF'
core_buf times 512 db 0
cpu_brnd0 db 0x0d,0x0a,' ',0
cpu_brand times 52 db 0
cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0
; 在用户任务的ldt安装8字节描述符
fill_descriptor_in_ldt:
push eax
push edx
push edi
; ldt基地址---线性地址
mov edi,[ebx+0x0c]
xor ecx,ecx
; ldt界限
mov cx,[ebx+0x0a]
inc cx
mov [edi+ecx+0x00],eax
mov [edi+ecx+0x04],edx
add cx,8
dec cx
mov [ebx+0x0a],cx
mov ax,cx
xor dx,dx
mov cx,8
div cx
mov cx,ax
shl cx,3
; 得到刚刚安装8字节描述符的选择子
or cx,0000_0000_0000_0100B
pop edi
pop edx
pop eax
ret
load_relocate_program:
pushad
mov ebp,esp
; 当前用的是内核任务的页目录表
; 将此页目录表前512项设置为0x0000 0000
mov ebx,0xfffff000
xor esi,esi
.b1:
mov dword [ebx+esi*4],0x00000000
inc esi
cmp esi,512
jl .b1
mov eax,cr3
; 可以使得快表内所有项失效
; 块表是一个寄存器,包含多个项
; 每个向给出线性地址--物理地址映射,可以加快地址转换
; 页目录表,页表更新后,快表的更新需要手动触发
; 快表不命中时,会自动进行更新
mov cr3,eax
mov eax,[ebp+40]
mov ebx,core_buf
call flat_4gb_code_seg_sel:read_hard_disk_0
mov eax,[core_buf]
mov ebx,eax
and ebx,0xfffff000
add ebx,0x1000
test eax,0x00000fff
mov eax,ebx
mov ecx,eax
; ecx代表了容纳用户任务所需的4kb页数量
shr ecx,12
mov eax,[ebp+40]
; tcb线性地址
mov esi,[ebp+36]
.b2:
alloc_user_linear
push ecx
mov ecx,8
.b3:
call flat_4gb_code_seg_sel:read_hard_disk_0
inc eax
loop .b3
pop ecx
loop .b2
; 在核心任务虚拟区域划分可用4kb用作用户任务的tss
alloc_core_linear
; TCB对象结构
; 0x44 2字节头部选择子
; 0x40 2特权级栈的初始ESP
; 0x3e 2特权级栈选择子
; 0x3a 2特权级栈基地址
; 0x36 2特权级栈以4KB为单位的长度
; 0x32 1特权级栈的初始ESP
; 0x30 1特权级栈选择子
; 0x2c 1特权级栈基地址
; 0x28 1特权级栈以4kb为单位的长度
; 0x24 0特权级栈的初始ESP
; 0x22 0特权级栈选择子
; 0x1e 0特权级栈基地址
; 0x1a 0特权级栈以4kb为单位的长度
; 0x18 tss选择子
; 0x14 tss基地址
; 0x12 tss界限值
; 0x10 ldt选择子
; 0x0c ldt基地址
; 0x0a ldt当前界限值
; 0x06 程序加载基地址
; 0x04 任务状态
; 0x00 下一个tcb基地址
; 用户任务tcb中设置用户任务tss基地址--线性地址
mov [esi+0x14],ebx
; 用户任务tcb中设置用户任务tss界限
mov word [esi+0x12],103
; 在用户任务虚拟区域划分可用4kb区域用作用户任务ldt区域
alloc_user_linear
; 用户任务tcb中设置ldt基地址--线性地址
mov [esi+0x0c],ebx
mov eax,0x00000000
mov ebx,0x000fffff
; 4kb计量 32位 段在内存 特权级3 非系统段 代码段
mov ecx,0x00c0f800
; 构造代码段
call flat_4gb_code_seg_sel:make_seg_descriptor
mov ebx,esi
; 在用户任务ldt区域安装代码段描述符
call fill_descriptor_in_ldt
; 段选择子请求特权级是3
or cx,0000_0000_0000_0011B
mov ebx,[esi+0x14]
; TSS格式
; I/OMapAddr Reserved 100
; Reserved LDT_Sector 96
; Reserved GS 92
; Reserved FS 88
; Reserved DS 84
; Reserved SS 80
; Reserved CS 76
; Reserved ES 72
; EDI 68
; ESI 64
; EBP 60
; ESP 56
; EBX 52
; EDX 48
; ECX 44
; EAX 40
; EFLAGS 36
; EIP 32
; CR3(PDBR) 28
; Reserved SS2 24
; ESP2 20
; Reserved SS1 16
; ESP1 12
; Reserved SS0 8
; ESP0 4
; Reserved Pre_Task_Tss 0
; 在用户任务tss中设置cs--代码段选择子
mov [ebx+76],cx
mov eax,0x00000000
mov ebx,0x000fffff
; 4kb计量 32位 段在内存 特权级3 非系统段 可读可写数据段
mov ecx,0x00c0f200
call flat_4gb_code_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0011B
mov ebx,[esi+0x14]
; 在用户任务tss中设置ds,es,fs,gs--数据段选择子
mov [ebx+84],cx
mov [ebx+72],cx
mov [ebx+88],cx
mov [ebx+92],cx
alloc_user_linear
mov ebx,[esi+0x14]
; 在用户任务tss中设置ss--栈段选择子
mov [ebx+80],cx
mov edx,[esi+0x06]
; 在用户任务tss中设置esp--栈段的esp,初始时刻指向区域尾后位置
mov [ebx+56],edx
; 用户任务虚拟空间内划分4kb可用空间用于特权级0栈
alloc_user_linear
mov eax,0x00000000
mov ebx,0x000fffff
mov ecx,0x00c09200
call flat_4gb_code_seg_sel:make_seg_descriptor
mov ebx,esi
; 在用户任务ldt区域安装段描述符
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0000B
mov ebx,[esi+0x14]
; 在用户任务tss中设置ss0--0特权级栈段选择子
mov [ebx+8],cx
mov edx,[esi+0x06]
; 在用户任务tss中设置esp0--0特权级栈段的esp
mov [ebx+4],edx
alloc_user_linear
mov eax,0x00000000
mov ebx,0x000fffff
mov ecx,0x00c0b200
call flat_4gb_code_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0001B
mov ebx,[esi+0x14]
; 在用户任务tss中设置ss1--1特权级栈段选择子
mov [ebx+16],cx
mov edx,[esi+0x06]
; 在用户任务tss中设置esp1--1特权级栈段的esp
mov [ebx+12],edx
alloc_user_linear
mov eax,0x00000000
mov ebx,0x000fffff
mov ecx,0x00c0d200
call flat_4gb_code_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,0000_0000_0000_0010B
mov ebx,[esi+0x14]
; 在用户任务tss中设置ss2--2特权级栈段选择子
mov [ebx+24],cx
mov edx,[esi+0x06]
; 在用户任务tss中设置esp2--2特权级栈段的esp
mov [ebx+20],edx
cld
; 对用户任务的每个符号进行内循环处理
; 用户任务符号数
mov ecx,[0x0c]
; 首个符号的段内偏移
mov edi,[0x08]
.b4:
push ecx
push edi
; 对用户任务当前符号edi,依次将内核每个符号与其匹配
; 内核任务符号数
mov ecx,salt_items
; 内核任务首个符号偏移---就是符号的线性地址
mov esi,salt
.b5:
push edi
push esi
push ecx
mov ecx,64
repe cmpsd
jnz .b6
; 比较并匹配
mov eax,[esi]
; 用户任务在符号头存储4字节段内偏移
mov [edi-256],eax
mov ax,[esi+4]
or ax,0000000000000011B
; 用户任务在符号头5,6字节存储门选择子(请求特权级是3)
mov [edi-252],ax
.b6:
pop ecx
pop esi
; esi指向内核下一符号
add esi,salt_item_len
pop edi
loop .b5
pop edi
; edi指向用户任务下一符号
add edi,256
pop ecx
loop .b4
; 用户任务tcb对象线性地址
mov esi,[ebp+36]
; TCB对象结构
; 0x44 2字节头部选择子
; 0x40 2特权级栈的初始ESP
; 0x3e 2特权级栈选择子
; 0x3a 2特权级栈基地址
; 0x36 2特权级栈以4KB为单位的长度
; 0x32 1特权级栈的初始ESP
; 0x30 1特权级栈选择子
; 0x2c 1特权级栈基地址
; 0x28 1特权级栈以4kb为单位的长度
; 0x24 0特权级栈的初始ESP
; 0x22 0特权级栈选择子
; 0x1e 0特权级栈基地址
; 0x1a 0特权级栈以4kb为单位的长度
; 0x18 tss选择子
; 0x14 tss基地址
; 0x12 tss界限值
; 0x10 ldt选择子
; 0x0c ldt基地址
; 0x0a ldt当前界限值
; 0x06 程序加载基地址
; 0x04 任务状态
; 0x00 下一个tcb基地址
; 用户任务ldt基地址--线性地址
mov eax,[esi+0x0c]
; 用户任务ldt区域界限
movzx ebx,word [esi+0x0a]
; 字节计量 32位 段在内存 特权级0 系统段 可读可写数据段
mov ecx,0x00408200
call flat_4gb_code_seg_sel:make_seg_descriptor
; 用户任务ldt区域的描述符需要安装在gdt
call flat_4gb_code_seg_sel:set_up_gdt_descriptor
; 用户任务tcb对象中设置ldt段的选择子
mov [esi+0x10],cx
mov ebx,[esi+0x14]
; TSS格式
; I/OMapAddr Reserved 100
; Reserved LDT_Sector 96
; Reserved GS 92
; Reserved FS 88
; Reserved DS 84
; Reserved SS 80
; Reserved CS 76
; Reserved ES 72
; EDI 68
; ESI 64
; EBP 60
; ESP 56
; EBX 52
; EDX 48
; ECX 44
; EAX 40
; EFLAGS 36
; EIP 32
; CR3(PDBR) 28
; Reserved SS2 24
; ESP2 20
; Reserved SS1 16
; ESP1 12
; Reserved SS0 8
; ESP0 4
; Reserved Pre_Task_Tss 0
; 用户任务tss对象中设置用户任务ldt区域的选择子
mov [ebx+96],cx
; 用户任务tss对象中设置前一任务tss选择子为0x00 00
mov word [ebx+0],0
mov dx,[esi+0x12]
; 用户任务tss对象中设置i/o映射信息
mov [ebx+102],dx
mov word [ebx+100],0
; 入口点的段内偏移
mov eax,[0x04]
; 用户任务tss对象中设置eip
mov [ebx+32],eax
; 获取当前eflags
pushfd
pop edx
; 用户任务tss对象中设置eflags
mov [ebx+36],edx
; 用户任务tss基地址
mov eax,[esi+0x14]
; 用户任务tss界限值
movzx ebx,word [esi+0x12]
; 字节计量 32位 段在内存 0特权级 系统段 代码段&已经访问
mov ecx,0x00408900
call flat_4gb_code_seg_sel:make_seg_descriptor
call flat_4gb_code_seg_sel:set_up_gdt_descriptor
; 用户任务tcb中设置tss段的选择子
mov [esi+0x18],cx
; 分配4kb物理页,将当前页目录表拷贝到新分配的物理页
call flat_4gb_code_seg_sel:create_copy_cur_pdir
mov ebx,[esi+0x14]
; 用户任务tss对象中设置cr3---用户任务页目录表的物理地址
mov dword [ebx+28],eax
popad
; 出栈并控制跳转,跳转前继续出栈8字节
ret 8
; 加入tcb链式结构
append_to_tcb_link:
cli
push eax
push ebx
mov eax,tcb_chain
.b0:
; 取出下一tcb对象
mov ebx,[eax]
or ebx,ebx
; 下一tcb对象不存在
jz .b1
mov eax,ebx
jmp .b0
.b1:
; 设置下一tcb对象
mov [eax],ecx
; 设置传入tcb对象的下一tcb对象为不存在
mov dword [ecx],0x00000000
pop ebx
pop eax
sti
ret
start:
mov eax,general_exception_handler
mov bx,flat_4gb_code_seg_sel
; P DPL 0 TYPE(1100) 000 参数个数(5位)
; 门指向代码在内存 特权级0 类别
mov cx,0x8e00
call flat_4gb_code_seg_sel:make_gate_descriptor
mov ebx,idt_linear_address
xor esi,esi
.idt0:
mov [ebx+esi*8],eax
mov [ebx+esi*8+4],edx
inc esi
cmp esi,19
jle .idt0
mov eax,general_interrupt_handler
mov bx,flat_4gb_code_seg_sel
; 门指向代码在内存 特权级0 类别
mov cx,0x8e00
call flat_4gb_code_seg_sel:make_gate_descriptor
mov ebx,idt_linear_address
.idt1:
mov [ebx+esi*8],eax
mov [ebx+esi*8+4],edx
inc esi
cmp esi,255
jle .idt1
mov eax,rtm_0x70_interrupt_handle
mov bx,flat_4gb_code_seg_sel
; 门指向代码在内存 特权级0 类别
mov cx,0x8e00
call flat_4gb_code_seg_sel:make_gate_descriptor
mov ebx,idt_linear_address
mov [ebx+0x70*8],eax
mov [ebx+0x70*8+4],edx
; 设置idt区域前256个8字节描述符&通知处理器
mov word [pidt],256*8-1
mov dword [pidt+2],idt_linear_address
lidt [pidt]
mov al,0x11
; 向0x20写入0x11
out 0x20,al
mov al,0x20
; 向0x21写入0x20
out 0x21,al
mov al,0x04
; 向0x21写入0x04
out 0x21,al
mov al,0x01
; 向0x21写入0x01
out 0x21,al
mov al,0x11
; 向0xa0写入0x11
out 0xa0,al
mov al,0x70
; 向0xa1写入0x70
out 0xa1,al
mov al,0x04
; 向0xa1写入0x04
out 0xa1,al
mov al,0x01
; 向0xa1写入0x01
out 0xa1,al
mov al,0x0b
; 得到0x8b
or al,0x80
; 向0x70写入0x8b
out 0x70,al
mov al,0x12
; 向0x71写入0x12
out 0x71,al
; 从0xa1读取1字节
in al,0xa1
; 读取1字节最低位设置为0
and al,0xfe
; 向0xa1写入a1
out 0xa1,al
mov al,0x0c
; 向0x70写入0x0c
out 0x70,al
; 从0x71读取1字节
in al,0x71
; 进入内核任务是硬件中断是关闭的,中断机制准备就绪后,才开启硬件中断
sti
mov ebx,message_0
call flat_4gb_code_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 flat_4gb_code_seg_sel:put_string
mov ebx,cpu_brand
call flat_4gb_code_seg_sel:put_string
mov ebx,cpu_brnd1
call flat_4gb_code_seg_sel:put_string
mov edi,salt
mov ecx,salt_items
.b4:
push ecx
mov eax,[edi+256]
mov bx,[edi+260]
; P DPL 0 TYPE(1100) 000 参数个数(5位)
; 门指向代码在内存 特权级3 类别
mov cx,1_11_0_1100_000_00000B
call flat_4gb_code_seg_sel:make_gate_descriptor
call flat_4gb_code_seg_sel:set_up_gdt_descriptor
; 符号中本来存储段选择子的现在存储门选择子
mov [edi+260],cx
add edi,salt_item_len
pop ecx
loop .b4
mov ebx,message_1
call far [salt_1+256]
; TCB对象结构
; 0x44 2字节头部选择子
; 0x40 2特权级栈的初始ESP
; 0x3e 2特权级栈选择子
; 0x3a 2特权级栈基地址
; 0x36 2特权级栈以4KB为单位的长度
; 0x32 1特权级栈的初始ESP
; 0x30 1特权级栈选择子
; 0x2c 1特权级栈基地址
; 0x28 1特权级栈以4kb为单位的长度
; 0x24 0特权级栈的初始ESP
; 0x22 0特权级栈选择子
; 0x1e 0特权级栈基地址
; 0x1a 0特权级栈以4kb为单位的长度
; 0x18 tss选择子
; 0x14 tss基地址
; 0x12 tss界限值
; 0x10 ldt选择子
; 0x0c ldt基地址
; 0x0a ldt当前界限值
; 0x06 程序加载基地址
; 0x04 任务状态
; 0x00 下一个tcb基地址
; 设置任务状态
mov word [core_tcb+0x04],0xffff
; 设置内核任务下一可用线性地址
mov dword [core_tcb+0x06],0x80100000
; 设置ldt当前界限值
mov word [core_tcb+0x0a],0xffff
; tcb对象加入链式结构
mov ecx,core_tcb
call append_to_tcb_link
; 分配4kb可用虚拟区域 ---用于存储内核任务tss对象
alloc_core_linear
; TSS格式
; I/OMapAddr Reserved 100
; Reserved LDT_Sector 96
; Reserved GS 92
; Reserved FS 88
; Reserved DS 84
; Reserved SS 80
; Reserved CS 76
; Reserved ES 72
; EDI 68
; ESI 64
; EBP 60
; ESP 56
; EBX 52
; EDX 48
; ECX 44
; EAX 40
; EFLAGS 36
; EIP 32
; CR3(PDBR) 28
; Reserved SS2 24
; ESP2 20
; Reserved SS1 16
; ESP1 12
; Reserved SS0 8
; ESP0 4
; Reserved Pre_Task_Tss 0
; tss对象设置前一任务tss选择子
mov word [ebx+0],0
mov eax,cr3
; tss对象设置页目录表---每个任务有自己的页目录表,这里存储的是页目录表的物理地址
; 页目录表,页表中目录项的地址信息也是物理地址的信息
mov dword [ebx+28],eax
; tss对象设置ldt选择子
mov word [ebx+96],0
; tss对象设置i/o映射
mov word [ebx+100],0
mov word [ebx+102],103
mov eax,ebx
mov ebx,103
; 字节计量 32位 段在内存 特权级0 系统段 代码段&被访问过
mov ecx,0x00408900
call flat_4gb_code_seg_sel:make_seg_descriptor
call flat_4gb_code_seg_sel:set_up_gdt_descriptor
mov [core_tcb+0x18],cx
ltr cx
alloc_core_linear
; 新的区域用于用户任务tcb对象
; TCB对象结构
; 0x44 2字节头部选择子
; 0x40 2特权级栈的初始ESP
; 0x3e 2特权级栈选择子
; 0x3a 2特权级栈基地址
; 0x36 2特权级栈以4KB为单位的长度
; 0x32 1特权级栈的初始ESP
; 0x30 1特权级栈选择子
; 0x2c 1特权级栈基地址
; 0x28 1特权级栈以4kb为单位的长度
; 0x24 0特权级栈的初始ESP
; 0x22 0特权级栈选择子
; 0x1e 0特权级栈基地址
; 0x1a 0特权级栈以4kb为单位的长度
; 0x18 tss选择子
; 0x14 tss基地址
; 0x12 tss界限值
; 0x10 ldt选择子
; 0x0c ldt基地址
; 0x0a ldt当前界限值
; 0x06 程序加载基地址
; 0x04 任务状态
; 0x00 下一个tcb基地址
; tcb对象设置任务状态
mov word [ebx+0x04],0
; tcb对象设置用户任务下一可用线性地址
mov dword [ebx+0x06],0
; tcb对象设置ldt当前界限值
mov word [ebx+0x0a],0xffff
push dword 50
push ebx
call load_relocate_program
mov ecx,ebx
call append_to_tcb_link
alloc_core_linear
; 新的区域用于用户任务tcb对象
; TCB对象结构
; 0x44 2字节头部选择子
; 0x40 2特权级栈的初始ESP
; 0x3e 2特权级栈选择子
; 0x3a 2特权级栈基地址
; 0x36 2特权级栈以4KB为单位的长度
; 0x32 1特权级栈的初始ESP
; 0x30 1特权级栈选择子
; 0x2c 1特权级栈基地址
; 0x28 1特权级栈以4kb为单位的长度
; 0x24 0特权级栈的初始ESP
; 0x22 0特权级栈选择子
; 0x1e 0特权级栈基地址
; 0x1a 0特权级栈以4kb为单位的长度
; 0x18 tss选择子
; 0x14 tss基地址
; 0x12 tss界限值
; 0x10 ldt选择子
; 0x0c ldt基地址
; 0x0a ldt当前界限值
; 0x06 程序加载基地址
; 0x04 任务状态
; 0x00 下一个tcb基地址
mov word [ebx+0x04],0
mov dword [ebx+0x06],0
mov word [ebx+0x0a],0xffff
push dword 100
push ebx
call load_relocate_program
mov ecx,ebx
call append_to_tcb_link
.core:
mov ebx,core_msg0
call flat_4gb_code_seg_sel:put_string
jmp .core
core_code_end:
SECTION core_trail
core_end:
3.用户任务
program_length dd program_end
entry_point dd start
salt_position dd salt_begin
salt_items dd (salt_end-salt_begin)/256
salt_begin:
PrintString db '@PrintString'
times 256-($-PrintString) db 0
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
ReadDiskData db '@ReadDiskData'
times 256-($-ReadDiskData) db 0
PrintDwordAsHex db '@PrintDwordAsHexString'
times 256-($-PrintDwordAsHex) db 0
salt_end:
message_0 db ' User task A->;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;'
db 0x0d,0x0a,0
[bits 32]
start:
mov ebx,message_0
call far [PrintString]
jmp start
call far [TerminateProgram]
program_end:
4.用户任务2
program_length dd program_end
entry_point dd start
salt_position dd salt_begin
salt_items dd (salt_end-salt_begin)/256
salt_begin:
PrintString db '@PrintString'
times 256-($-PrintString) db 0
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
ReadDiskData db '@ReadDiskData'
times 256-($-ReadDiskData) db 0
PrintDwordAsHex db '@PrintDwordAsHexString'
times 256-($-PrintDwordAsHex) db 0
salt_end:
message_0 db ' User task B->$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$'
db 0x0d,0x0a,0
[bits 32]
start:
mov ebx,message_0
call far [PrintString]
jmp start
call far [TerminateProgram]
program_end: