汇编实例解析--利用tcb,tss,全局tss,ldt管理任务实现任务切换

1.引导程序

         ; 职责:
         ; 1.完成实模式到保护模式切换
         ; 2.安装保护模式下4个基础段描述符
         ; 3.将指定位置物理磁盘程序加载到指定物理内存位置
         ; 4.为加载的程序构造&安装3个段描述符
         ; 5.将执行流程跳转到加载的程序      
         core_base_address equ 0x00040000
         core_start_sector equ 0x00000001
         ; 刚刚进入主引导程序时候
         ; 进程位于物理内存0x0000:0x7c00处,cs的内容是0x0000   
         mov ax,cs      
         mov ss,ax
         mov sp,0x7c00

         mov eax,[cs:pgdt+0x7c00+0x02]      
         xor edx,edx
         mov ebx,16
         div ebx    

         mov ds,eax                     
         mov ebx,edx                       
         
         ; ds:edx执行gdt区域起始位置

         ; gdt中索引1
         ; 段基地址为0x0000 0000
         ; 段内的数据是32位的
         ; 4GB尺寸
         ; 可读,可写的数据段
         mov dword [ebx+0x08],0x0000ffff
         mov dword [ebx+0x0c],0x00cf9200   
         
         ; gdt中索引2
         ; 段基地址0x0000 7c00
         ; 段尺寸512
         ; 段是只执行的代码段
         mov dword [ebx+0x10],0x7c0001ff
         mov dword [ebx+0x14],0x00409800    
         
         ; gdt中索引3
         ; 段基地址0x0000 7c00
         ; 段尺寸4KB
         ; 段是可读,可写的栈段
         mov dword [ebx+0x18],0x7c00fffe
         mov dword [ebx+0x1c],0x00cf9600
         
         ; gdt中索引4
         ; 段基地址为0x000b 8000
         ; 段尺寸0x8000
         ; 可读,可写的数据段
         mov dword [ebx+0x20],0x80007fff
         mov dword [ebx+0x24],0x0040920b    
         
         ; gdt界限=gdt尺寸-1
         mov word [cs: pgdt+0x7c00],39   
         ; gdt界限,基地址传递到处理器  
         lgdt [cs: pgdt+0x7c00]
         
         in al,0x92                         
         or al,0000_0010B
         out 0x92,al                        
         cli                               

         mov eax,cr0
         or eax,1
         mov cr0,eax  
         ; 保护模式下,0x0010:flush解释为
         ; 0x0010得到RPL为0,位于gdt中,索引为2的8字节描述符描述的段                     
         jmp dword 0x0010:flush            
         [bits 32]               
  flush:       
         mov eax,0x0008  
         ; 设置ds指向全局数据段 
         mov ds,eax
         
         ; 这里及后续使用的栈区域是gdt中索引为3的描述符指向的
         mov eax,0x0018   
         mov ss,eax
         ; 描述符中的段界限值*0x1000 + 0xFFF + 1 <= [ESP-操作数长度] <= 0xFFFFFFFF
         xor esp,esp   
         mov esp, 0xffffffff                     
         
         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                            
         ; 到这里,eax代表了,进程剩余部门占据的磁盘扇区数
   @1:
         or eax,eax                         
         ; 如果进程已经全部拷贝到物理内存
         jz setup                          
         mov ecx,eax        
         mov eax,core_start_sector
         inc eax                            
   @2:
         call read_hard_disk_0
         inc eax
         loop @2                            
         
         ; 此时内核进程【操作系统】已经全部拷贝到了物理内存
 setup:
         mov esi,[0x7c00+pgdt+0x02]   
         
         ; 操作系统的
         ; 例程段,代码段,数据段,栈段需要在引导程序中构造并安装到gdt
         mov eax,[edi+0x04]   
         mov ebx,[edi+0x08]                 
         sub ebx,eax
         dec ebx        
         add eax,edi                     
         mov ecx,0x00409800   
         ; 为进程公共例程段构造8位段描述符
         call make_gdt_descriptor

         ; gdt索引5
         ; 进程例程段描述符        
         mov [esi+0x28],eax
         mov [esi+0x2c],edx
        
         mov eax,[edi+0x08]               
         mov ebx,[edi+0x0c]                
         sub ebx,eax
         dec ebx     
         add eax,edi              
         mov ecx,0x00409200                 
         call make_gdt_descriptor
         ; gdt索引6
         ; 进程数据段描述符         
         mov [esi+0x30],eax
         mov [esi+0x34],edx 
      
         mov eax,[edi+0x0c]               
         mov ebx,[edi+0x00]              
         sub ebx,eax
         dec ebx      
         add eax,edi    
         mov ecx,0x00409800             
         call make_gdt_descriptor
         ; gdt索引7
         ; 内核代码段
         mov [esi+0x38],eax
         mov [esi+0x3c],edx
         ; gdt界限
         mov word [0x7c00+pgdt],63         

         ; 将gdt界限,基地址通知处理器
         lgdt [0x7c00+pgdt]   
         ; 从指向物理内存位置取4字节段内偏移,2字节段选择子,并据此跳转
         ; 执行流程从引导程序进入操作系统
         jmp far [edi+0x10]  
         
