汇编实例解析--分页机制,多任务,基于中断的任务切换

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:

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/126395776