分页模式任务切换测试
该代码,在分页模式下,有两个ring3级的任务,分别输出WY和LS两个字串。
程序为两个任务建立了3级的代码段和数据堆栈段,建立了IDT段,只设置了一个页表,只映射了4M内存空间。
内核代码被复制到0x2000h处,0x0——0x1FFF被用来存放pdt和第一个pet,进入内核代码前,已经为内核代码建立过临时代码段和数据堆栈段,大小都是4G的大段。
内核代码首先初始化内核代码的TSS描述符,然后设置时钟中断TSS和其TSS描述符,;设置任务1和任务2的任务状态段、任务描述符、LDT段描述符和任务状态表。然后设置任务状态表,任务状态表包含任务是否可用和任务的时间片,其中内核任务不作为调度对象,所以在时钟中断中始终将内核任务的可用标志位置位不可用。接着去设置IDT表,将所有的中段都指向其中一个中断函数,再设置8h时钟中断,这个是bios设置的,其实我们需要将这个修改,因为在保护模式下这应该为一个异常处理中断,但只要保证这个程序里不犯这样的错误就行了。
最后设置pdt和pet,并将pdt的第一个表项u/s位设为1这样,其下的pet的表项才可能被ring3的代码访问,将第三个pet表项u/s射为1,映射到ring3的任务代码页,并将0xb8xxx线性地址映射到0xf0xxx,将0xf0xxx映射到0xb8xxx并将f0项的u/s位设为1,这样任务代码就可以访问显存,显示信息。
在时钟中断代码中,主要是搜索可用的任务,然后修改时钟TSS的link字段,修改相对应的任务状态表,最后转移到可用任务代码里。
目前没有实验的是每一个任务独立4G的空间。
下面是内核代码。
setup.asm
kernel.asm
- ;定义一些段和描述符中字段的偏移
- ;任务状态段各字段偏移
- tss_link equ 0H
- tss_esp0 equ 4H
- tss_ss0 equ 8H
- tss_esp1 equ 0CH
- tss_ss1 equ 10H
- tss_esp2 equ 14H
- tss_ss2 equ 18H
- tss_cr3 equ 1CH
- tss_eip equ 20H
- tss_eflags equ 24H
- tss_eax equ 28H
- tss_ecx equ 2CH
- tss_edx equ 30H
- tss_ebx equ 34H
- tss_esp equ 38H
- tss_ebp equ 3CH
- tss_esi equ 40H
- tss_edi equ 44H
- tss_es equ 48H
- tss_cs equ 4CH
- tss_ss equ 50H
- tss_ds equ 54H
- tss_fs equ 58H
- tss_gs equ 5CH
- tss_ld equ 60H
- tss_IO equ 66H
- bits 32
- org 2000h
- jmp main
- gdt_desc:
- dw 0x820
- gdtaddr dd gdt
- ;=========================
- gdt:
- NULL:
- dw 0x0,0x0,0x0,0x0
- SystemCode:
- dw 0xFFFF
- dw 0x0
- dw 0x9A00
- dw 0x00CF
- SystemData:
- dw 0xFFFF
- dw 0x0
- dw 0x9200
- dw 0x00CF
- CLKTaskDesc: ;时钟任务描述符表
- dw 0x69
- dw 0x0
- dw 0x8900
- dw 0x0080
- UserSpace:
- user times 0x800 db 0 ;留给用户进程的全局描述符,模式采用类似于linux的设置,
- ;ldt0,tss0,ldt1,tss1如此类推,总共可以支持128个用户任务
- ;========================= ;中断描述符表伪描述符
- idt_desc:
- dw 0x800
- idtaddr dd idt
- ;========================= ;中断描述符表
- idt:
- times 0x800 db 0
- ;=========================
- CLKTask: ;时钟任务状态段
- times 102 db 0
- dw $+2 ;I/O许可位图偏移
- db 0xFF ;I/O许可位图结束字节
- KernelTask: ;内核任务状态段
- times 102 db 0
- dw $+2
- db 0xFF ;I/O许可位图结束字节
- TestTask1: ;Task1的任务状态段
- times 102 db 0
- dw $+2
- db 0xFF
- TestTask2: ;Task2的任务状态段。
- times 102 db 0
- dw $+2
- db 0xFF
- LDT1: ;Task1的局部描述符表
- Task1KnlStack:
- dw 0xFFFF
- dw 0x0
- dw 0x9200
- dw 0x00CF
- Task1CodeSeg:
- dw 0xFFFF
- dw 0x0
- dw 0xFA00
- dw 0x00CF
- Task1DataSeg:
- dw 0xFFFF
- dw 0x0
- dw 0xF200
- dw 0x00CF
- LDT2: ;Task2的局部描述符表
- Task2KnlStack:
- dw 0xFFFF
- dw 0x0
- dw 0x9200
- dw 0x00CF
- Task2CodeSeg:
- dw 0xFFFF
- dw 0x0
- dw 0xFA00
- dw 0x00CF
- Task2DataSeg:
- dw 0xFFFF
- dw 0x0
- dw 0xF200
- dw 0x00CF
- ;===========任务状态表
- task_status equ 0h
- task_cpu_time equ 1h
- task_link equ 2h
- TaskStatusTable:
- Task0:
- db 1
- db 0
- dw 0
- Task1:
- db 1
- db 0
- dw 0
- Task2:
- db 0
- db 0
- dw 0
- ;===============代码开始处
- main:
- mov ax,0x10 ;虽然setup程序已经设置过段寄存器,为了程序的正常运行,还是重新设置下
- mov ds,ax
- xor esi,esi
- xor edi,edi
- mov esp,0xA0000
- xor ebp,ebp
- ;设置内核TSS描述符
- mov esi,UserSpace+8 ;获得内核TSSDESC的地址
- mov eax,KernelTask ;内核TSS的地址->eax
- mov word [esi+2],ax ;设置base addr的低16位
- shr eax,16 ;将高16位送入到ax
- mov byte [esi+4],al ;设置base addr中间8位
- mov byte [esi+7],ah ;设置base addr高8位
- mov word [esi],0x69 ;设置TSS的大小
- mov byte [esi+5],0x89 ;设置描述符的类型和访问权限
- mov byte [esi+6],0x80 ;设置段粒度和其他属性
- ;设置时钟TSS
- mov esi,CLKTask
- mov dword [esi+tss_cr3],0
- mov dword [esi+tss_eip],WYOS_clock_task
- mov dword [esi+tss_eflags],0x202
- mov word [esi+tss_es],0x10
- mov word [esi+tss_cs],0x8
- mov word [esi+tss_ds],0x10
- mov word [esi+tss_fs],0x10
- mov word [esi+tss_gs],0x10
- mov word [esi+tss_ss],0x10
- mov dword [esi+tss_esp],0xa0000
- ;设置时钟任务状态段描述符
- mov eax,CLKTask
- mov word [CLKTaskDesc+2],ax
- shr eax,16
- mov byte [CLKTaskDesc+4],al
- mov byte [CLKTaskDesc+7],ah
- ;设置任务1和任务2的任务状态段、任务描述符、LDT段描述符和任务状态表
- ;Task1 TSS
- mov esi,TestTask1
- mov word [esi+tss_ss0],0x4
- mov dword [esi+tss_esp0],0x80000
- mov dword [esi+tss_cr3],0
- mov word [esi+tss_ldt],0x30
- mov dword [esi+tss_eip],Task1Code
- mov dword [esi+tss_eflags],0x202
- mov word [esi+tss_es],0x17
- mov word [esi+tss_cs],0xf
- mov word [esi+tss_ds],0x17
- mov word [esi+tss_fs],0x17
- mov word [esi+tss_gs],0x17
- mov word [esi+tss_ss],0x17
- mov dword [esi+tss_esp],0x90000
- ;TSS DESC
- mov esi,UserSpace+3*8
- mov eax,TestTask1
- mov word [esi+2],ax
- shr eax,16
- mov byte [esi+4],al
- mov byte [esi+7],ah
- mov word [esi],0x69
- mov byte [esi+5],0x89
- mov byte [esi+6],0x0
- ;LDT DESC
- mov esi,UserSpace+2*8
- mov eax,0x18
- mov word [esi],ax
- mov eax,LDT1
- mov word [esi+2],ax
- shr eax,16
- mov byte [esi+4],al
- mov byte [esi+7],ah
- mov byte [esi+5],0x82
- mov byte [esi+6],0x0
- ;Task2
- ;Task2 TSS
- mov esi,TestTask2
- mov word [esi+tss_ss0],0x4
- mov dword [esi+tss_esp0],0x80000
- mov dword [esi+tss_cr3],0
- mov word [esi+tss_ldt],0x40
- mov dword [esi+tss_eip],Task2Code
- mov dword [esi+tss_eflags],0x202
- mov word [esi+tss_es],0x17
- mov word [esi+tss_cs],0xf
- mov word [esi+tss_ds],0x17
- mov word [esi+tss_fs],0x17
- mov word [esi+tss_gs],0x17
- mov word [esi+tss_ss],0x17
- mov dword [esi+tss_esp],0x90000
- ;TSS DESC
- mov esi,UserSpace+5*8
- mov eax,TestTask2
- mov word [esi+2],ax
- shr eax,16
- mov byte [esi+4],al
- mov byte [esi+7],ah
- mov word [esi],0x69
- mov byte [esi+5],0x89
- mov byte [esi+6],0x0
- ;LDT DESC
- mov esi,UserSpace+4*8
- mov eax,0x18
- mov word [esi],ax
- mov eax,LDT2
- mov word [esi+2],ax
- shr eax,16
- mov byte [esi+4],al
- mov byte [esi+7],ah
- mov byte [esi+5],0x82
- mov byte [esi+6],0x0
- ;TaskStatus
- mov esi,TaskStatusTable
- mov byte [esi+task_status],1
- mov byte [esi+task_cpu_time],0
- mov word [esi+task_link],0x28
- add esi,4
- mov byte [esi+task_status],0
- mov byte [esi+task_cpu_time],5
- mov word [esi+task_link],0x38
- add esi,4
- mov byte [esi+task_status],0
- mov byte [esi+task_cpu_time],5
- mov word [esi+task_link],0x48
- ;设置IDT中的时钟中断任务们描述符,暂定为8H,
- mov esi,idt
- mov ecx,256
- SetIdtItem:
- mov eax,WYOS_default_interrupt
- mov word [esi],ax
- mov word [esi+2],0x8
- mov byte [esi+5],0x8E
- shr ax,16
- mov word [esi+6],ax
- add esi,8
- loop SetIdtItem
- mov esi,idt
- mov word [esi+(8H*8+2)],0x18 ;填写8H中断
- mov byte [esi+(8H*8+5)],0x85
- lgdt [gdt_desc]
- lidt [idt_desc]
- mov ax,0x28
- ltr ax
- jmp clear
- clear: ;跳转一下清除高速缓冲寄存器
- ; sti
- ;setpage: ;设置页目录表和第一个页表,先映射4M内存
- setpdt:
- mov ax,0x10
- mov es,ax
- xor edi,edi
- mov eax,0
- mov ecx,0x400
- rep stosd
- ;设置第一个页目录表项
- xor edi,edi
- mov eax,0x1007
- stosd
- setpet:
- mov edi,0x1000
- mov ecx,0x400
- mov eax,3
- setpetvalue:
- stosd
- add eax,0x1000
- loop setpetvalue
- ;设置显示寄存器的虚拟地址为0xf0xxx而0xb8000则指向物理0xf0xxx这一块区间
- mov edi,0x1000
- mov dword [edi+0x3*4],0x3007
- mov dword [edi+0xb8*4],0xf0003
- mov dword [edi+0xf0*4],0xb8007
- ;设置完成进入保护模式
- mov eax,0
- mov cr3,eax
- mov eax,cr0
- or eax,0x80000000
- mov cr0,eax
- jmp clear_pregetinstruction
- clear_pregetinstruction:
- mov byte [0xf0004],'P'
- mov byte [0xf0006],'A'
- mov byte [0xf0008],'G'
- mov byte [0xf000a],'I'
- mov byte [0xf000c],'N'
- mov byte [0xf000e],'G'
- sti
- die: jmp $
- jmp die
- WYOS_default_interrupt:
- push eax
- pushf
- mov al,[WYOS_interrupt_string]
- mov byte [0xf0002],al
- cmp al,0xFF
- jb save
- reset:
- mov al,0
- save:
- inc al
- mov byte [WYOS_interrupt_string],al
- mov al , 0x20
- out 0x20 , al
- out 0xa0 , al
- popf
- pop eax
- iret
- WYOS_clock_task:
- push eax
- push edx
- push esi
- push edi
- ;获得正在运行的任务的任务门选择子
- mov esi,CLKTask
- mov ax,word [esi+tss_link]
- ;通过任务门选择子获得正在运行的任务TID
- shr ax,3
- sub ax,5
- shr ax,1
- or ax,ax
- jnz CheckCpuTime ;如果不是0号任务则检查当前任务的剩余时间片
- ; ;设置返回任务的TID
- mov eax,0
- CheckCpuTime: ;eax包含指向当前的任务的TID
- mov edx,eax ;将TID保存到edx中
- shl eax,2
- mov esi,TaskStatusTable
- add esi,eax
- mov al,byte [esi+task_cpu_time]
- or al,al
- jnz SetCpuTime ;如果剩余时间片不为0,则继续运行该任务
- ;剩余时间片为0,则寻找一个可用的任务,并将当前任务设为可用
- mov ecx,2
- push esi ;保存当前任务的状态表的指针
- mov esi,TaskStatusTable
- search:
- mov al,byte [esi+task_status]
- or al,al
- jz find
- add esi,4
- loop search
- find:
- mov eax,esi ;先将找到的任务的状态表的指针保存到eax,因为要修改以前的任务为可用
- pop esi
- or edx,edx ;如果是0号任务,则不置为可用
- jz SetTaskStatus
- mov byte [esi+task_status],0
- SetTaskStatus:
- mov esi,eax
- mov byte [esi+task_status],1 ;设找到的可用任务为不可用
- mov byte [esi+task_cpu_time],6 ;设为6这是因为下面的SetCpuTime会自动将任务的时间片去1
- SetReturnTSSSel: ;设置返回的任务门选择子
- mov edi,CLKTask
- mov ax,word [esi+task_link]
- mov word [edi+tss_link],ax
- ;将目标任务设为忙
- mov edx,2
- sub edx,ecx
- shl edx,1
- add edx,5
- shl edx,3
- mov edi,gdt
- add edi,edx
- mov edx,[edi+5]
- or edx,0x2
- mov [edi+5],edx
- SetCpuTime: ;esi中包含可用的任务的状态表指针
- mov al,byte [esi+task_cpu_time]
- dec al
- mov byte [esi+task_cpu_time],al
- CLKTaskEnd:
- pop edi
- pop esi
- pop edx
- pop eax
- mov al , 0x20
- out 0x20 , al
- out 0xa0 , al
- iret
- jmp WYOS_clock_task
- WYOS_clock_task_delay:
- ret
- ;============Task1 Code============
- Task1Code:
- mov esi,dword [WYOS_cursor_position]
- cmp esi,0xf0fff
- jc Task1Show
- mov esi,0xf0000
- Task1Show:
- mov ecx,0xFFFF
- Task1_delay:
- loop Task1_delay
- mov byte [esi],'W'
- inc esi
- inc esi
- mov dword [WYOS_cursor_position],esi
- mov byte [esi],'Y'
- inc esi
- inc esi
- mov dword [WYOS_cursor_position],esi
- jmp Task1Code
- ;============Task2 Code============
- Task2Code:
- mov esi,dword [WYOS_cursor_position]
- cmp esi,0xf0fff
- jc Task2Show
- mov esi,0xf0000
- Task2Show:
- mov ecx,0xFFFF
- Task2_delay:
- loop Task2_delay
- mov byte [esi],'L'
- inc esi
- inc esi
- mov dword [WYOS_cursor_position],esi
- mov byte [esi],'S'
- inc esi
- inc esi
- mov dword [WYOS_cursor_position],esi
- jmp Task2Code
- ;==================
- ;其它数据
- WYOS_font_character db 'Y'
- WYOS_interrupt_string db 0
- WYOS_cursor_position dd 0xf0000