; 传入:eax是磁盘起始逻辑扇区
; 传入:ds:ebx构成存放磁盘扇区起始物理内存位置
; 传出:ds:ebx磁盘扇区物理内存尾后位置
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

; 依据传入构造8位edx:eax描述符
make_gdt_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   
         ret
      
         pgdt             dw 0
                          dd 0x00007e00    
         times 510-($-$$) db 0
                          db 0x55,0xaa

2.内核程序

         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       
; 功能函数  
; 显示ds:ebx指向位置的一个字符串
; 可处理换行符,回车符
; 字符串终止标记是'\0'    
; 执行后ds:ebx指向终止符
put_string:                                
         push ecx
  .getc:
         mov cl,[ebx]
         or cl,cl
         jz .exit
         call put_char
         inc ebx
         jmp .getc
  .exit:
         pop ecx
         retf                              
put_char:                                   
         pushad
         mov dx,0x3d4
         mov al,0x0e
         out dx,al
         inc dx                            
         in al,dx                         
         mov ah,al

         dec dx                           
         mov al,0x0f
         out dx,al
         inc dx                            
         in al,dx                          
         mov bx,ax                         

         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         
         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                       
         mov edi,0x00                      
         mov ecx,1920
         rep movsd
         mov bx,3840                       
         mov ecx,80                         
  .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                             
         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位置
; 执行后ds:ebx指向扇区在物理内存尾后位置
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
         retf                              

; 功能函数
; 将edx这个32位二进制按十六进制格式显示
put_hex_dword:                             
         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

; 函数功能
; 划分一块物理内存区域
; ecx调用时,传递尺寸。调用后,保存了划分区域起始位置。 
; ds:ram_alloc,调用后更新为划分区域尾后位置。
allocate_memory:                           
         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               
         ;cmovnz eax,ebx
         mov eax,ebx                    
         mov [ram_alloc],eax              
         pop ebx
         pop eax
         pop ds
         retf

; 功能:
; edx:eax写入gdt区域,更新gdt界限,通知处理器
; cx记录edx:eax这个描述符的选择子
set_up_gdt_descriptor:                     
         push eax
         push ebx
         push edx
         push ds
         push es

         mov ebx,core_data_seg_sel         
         mov ds,ebx
         
         ; ds:pgdt处6字节存储gdt界限,基地址
         sgdt [pgdt]                        

         mov ebx,mem_0_4_gb_seg_sel
         mov es,ebx

         movzx ebx,word [pgdt]              
         inc bx    
         ; gdt尾后位置                        
         add ebx,[pgdt+2]                  

         mov [es:ebx],eax
         mov [es:ebx+4],edx

         add word [pgdt],8                 
         
         ; 向gdt区域写入8字节,更新gdt界限后再次通知处理器
         lgdt [pgdt]                       
         mov ax,[pgdt]                    
         xor dx,dx
         mov bx,8
         div bx                           
         mov cx,ax
         ; cx是添加的8位描述符对应的选择子
         shl cx,3                         

         pop es
         pop ds
         pop edx
         pop ebx
         pop eax
         retf

; 功能:
; 提供eax,edx,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

; 功能
; 构造门描述符
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                                   

; 功能函数
; 终止当前任务                         
terminate_current_task:  
         ; pushfd:然后将32位标志寄存器EFLAGS压入堆栈                   
         pushfd
         ; ss:esp这是取出栈顶4字节,这是EFLAGS
         mov edx,[esp]   
         ; 类似出栈,只不过栈段元素没有存储                  
         add esp,4                         

         mov eax,core_data_seg_sel
         mov ds,eax
         
         test dx,0100_0000_0000_0000B      
         jnz .b1                           
         mov ebx,core_msg1                  
         call sys_routine_seg_sel:put_string
         jmp far [prgman_tss]               
       
  .b1: 
         mov ebx,core_msg0
         call sys_routine_seg_sel:put_string
         iretd
sys_routine_end:

; 数据段
SECTION core_data vstart=0                 
         pgdt             dw  0            
                          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  terminate_current_task
                          dw  sys_routine_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
         
         bin_hex          db '0123456789ABCDEF'
         core_buf   times 2048 db 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
         
         prgman_tss       dd  0   
                          dw  0             

         prgman_msg1      db  0x0d,0x0a
                          db  '[PROGRAM MANAGER]: Hello! I am Program Manager,'
                          db  'run at CPL=0.Now,create user task and switch '
                          db  'to it by the CALL instruction...',0x0d,0x0a,0
                 
         prgman_msg2      db  0x0d,0x0a
                          db  '[PROGRAM MANAGER]: I am glad to regain control.'
                          db  'Now,create another user task and switch to '
                          db  'it by the JMP instruction...',0x0d,0x0a,0
                 
         prgman_msg3      db  0x0d,0x0a
                          db  '[PROGRAM MANAGER]: I am gain control again,'
                          db  'HALT...',0

         core_msg0        db  0x0d,0x0a
                          db  '[SYSTEM CORE]: Uh...This task initiated with '
                          db  'CALL instruction or an exeception/ interrupt,'
                          db  'should use IRETD instruction to switch back...'
                          db  0x0d,0x0a,0

         core_msg1        db  0x0d,0x0a
                          db  '[SYSTEM CORE]: Uh...This task initiated with '
                          db  'JMP instruction,  should switch to Program '
                          db  'Manager directly by the JMP instruction...'
                          db  0x0d,0x0a,0

       benhao_1             db  0x0d,0x0a
                            db  '1111111111benhao_1',0
       benhao_2             db  0x0d,0x0a
                            db  '22222222222benhao_2',0
       benhao_3             db  0x0d,0x0a
                            db  '333333333benhao_3',0
       benhao_4             db  0x0d,0x0a
                            db  '44444444444benhao_4',0
       benhao_5             db  0x0d,0x0a
                            db  '5555555555benhao_5',0
       benhao_6             db  0x0d,0x0a
                            db  '666666666666benhao_6',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
         ; 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基地址
         ; ds:ebx是tcb对象基地址
         mov edi,[ebx+0x0c]                 
         
         xor ecx,ecx
         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       
         ; cx是写入ldt的描述符对应的段的选择子【RPL为0】                   
         or cx,0000_0000_0000_0100B        

         pop ds
         pop edi
         pop edx
         pop eax
         ret

; 函数过程
; 每个应用有唯一的tcb对象
; 每个应用有唯一的tss对象
; 每个应用需有唯一的ldt区域,ldt区域描述符位于gdt
; 每个应用需在加载程序中从磁盘加载到物理内存
; 每个应用需要加载程序中完成符号表重定位
; 每个应用自己的段需注册到ldt区域,且将每个段的选择子写入应用头部对应位置。
; 关于特权级
; 系统例程特权级是0
; 外部应用调系统例程,外部应用代码段特权级是3
; 外部应用调系统例程通过调用门描述符,RPL是3。
load_relocate_program:     
         ; 多个寄存器顺序入栈 
         ; EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI 
         pushad
         push ds
         push es
         
         mov ebp,esp                       
         mov ecx,mem_0_4_gb_seg_sel
         mov es,ecx
         mov esi,[ebp+11*4]               
         
         ; 划分尺寸为160的物理区域用于用户进程的ldt
         mov ecx,160   
         call sys_routine_seg_sel:allocate_memory
         ; 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对象中记录ldt基地址       
         mov [es:esi+0x0c],ecx   
         ; tcb对象中记录ldt当前界限           
         mov word [es:esi+0x0a],0xffff      

         mov eax,core_data_seg_sel
         mov ds,eax                        

         ; 应用进程在磁盘起始逻辑扇区号
         mov eax,[ebp+12*4]                
         mov ebx,core_buf              
         ; 将磁盘扇区读取一块到内核缓冲区         
         call sys_routine_seg_sel:read_hard_disk_0
         
         ; 应用首个扇区头4字节
         mov eax,[core_buf]               
         mov ebx,eax
         and ebx,0xfffffe00                 
         add ebx,512                       
         test eax,0x000001ff               
         ;cmovnz eax,ebx      
         ; eax是一个可被512整除,且尺寸足够容纳整个磁盘应用的尺寸              
         mov eax, ebx

         mov ecx,eax                       
         ; 申请一块物理内存区域用于存储磁盘应用
         call sys_routine_seg_sel:allocate_memory
         ; 在tcb对象中记录程序加载基地址
         mov [es:esi+0x06],ecx              
       
         mov ebx,ecx                        
         xor edx,edx
         mov ecx,512
         div ecx
         
         ; 循环读取磁盘应用的全部扇区到物理内存
         ; ecx是磁盘应用对应的扇区数
         mov ecx,eax                       
         mov eax,mem_0_4_gb_seg_sel         
         mov ds,eax
         ; 磁盘首个扇区编号
         mov eax,[ebp+12*4]                 
  .b1:
         ; 读取一个磁盘扇区到物理内存
         call sys_routine_seg_sel:read_hard_disk_0
         inc eax
         loop .b1                          

         ; 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基地址
         ; es:esi指向应用tcb基础地址
         ; ds:edi指向应用自身
         mov edi,[es:esi+0x06]            
         
         ; 为应用tcb对象各个部分填充有效值
         ; 将应用加载到物理内存
         ; 为应用构造头部段,代码段,数据段,栈段。
         ; 这样段存储在应用的ldt
         ; 应用的ldt也是一个段存储在gdt
         ; 应用的tcb也是一个段存储在gdt
         ; 应用的tss也是一个段存储在gdt
         
         ; 为应用头部段构造描述符并将其存储到ldt
         mov eax,edi    
         mov ebx,[edi+0x04]
         dec ebx
         mov ecx,0x0040f200   
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi    
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B  
         ; 头部段信息tcb中需记录  
         mov [es:esi+0x44],cx     

         mov [edi+0x04],cx                  
         mov eax,edi
         add eax,[edi+0x14]      
         mov ebx,[edi+0x18]
         dec ebx                           
         mov ecx,0x0040f800                 
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                       
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B   
         ; 代码段信息tcb中需记录      
         mov [edi+0x14],cx                  

         mov eax,edi
         add eax,[edi+0x1c]                
         mov ebx,[edi+0x20]                
         dec ebx                            
         mov ecx,0x0040f200                 
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                       
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B   
         ; 数据段信息tcb中需记录    
         mov [edi+0x1c],cx                
         
         mov ecx,[edi+0x0c]                
         mov ebx,0x000fffff
         sub ebx,ecx                       
         mov eax,4096
         mul ecx                         
         mov ecx,eax   
         call sys_routine_seg_sel:allocate_memory
         add eax,ecx                        
         mov ecx,0x00c0f600                 
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                       
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B    
         mov [edi+0x08],cx                 

         ; 为应用头部段,代码段,数据段,栈段构造描述符,写入应用自己的ldt
         ; 将相应段的选择子写入应用头部对应位置

         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]     
         ; es:edi指向首个符号位置       
         add edi,0x28                     
  .b2: 
         ; 寄存器入栈
         push ecx
         push edi
         
         ; 对内核中每个符号执行一次与应用中当前符号的匹配处理
         ; ecx此时存储了内核符号数
         mov ecx,salt_items
         ; esi此时存储了内核首个符号段内偏移
         mov esi,salt
  .b3:
         ; 寄存器入栈
         push edi
         push esi
         push ecx
         
         ; ecx存储最多比较次数
         mov ecx,64           
         repe cmpsd   
         ; 结果不匹配,跳转到b4                     
         jnz .b4
         ; 这里是匹配了
         ; [ds:esi]此符号的段内偏移
         mov eax,[esi]             
         ; [es:edi-256]应用中此符号头4字节        
         mov [es:edi-256],eax   
         ; [ds:esi+4]此符号所在段的选择子          
         mov ax,[esi+4]
         or ax,0000000000000011B       
         ; [es:edi-252]应用中此符号第5,6字节存储此符号所在段的选择子[RPL为3]  
         mov [es:edi-252],ax              
  .b4:
         pop ecx
         pop esi
         ; esi指向内核中当前符号的下一符号
         add esi,salt_item_len
         pop edi                           
         loop .b3
       
         pop edi
         ; edi执行应用中当前符号的下一符号
         add edi,256
         pop ecx
         loop .b2
         
         ; 以上对应用程序每个符号完成符号重新定位
         ; 每个应用内符号头6字节是符号段内偏移,符号选择子【对应的不是段描述符,而是调用门描述符】
         ; 调用门描述符类型是依从的。RPL是3。调用门描述符里特权级是0。
         mov esi,[ebp+11*4]                 
         
         ; 从低特权级变化到高特权级时候,栈要配套切换。
         ; 对于特权级3的应用需准备号特权级0,1,2的栈
         mov ecx,4096
         mov eax,ecx     
         mov [es:esi+0x1a],ecx
         shr dword [es:esi+0x1a],12  
         call sys_routine_seg_sel:allocate_memory
         add eax,ecx                       
         mov [es:esi+0x1e],eax    
         mov ebx,0xffffe                   
         mov ecx,0x00c09600   
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi         
         ; 额外的栈段首先需要在ldt注册
         ; 然后,应用tcb中也需要注册              
         call fill_descriptor_in_ldt
         mov [es:esi+0x22],cx   
         mov dword [es:esi+0x24],0xffffffff         
      
         mov ecx,4096
         mov eax,ecx                       
         mov [es:esi+0x28],ecx
         shr [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
         ; 1特权级栈的rpl是1
         or cx,0000_0000_0000_0001         
         mov [es:esi+0x30],cx              
         ; 栈的初始esp设置为0会报错,统一设置为0xffffffff
         mov dword [es:esi+0x32],0xffffffff        

         mov ecx,4096
         mov eax,ecx                        
         mov [es:esi+0x36],ecx
         shr [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
         mov ebx,esi                        
         call fill_descriptor_in_ldt
         ; 2特权级栈选择子的RPL是2
         or cx,0000_0000_0000_0010          
         mov [es:esi+0x3e],cx     
         ; 栈的初始esp设置为0会报错,统一设置为0xffffffff          
         mov dword [es:esi+0x40],0xffffffff        
       
         mov eax,[es:esi+0x0c]             
         movzx ebx,word [es:esi+0x0a]   
         ; ldt这块区域作为段性质是系统段    
         mov ecx,0x00408200                 
         call sys_routine_seg_sel:make_seg_descriptor
         ; 每个应用的ldt区域作为段记录在gdt中
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [es:esi+0x10],cx              
       
         mov ecx,104                       
         mov [es:esi+0x12],cx              
         dec word [es:esi+0x12]            
         call sys_routine_seg_sel:allocate_memory
         mov [es:esi+0x14],ecx              
       
         ; 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 
         mov word [es:ecx+0],0             
         mov edx,[es:esi+0x24]             
         mov [es:ecx+4],edx                 
      
         mov dx,[es:esi+0x22]              
         mov [es:ecx+8],dx                 
      
         mov edx,[es:esi+0x32]             
         mov [es:ecx+12],edx               

         mov dx,[es:esi+0x30]              
         mov [es:ecx+16],dx                 

         mov edx,[es:esi+0x40]              
         mov [es:ecx+20],edx               

         mov dx,[es:esi+0x3e]              
         mov [es:ecx+24],dx               

         mov dx,[es:esi+0x10]              
         mov [es:ecx+96],dx                
      
         mov dx,[es:esi+0x12]              
         ; 将tss中的i/o映射基地址部分设置为tss界限
         mov [es:ecx+102],dx               
      
         mov word [es:ecx+100],0  
         ; CR3(PDBR)         
         mov dword [es:ecx+28],0            
         
         mov ebx,[ebp+11*4]               
         ; 程序加载基地址
         mov edi,[es:ebx+0x06]           
         
         ; 应用程序偏移为0x10处取出4字节
         mov edx,[es:edi+0x10]    
         ; 用于设置EIP         
         mov [es:ecx+32],edx             
         
         ; 应用程序偏移为0x14处取出2字节
         mov dx,[es:edi+0x14]   
         ; 用于设置cs           
         mov [es:ecx+76],dx                

         mov dx,[es:edi+0x08]    
         ; 用于设置ss         
         mov [es:ecx+80],dx                

         mov dx,[es:edi+0x04]        
         ; 用于设置ds      
         mov word [es:ecx+84],dx 
         ; 无关寄存器设置为0           
         mov word [es:ecx+72],0             
         mov word [es:ecx+88],0            
         mov word [es:ecx+92],0    

         ; PUSH EFLAGS    
         pushfd
         ; 用edx存储EFLAGS
         pop edx

         ; 存储EFLAGS         
         mov dword [es:ecx+36],edx 
         
         mov eax,[es:esi+0x14]             
         movzx ebx,word [es:esi+0x12]     
         ; tss对象属于系统段
         mov ecx,0x00408900              
         call sys_routine_seg_sel:make_seg_descriptor
         ; tss描述符也将安装到gdt中
         call sys_routine_seg_sel:set_up_gdt_descriptor
         ; 设置tss选择子
         mov [es:esi+0x18],cx             

         pop es                             
         pop ds   
         ; EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次出栈                         
         popad
         ; 一般在调用某个子程序之前,会像堆栈中压入一些参数供子程序使用。
         ; 那么,子程序在返回时,如果要主动废除这些压入的参数的话,就可以通过RET XX来实现。
         ; XX--〉代表压入的参数的字节数目。
         ret 8                              
      
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]               
         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 ecx,mem_0_4_gb_seg_sel        
         mov es,ecx
         mov ebx,message_1
         call sys_routine_seg_sel:put_string

         ; 对系统例程段函数的调用不再通过段选择子:段内偏移方式来转移控制
         ; 修改为通过调用门描述符来进行                               
         mov edi,salt          
         mov ecx,salt_items          
  .b3:
         push ecx   
         mov eax,[edi+256]    
         mov bx,[edi+260]   
         ; 调用门描述符属性位,DPL为3.只执行,依从的代码段。                
         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,prgman_msg1
         ; 通过调用门实现流程跳转
         call far [salt_1+256] 

         mov ebx,message_2
         call far [salt_1+256]             
       
         ; 这个tss代表的是操作系统自己,这里操作系统是一个任务。
         ; tss中有些部分需要创建时设置好以便操作系统读取使用
         ; tss中还有一些部分内容在任务切换时由操作系统维护,写入和读取。
         mov ecx,104                       
         call sys_routine_seg_sel:allocate_memory
         ; [prgman_tss+0x00]存储tss对象基地址
         mov [prgman_tss+0x00],ecx    

         ; LDT_Sector设置为0
         mov word [es:ecx+96],0    
         ; I/OMapAddr设置为103        
         mov word [es:ecx+102],103    
         ; Pre_Task_Tss设置为0  
         mov word [es:ecx+0],0
         ; CR3(PDBR)设置为0            
         mov dword [es:ecx+28],0   
         ; Reserved设置为0       
         mov word [es:ecx+100],0    
         mov eax,ecx                       
         mov ebx,103                        
         mov ecx,0x00408900      
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor


         mov [prgman_tss+0x04],cx   
         ; 显示通知处理器到那里取当前tss的选择子
         ; 通过tss选择子可以得到tss描述符
         ; 通过tss描述符可以得到tss区域,进而取其中内容或设置其中内容                
         ltr cx                              
         
         mov ecx,0x46
         call sys_routine_seg_sel:allocate_memory
         call append_to_tcb_link           
         push dword 50
         push ecx                           
         call load_relocate_program
         ; 任务间切换通过tss基地址,tss选择子进行。
         ; 执行流程从操作系统转移到tss指向的任务
         call far [es:ecx+0x14]   

         mov ebx,prgman_msg2
         call sys_routine_seg_sel:put_string

         mov ecx,0x46
         call sys_routine_seg_sel:allocate_memory
         call append_to_tcb_link           

         mov ebx,benhao_1
         call sys_routine_seg_sel:put_string

         push dword 50                     
         push ecx      
         call load_relocate_program
         ; 前一任务的tss自动存储,前一任务tss基地址,选择子由处理器存储到对应位置【事先告知处理器全局tss基地址,选择子存放位置】
         jmp far [es:ecx+0x14]   
         
         mov ebx,prgman_msg3
         call sys_routine_seg_sel:put_string
         hlt
            
core_code_end:

SECTION core_trail
core_end:

3.用户程序

SECTION header vstart=0
         program_length   dd program_end         
         head_len         dd header_end           
         stack_seg        dd 0                    
         stack_len        dd 1                    
         prgentry         dd start                
         code_seg         dd section.code.start   
         code_len         dd code_end             
         data_seg         dd section.data.start   
         data_len         dd data_end            
         salt_items       dd (header_end-salt)/256 
         salt:                                   
         PrintString      db  '@PrintString'
                     times 256-($-PrintString) db 0
         TerminateProgram db  '@TerminateProgram'
                     times 256-($-TerminateProgram) db 0
         ReadDiskData     db  '@ReadDiskData'
                     times 256-($-ReadDiskData) db 0
header_end:
  
SECTION data vstart=0                
         message_1        db  0x0d,0x0a
                          db  '[USER TASK]: Hi! nice to meet you,'
                          db  'I am run at CPL=',0
         message_2        db  0
                          db  '.Now,I must exit...',0x0d,0x0a,0
data_end:

      [bits 32]
SECTION code vstart=0
start:
         mov eax,ds
         mov fs,eax
         mov eax,[data_seg]
         mov ds,eax
        
         mov ebx,message_1
         call far [fs:PrintString]
         
         mov ax,cs
         and al,0000_0011B
         or al,0x0030
         mov [message_2],al
         
         mov ebx,message_2
         call far [fs:PrintString]
         call far [fs:TerminateProgram]      
code_end:

SECTION trail
program_end:

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/126074330
TSS
今日推荐