Groupe de commutation

Insérer ici l'image Description

Procédé de commutation de tâches

  La première méthode consiste à faire d'interruption de l'utilisation à la commutation de tâches, ce qui est la base de multi-tâches préemptif moderne. En mode réel, 1KB de mémoire est le plus bas de gamme interruption table de vecteurs, contient 256 adresse d'interruption du segment de traitement et l'adresse de décalage. En mode protégé, le processeur d'interruption table de vecteur n'est plus utilisé, mais l'utilisation de la table de descripteurs d'interruption. Et la table de descripteurs TDG, LDT est le même, sauf pour le descripteur, la seule différence est qu'il a sauvé les descripteurs de porte, y compris la porte d'interruption, trappes et la porte de la tâche. porte d'interruption et trappe d'interruption de traitement est autorisé dans la mission, aller à l'espace global pour effectuer une gestion au niveau du système, en substance, est le transfert du comportement de contrôle dans la tâche. Cependant, lorsqu'une interruption se produit, et si le nombre d'interruption correspondant à la porte de la porte d'entrée, il doit être le changement de tâche (des interruptions du d'exécution de la tâche en cours, la tâche de protéger le site actuel, et transférés à une autre tâche à effectuer).

  Comme chemin de la porte de la tâche, un système est un segment de porte tâche, S doit être 0, TYPE 0101 indique que le bit est une porte de tâche. Les bits porte tâche P pour représenter cette porte est efficace, lorsque P = 0 lorsque le changement de tâche est effectuée n'est pas autorisé par cette porte, le niveau de privilège de descripteur de porte tâche de chef de projet, mais parce que l'interruption a initié la commutation de tâches ne fonctionne pas le processeur n'impose aucune protection par niveau de privilège.

  Quand une interruption se produit, le processeur est multiplié par 8 table de descripteurs d'interruption comme la visite d'index, lorsque cette porte est la tâche de la porte, le processeur prendra un nouvel emploi sélecteurs TSS, puis visiter avec TDG sélecteurs TSS, supprimer le descripteur TSS. Ensuite, le processeur accède à la nouvelle TSS, tout récupérer dans TSS enregistré. Ensuite, le point de départ de la nouvelle tâche TR TSS, une fois lancé la nouvelle tâche, le firmware du processeur automatiquement bit descripteur B TSS est réglé sur 1 (indiquant occupé).

  Quand une interruption se produit, il peut effectuer un traitement régulier d'interruption. Les tâches peuvent également être activés. Deux utilisent instruction IRET pour revenir. Le premier est un retour à un différents segments de code la même tâche, qui est de revenir à la tâche interrompue. Processeur distingue par la différence entre les deux EFLAGS bits.

  Essentiellement, parce que sont maintenant tous la porte de descripteur d'interruption, de sorte que tous peuvent être déterminées en fonction de la façon dont le bit le retour de NT. Si une tâche de commutation en raison de l'interruption a lancé un changement de tâche se produit plus tard, les anciennes tâches le site descripteur B TSS ne changera pas, le processeur devra remplir tous les champs de la TSS remplir tous les éléments, puis les anciennes tâches à (0x00) TSS descripteur sélection sous écriture nouveau champ de connexion de la tâche de la tâche, alors que l'emplacement de la nouvelle tâche registre NT EFLAGS devient 1, pour permettre l'exploitation d'une nouvelle tâche de retourner l'ancienne tâche, mais aussi la nouvelle TSS pour la tâche Descripteur B bit est mis à 1 (occupé).

  Sauvegardez les instructions IRET, voir instruction IRET lorsque le processeur immédiatement vérifier le bit NT Si ce bit est 0, selon le fonctionnement général de retour d'interruption;. Si ce bit est 1, il retournera à l'original interrompu la position de l'ancienne tâche de poursuivre la tâche, alors que la récupération bit NT, et la tâche avant de passer bit descripteur B TSS est réglé sur 0. Ensuite, les points de TR à la tâche à l'origine transfert TSS, la commutation de tâches.

processeur de commutation de mode a quatre tâches:

  • Le programme actuel ou le contrôle de l'exécution des tâches est transférée à un descripteur TSS dans le TDG (utilisant l'appel ou JMP commuté).
  • Le programme actuel ou le contrôle de l'exécution des tâches est transférée à la TDG ou le descripteur de porte LDT actuelle d'une tâche (tâches portes LDT peuvent être la tâche, sur la TSS doit TDG) (en utilisant appel ou JMP commuté).
  • Quand une exception ou d'interruption, le numéro d'interruption à la porte de la tâche d'interruption dans la description.
  • Dans le cas où NT est la position des EFLAGS registre, une tâche en cours d'exécution IRET d'instruction.

  Initié par une instruction d'appel comme une tâche interruptions de commutation des tâches similaires initié le transfert, à savoir, initie une instruction d'appel est une commutation de tâches imbriquées, la tâche en cours (Tâche ancienne) le descripteur TSS B pour maintenir le bit d'origine « 1 » n'a pas changer. NT bit registre EFLAGS ne changera pas. TSS nouveaux descripteurs de tâche position B 1, NT est le bit EFLAGS est également défini, il indique que cette tâche est imbriquée dans d'autres tâches, alors que le contenu du domaine tâche de TSS connecté aux anciennes tâches de sélecteurs de descripteurs TSS.

  Different instruction d'appel et, en utilisant l'instruction jmp initiée par le changement de tâche, la tâche ne se forme pas dans une relation d' imbrication. Lors de la commutation de tâche, la tâche actuelle (tâche ancienne) Descripteur TSS est effacée, elle devient l' état non occupé, NT bit EFLAGS registre constant; TSS nouvelle tâche descripteur position de B-1, entre dans un état occupé, EFLAGS NT bits du registre de maintien état pendant le chargement du même de la TSS.
  
  Dans tous les cas, la tâche est pas réentrant, en substance, est le temps d'initier un changement de tâche, la tâche de la nouvelle B-position de descripteur de TSS est que 1, tâche de processeur B bits vérifie TSS descripteur de chaque commutateur.

Lors de l'exécution du changement de tâche, le processeur effectue les opérations suivantes:

  • Utilisation de JMP ou une instruction CALL opérande (note tâche grille de sélection des sous-problèmes, et la porte d'appel dans la mesure effectuée lors de l'appel de grille de tâche ignorer l'adresse de décalage de 32 bits, l'utilisation directe des sous-parties sélectionnées 16), grille de tâches TSS tâche ou le domaine des connexions actuel de la tâche d'obtenir une nouvelle tâche sous sélection descripteur TSS.
  • Vérifiez que pour permettre le passage d'une tâche en cours à une nouvelle tâche, l'accès aux données de contrôle de niveau de privilège s'applique à JMP et l'instruction appel, les segments en cours RPL CPL et la nouvelle tâche (ancienne) tâche doit être inférieure ou égale à la cible TSS ou la porte de la tâche en valeur DPL. Les exceptions, les interruptions (int n instruction est initiée par l'addition d'une interruption) et l'instruction de tâche iret provoque porte de commutation de tâche négligeable ou le descripteur DPL TSS, int n afin d'initier un changement de tâche, pour vérifier DPL.
  • Descripteurs pour vérifier si la nouvelle TSS pour la tâche est marquée comme étant valide (P = 1), et la limite est supérieure à 103, alors que les tâches d'inspection de site B, en plus de l'instruction iret initié transfert, les autres bits interrupteur B pour être égale à 0 (la tâche d'empêcher rentrante ), l'instruction de transfert intercellulaire iret initié B = 1.
  • Revoir l'actuel (les anciens tâches) et TSS de la nouvelle tâche, et tout d'un commutateur de tâche d'utiliser le descripteur a été organisé en mémoire du système.
  • Si le commutateur de travail a été initié par le JMP ou IRET, le processeur efface le B-bit courant (ancien) tâche, si elle est un appel, une exception ou d'interruption initiée, le bit B est de maintenir l'état d'origine.
  • Si le changement de tâche IRET est lancée, le processeur et la mise en place des EFLAGS enregistrer une copie du bit NT est effacé, si les trois autres façons de lancer le commutateur, la copie bit NT reste inchangé.
  • Enregistrer l'état actuel de sa tâche dans TSS (TSS toutes les copies nécessaires).
  • Si l'appel est un changement de tâche, exception ou interruption lancée, le processeur chargeant la nouvelle tâche EFLAGS de position NT registre, JMP ou si initié par le IRET, le NT reste inchangé.
  • Si la tâche est un appel, JMP, exception ou interruption lancée, le processeur sera une nouvelle tâche TSS descripteur B-1 position; si elle est amorcée par le IRET, B reste inchangé.
  • En sélectionnant sous-tâche et le nouveau descripteur TSS TSS est chargé dans le registre des tâches TR (note adresse de base de descripteur, limite, et le segment des attributs du TR est chargé dans le cache).
  • TSS données d'état est chargé dans le nouveau processeur de tâches (TSS dont certains ont chargé). Tant qu'il y aura un sera détruit (non révoqué) défaut a eu lieu pendant le chargement, l'état architectural. Le soi-disant état architectural est transféré d'un état à l'autre est une certaine, la situation imprévisible ne se posera pas.
  • sélecteur de segment descripteur correspondant sera chargé avec de nouvelles tâches de chargement de l'environnement et de vérification des erreurs relatives endommageront l'état architectural, si les étapes précédentes 1-10 erreur irrécupérable est survenue, le processeur ne sera pas en mesure de terminer la commutation de tâches, et d'assurer le processeur retourne à l'état avant l'exécution de l'instruction qui a lancé le changement de tâche, si une erreur irrécupérable à l'étape 11, sera détruit l'état architectural. Si après 12 se sont produits dans l'étape (point d'engagement) erreur irrécupérable, le processeur compléter le commutateur de tâche et génère une exception correspondant avant de commencer une nouvelle tâche.
  • La mise en œuvre de la nouvelle mission. Lors de la commutation de tâches, le niveau de privilège de la nouvelle tâche n'est pas hérité de la tâche suspendue. Les nouvelles tâches de niveau de privilège sont ses registres segment bas CS de deux décisions, alors que le contenu du registre est tiré de la nouvelle tâche de TSS. Parce que chaque tâche a son propre espace d'adressage séparé et l'état des tâches secteur TSS, donc entre les tâches sont isolées les unes des autres. Il suffit d'utiliser les règles de niveau de privilège pour contrôler l'accès aux TSS sur la ligne, le logiciel ne nécessite pas de contrôles explicites de niveau de privilège au moment de la commutation des tâches.

liste

         ;代码清单15-2
         ;文件名:c15.asm
         ;文件说明:用户程序  
;===============================================================================
SECTION header vstart=0

         program_length   dd program_end          ;程序总长度#0x00
         
         head_len         dd header_end           ;程序头部的长度#0x04

         stack_seg        dd 0                    ;用于接收堆栈段选择子#0x08
         stack_len        dd 1                    ;程序建议的堆栈大小#0x0c
                                                  ;以4KB为单位
                                                  
         prgentry         dd start                ;程序入口#0x10 
         code_seg         dd section.code.start   ;代码段位置#0x14
         code_len         dd code_end             ;代码段长度#0x18

         data_seg         dd section.data.start   ;数据段位置#0x1c
         data_len         dd data_end             ;数据段长度#0x20
;-------------------------------------------------------------------------------
         ;符号地址检索表
         salt_items       dd (header_end-salt)/256 ;#0x24
         
         salt:                                     ;#0x28
         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:
         ;任务启动时,DS指向头部段,也不需要设置堆栈 
         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:
         ;代码清单15-1
         ;文件名:c15_core.asm
         ;文件说明:保护模式微型核心程序 
         ;以下常量定义部分。内核的大部分内容都应当固定 
         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    ;整个0-4GB内存的段的选择子

;-------------------------------------------------------------------------------
         ;以下是系统核心的头部,用于加载核心程序 
         core_length      dd core_end       ;核心程序总长度#00

         sys_routine_seg  dd section.sys_routine.start
                                            ;系统公用例程段位置#04

         core_data_seg    dd section.core_data.start
                                            ;核心数据段位置#08

         core_code_seg    dd section.core_code.start
                                            ;核心代码段位置#0c


         core_entry       dd start          ;核心代码段入口点#10
                          dw core_code_seg_sel

;===============================================================================
         [bits 32]
;===============================================================================
SECTION sys_routine vstart=0                ;系统公共例程代码段 
;-------------------------------------------------------------------------------
         ;字符串显示例程
put_string:                                 ;显示0终止的字符串并移动光标 
                                            ;输入:DS:EBX=串地址
         push ecx
  .getc:
         mov cl,[ebx]
         or cl,cl
         jz .exit
         call put_char
         inc ebx
         jmp .getc

  .exit:
         pop ecx
         retf                               ;段间返回

;-------------------------------------------------------------------------------
put_char:                                   ;在当前光标处显示一个字符,并推进
                                            ;光标。仅用于段内调用 
                                            ;输入:CL=字符ASCII码 
         pushad

         ;以下取当前光标位置
         mov dx,0x3d4
         mov al,0x0e
         out dx,al
         inc dx                             ;0x3d5
         in al,dx                           ;高字
         mov ah,al

         dec dx                             ;0x3d4
         mov al,0x0f
         out dx,al
         inc dx                             ;0x3d5
         in al,dx                           ;低字
         mov bx,ax                          ;BX=代表光标位置的16位数

         cmp cl,0x0d                        ;回车符?
         jnz .put_0a
         mov ax,bx
         mov bl,80
         div bl
         mul bl
         mov bx,ax
         jmp .set_cursor

  .put_0a:
         cmp cl,0x0a                        ;换行符?
         jnz .put_other
         add bx,80
         jmp .roll_screen

  .put_other:                               ;正常显示字符
         push es
         mov eax,video_ram_seg_sel          ;0xb8000段的选择子
         mov es,eax
         shl bx,1
         mov [es:bx],cl
         pop es

         ;以下将光标位置推进一个字符
         shr bx,1
         inc bx

  .roll_screen:
         cmp bx,2000                        ;光标超出屏幕?滚屏
         jl .set_cursor

         push ds
         push es
         mov eax,video_ram_seg_sel
         mov ds,eax
         mov es,eax
         cld
         mov esi,0xa0                       ;小心!32位模式下movsb/w/d 
         mov edi,0x00                       ;使用的是esi/edi/ecx 
         mov ecx,1920
         rep movsd
         mov bx,3840                        ;清除屏幕最底一行
         mov ecx,80                         ;32位程序应该使用ECX
  .cls:
         mov word[es:bx],0x0720
         add bx,2
         loop .cls

         pop es
         pop ds

         mov bx,1920

  .set_cursor:
         mov dx,0x3d4
         mov al,0x0e
         out dx,al
         inc dx                             ;0x3d5
         mov al,bh
         out dx,al
         dec dx                             ;0x3d4
         mov al,0x0f
         out dx,al
         inc dx                             ;0x3d5
         mov al,bl
         out dx,al

         popad
         
         ret                                

;-------------------------------------------------------------------------------
read_hard_disk_0:                           ;从硬盘读取一个逻辑扇区
                                            ;EAX=逻辑扇区号
                                            ;DS:EBX=目标缓冲区地址
                                            ;返回:EBX=EBX+512
         push eax 
         push ecx
         push edx
      
         push eax
         
         mov dx,0x1f2
         mov al,1
         out dx,al                          ;读取的扇区数

         inc dx                             ;0x1f3
         pop eax
         out dx,al                          ;LBA地址7~0

         inc dx                             ;0x1f4
         mov cl,8
         shr eax,cl
         out dx,al                          ;LBA地址15~8

         inc dx                             ;0x1f5
         shr eax,cl
         out dx,al                          ;LBA地址23~16

         inc dx                             ;0x1f6
         shr eax,cl
         or al,0xe0                         ;第一硬盘  LBA地址27~24
         out dx,al

         inc dx                             ;0x1f7
         mov al,0x20                        ;读命令
         out dx,al

  .waits:
         in al,dx
         and al,0x88
         cmp al,0x08
         jnz .waits                         ;不忙,且硬盘已准备好数据传输 

         mov ecx,256                        ;总共要读取的字数
         mov dx,0x1f0
  .readw:
         in ax,dx
         mov [ebx],ax
         add ebx,2
         loop .readw

         pop edx
         pop ecx
         pop eax
      
         retf                               ;段间返回 

;-------------------------------------------------------------------------------
;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助 
put_hex_dword:                              ;在当前光标处以十六进制形式显示
                                            ;一个双字并推进光标 
                                            ;输入:EDX=要转换并显示的数字
                                            ;输出:无
         pushad
         push ds
      
         mov ax,core_data_seg_sel           ;切换到核心数据段 
         mov ds,ax
      
         mov ebx,bin_hex                    ;指向核心数据段内的转换表
         mov ecx,8
  .xlt:    
         rol edx,4
         mov eax,edx
         and eax,0x0000000f
         xlat
      
         push ecx
         mov cl,al                           
         call put_char
         pop ecx
       
         loop .xlt
      
         pop ds
         popad
         retf
      
;-------------------------------------------------------------------------------
allocate_memory:                            ;分配内存
                                            ;输入:ECX=希望分配的字节数
                                            ;输出:ECX=起始线性地址 
         push ds
         push eax
         push ebx
      
         mov eax,core_data_seg_sel
         mov ds,eax
      
         mov eax,[ram_alloc]
         add eax,ecx                        ;下一次分配时的起始地址
      
         ;这里应当有检测可用内存数量的指令
          
         mov ecx,[ram_alloc]                ;返回分配的起始地址

         mov ebx,eax
         and ebx,0xfffffffc
         add ebx,4                          ;强制对齐 
         test eax,0x00000003                ;下次分配的起始地址最好是4字节对齐
         cmovnz eax,ebx                     ;如果没有对齐,则强制对齐 
         mov [ram_alloc],eax                ;下次从该地址分配内存
                                            ;cmovcc指令可以避免控制转移 
         pop ebx
         pop eax
         pop ds

         retf

;-------------------------------------------------------------------------------
set_up_gdt_descriptor:                      ;在GDT内安装一个新的描述符
                                            ;输入:EDX:EAX=描述符 
                                            ;输出:CX=描述符的选择子
         push eax
         push ebx
         push edx

         push ds
         push es

         mov ebx,core_data_seg_sel          ;切换到核心数据段
         mov ds,ebx

         sgdt [pgdt]                        ;以便开始处理GDT

         mov ebx,mem_0_4_gb_seg_sel
         mov es,ebx

         movzx ebx,word [pgdt]              ;GDT界限
         inc bx                             ;GDT总字节数,也是下一个描述符偏移
         add ebx,[pgdt+2]                   ;下一个描述符的线性地址

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

         add word [pgdt],8                  ;增加一个描述符的大小

         lgdt [pgdt]                        ;对GDT的更改生效

         mov ax,[pgdt]                      ;得到GDT界限值
         xor dx,dx
         mov bx,8
         div bx                             ;除以8,去掉余数
         mov cx,ax
         shl cx,3                           ;将索引号移到正确位置

         pop es
         pop ds

         pop edx
         pop ebx
         pop eax

         retf
;-------------------------------------------------------------------------------
make_seg_descriptor:                        ;构造存储器和系统的段描述符
                                            ;输入:EAX=线性基地址
                                            ;      EBX=段界限
                                            ;      ECX=属性。各属性位都在原始
                                            ;          位置,无关的位清零 
                                            ;返回:EDX:EAX=描述符
         mov edx,eax
         shl eax,16
         or ax,bx                           ;描述符前32位(EAX)构造完毕

         and edx,0xffff0000                 ;清除基地址中无关的位
         rol edx,8
         bswap edx                          ;装配基址的31~24和23~16  (80486+)

         xor bx,bx
         or edx,ebx                         ;装配段界限的高4位

         or edx,ecx                         ;装配属性

         retf

;-------------------------------------------------------------------------------
make_gate_descriptor:                       ;构造门的描述符(调用门等)
                                            ;输入:EAX=门代码在段内偏移地址
                                            ;       BX=门代码所在段的选择子 
                                            ;       CX=段类型及属性等(各属
                                            ;          性位都在原始位置)
                                            ;返回:EDX:EAX=完整的描述符
         push ebx
         push ecx
      
         mov edx,eax
         and edx,0xffff0000                 ;得到偏移地址高16位 
         or dx,cx                           ;组装属性部分到EDX
       
         and eax,0x0000ffff                 ;得到偏移地址低16位 
         shl ebx,16                          
         or eax,ebx                         ;组装段选择子部分
      
         pop ecx
         pop ebx
      
         retf                                   
                             
;-------------------------------------------------------------------------------
terminate_current_task:                     ;终止当前任务
                                            ;注意,执行此例程时,当前任务仍在
                                            ;运行中。此例程其实也是当前任务的
                                            ;一部分 
         pushfd
         mov edx,[esp]                      ;获得EFLAGS寄存器内容
         add esp,4                          ;恢复堆栈指针

         mov eax,core_data_seg_sel
         mov ds,eax

         test dx,0100_0000_0000_0000B       ;测试NT位
         jnz .b1                            ;当前任务是嵌套的,到.b1执行iretd 
         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             ;用于设置和修改GDT 
                          dd  0

         ram_alloc        dd  0x00100000    ;下次分配内存时的起始地址

         ;符号地址检索表
         salt:
         salt_1           db  '@PrintString'
                     times 256-($-salt_1) db 0
                          dd  put_string
                          dw  sys_routine_seg_sel

         salt_2           db  '@ReadDiskData'
                     times 256-($-salt_2) db 0
                          dd  read_hard_disk_0
                          dw  sys_routine_seg_sel

         salt_3           db  '@PrintDwordAsHexString'
                     times 256-($-salt_3) db 0
                          dd  put_hex_dword
                          dw  sys_routine_seg_sel

         salt_4           db  '@TerminateProgram'
                     times 256-($-salt_4) db 0
                          dd  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'
                                            ;put_hex_dword子过程用的查找表 

         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             ;程序管理器的TSS基地址
                          dw  0             ;程序管理器的TSS描述符选择子 

         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

core_data_end:
               
;===============================================================================
SECTION core_code vstart=0
;-------------------------------------------------------------------------------
fill_descriptor_in_ldt:                     ;在LDT内安装一个新的描述符
                                            ;输入:EDX:EAX=描述符
                                            ;          EBX=TCB基地址
                                            ;输出:CX=描述符的选择子
         push eax
         push edx
         push edi
         push ds

         mov ecx,mem_0_4_gb_seg_sel
         mov ds,ecx

         mov edi,[ebx+0x0c]                 ;获得LDT基地址
         
         xor ecx,ecx
         mov cx,[ebx+0x0a]                  ;获得LDT界限
         inc cx                             ;LDT的总字节数,即新描述符偏移地址
         
         mov [edi+ecx+0x00],eax
         mov [edi+ecx+0x04],edx             ;安装描述符

         add cx,8                           
         dec cx                             ;得到新的LDT界限值 

         mov [ebx+0x0a],cx                  ;更新LDT界限值到TCB

         mov ax,cx
         xor dx,dx
         mov cx,8
         div cx
         
         mov cx,ax
         shl cx,3                           ;左移3位,并且
         or cx,0000_0000_0000_0100B         ;使TI位=1,指向LDT,最后使RPL=00 

         pop ds
         pop edi
         pop edx
         pop eax
     
         ret
         
;------------------------------------------------------------------------------- 
load_relocate_program:                      ;加载并重定位用户程序
                                            ;输入: PUSH 逻辑扇区号
                                            ;      PUSH 任务控制块基地址
                                            ;输出:无 
         pushad
      
         push ds
         push es
      
         mov ebp,esp                        ;为访问通过堆栈传递的参数做准备
      
         mov ecx,mem_0_4_gb_seg_sel
         mov es,ecx
      
         mov esi,[ebp+11*4]                 ;从堆栈中取得TCB的基地址

         ;以下申请创建LDT所需要的内存
         mov ecx,160                        ;允许安装20个LDT描述符
         call sys_routine_seg_sel:allocate_memory
         mov [es:esi+0x0c],ecx              ;登记LDT基地址到TCB中
         mov word [es:esi+0x0a],0xffff      ;登记LDT初始的界限到TCB中 

         ;以下开始加载用户程序 
         mov eax,core_data_seg_sel
         mov ds,eax                         ;切换DS到内核数据段
       
         mov eax,[ebp+12*4]                 ;从堆栈中取出用户程序起始扇区号 
         mov ebx,core_buf                   ;读取程序头部数据     
         call sys_routine_seg_sel:read_hard_disk_0

         ;以下判断整个程序有多大
         mov eax,[core_buf]                 ;程序尺寸
         mov ebx,eax
         and ebx,0xfffffe00                 ;使之512字节对齐(能被512整除的数低 
         add ebx,512                        ;9位都为0 
         test eax,0x000001ff                ;程序的大小正好是512的倍数吗? 
         cmovnz eax,ebx                     ;不是。使用凑整的结果
      
         mov ecx,eax                        ;实际需要申请的内存数量
         call sys_routine_seg_sel:allocate_memory
         mov [es:esi+0x06],ecx              ;登记程序加载基地址到TCB中
      
         mov ebx,ecx                        ;ebx -> 申请到的内存首地址
         xor edx,edx
         mov ecx,512
         div ecx
         mov ecx,eax                        ;总扇区数 
      
         mov eax,mem_0_4_gb_seg_sel         ;切换DS到0-4GB的段
         mov ds,eax

         mov eax,[ebp+12*4]                 ;起始扇区号 
  .b1:
         call sys_routine_seg_sel:read_hard_disk_0
         inc eax
         loop .b1                           ;循环读,直到读完整个用户程序

         mov edi,[es:esi+0x06]              ;获得程序加载基地址

         ;建立程序头部段描述符
         mov eax,edi                        ;程序头部起始线性地址
         mov ebx,[edi+0x04]                 ;段长度
         dec ebx                            ;段界限
         mov ecx,0x0040f200                 ;字节粒度的数据段描述符,特权级3 
         call sys_routine_seg_sel:make_seg_descriptor
      
         ;安装头部段描述符到LDT中 
         mov ebx,esi                        ;TCB的基地址
         call fill_descriptor_in_ldt

         or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
         mov [es:esi+0x44],cx               ;登记程序头部段选择子到TCB 
         mov [edi+0x04],cx                  ;和头部内 
      
         ;建立程序代码段描述符
         mov eax,edi
         add eax,[edi+0x14]                 ;代码起始线性地址
         mov ebx,[edi+0x18]                 ;段长度
         dec ebx                            ;段界限
         mov ecx,0x0040f800                 ;字节粒度的代码段描述符,特权级3
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基地址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
         mov [edi+0x14],cx                  ;登记代码段选择子到头部

         ;建立程序数据段描述符
         mov eax,edi
         add eax,[edi+0x1c]                 ;数据段起始线性地址
         mov ebx,[edi+0x20]                 ;段长度
         dec ebx                            ;段界限 
         mov ecx,0x0040f200                 ;字节粒度的数据段描述符,特权级3
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基地址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
         mov [edi+0x1c],cx                  ;登记数据段选择子到头部

         ;建立程序堆栈段描述符
         mov ecx,[edi+0x0c]                 ;4KB的倍率 
         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                 ;字节粒度的堆栈段描述符,特权级3
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基地址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
         mov [edi+0x08],cx                  ;登记堆栈段选择子到头部

         ;重定位SALT 
         mov eax,mem_0_4_gb_seg_sel         ;这里和前一章不同,头部段描述符
         mov es,eax                         ;已安装,但还没有生效,故只能通
                                            ;过4GB段访问用户程序头部          
         mov eax,core_data_seg_sel
         mov ds,eax
      
         cld

         mov ecx,[es:edi+0x24]              ;U-SALT条目数(通过访问4GB段取得) 
         add edi,0x28                       ;U-SALT在4GB段内的偏移 
  .b2: 
         push ecx
         push edi
      
         mov ecx,salt_items
         mov esi,salt
  .b3:
         push edi
         push esi
         push ecx

         mov ecx,64                         ;检索表中,每条目的比较次数 
         repe cmpsd                         ;每次比较4字节 
         jnz .b4
         mov eax,[esi]                      ;若匹配,则esi恰好指向其后的地址
         mov [es:edi-256],eax               ;将字符串改写成偏移地址 
         mov ax,[esi+4]
         or ax,0000000000000011B            ;以用户程序自己的特权级使用调用门
                                            ;故RPL=3 
         mov [es:edi-252],ax                ;回填调用门选择子 
  .b4:
      
         pop ecx
         pop esi
         add esi,salt_item_len
         pop edi                            ;从头比较 
         loop .b3
      
         pop edi
         add edi,256
         pop ecx
         loop .b2

         mov esi,[ebp+11*4]                 ;从堆栈中取得TCB的基地址

         ;创建0特权级堆栈
         mov ecx,4096
         mov eax,ecx                        ;为生成堆栈高端地址做准备 
         mov [es:esi+0x1a],ecx
         shr dword [es:esi+0x1a],12         ;登记0特权级堆栈尺寸到TCB 
         call sys_routine_seg_sel:allocate_memory
         add eax,ecx                        ;堆栈必须使用高端地址为基地址
         mov [es:esi+0x1e],eax              ;登记0特权级堆栈基地址到TCB 
         mov ebx,0xffffe                    ;段长度(界限)
         mov ecx,0x00c09600                 ;4KB粒度,读写,特权级0
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基地址
         call fill_descriptor_in_ldt
         ;or cx,0000_0000_0000_0000          ;设置选择子的特权级为0
         mov [es:esi+0x22],cx               ;登记0特权级堆栈选择子到TCB
         mov dword [es:esi+0x24],0          ;登记0特权级堆栈初始ESP到TCB
      
         ;创建1特权级堆栈
         mov ecx,4096
         mov eax,ecx                        ;为生成堆栈高端地址做准备
         mov [es:esi+0x28],ecx
         shr [es:esi+0x28],12               ;登记1特权级堆栈尺寸到TCB
         call sys_routine_seg_sel:allocate_memory
         add eax,ecx                        ;堆栈必须使用高端地址为基地址
         mov [es:esi+0x2c],eax              ;登记1特权级堆栈基地址到TCB
         mov ebx,0xffffe                    ;段长度(界限)
         mov ecx,0x00c0b600                 ;4KB粒度,读写,特权级1
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基地址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0001          ;设置选择子的特权级为1
         mov [es:esi+0x30],cx               ;登记1特权级堆栈选择子到TCB
         mov dword [es:esi+0x32],0          ;登记1特权级堆栈初始ESP到TCB

         ;创建2特权级堆栈
         mov ecx,4096
         mov eax,ecx                        ;为生成堆栈高端地址做准备
         mov [es:esi+0x36],ecx
         shr [es:esi+0x36],12               ;登记2特权级堆栈尺寸到TCB
         call sys_routine_seg_sel:allocate_memory
         add eax,ecx                        ;堆栈必须使用高端地址为基地址
         mov [es:esi+0x3a],ecx              ;登记2特权级堆栈基地址到TCB
         mov ebx,0xffffe                    ;段长度(界限)
         mov ecx,0x00c0d600                 ;4KB粒度,读写,特权级2
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基地址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0010          ;设置选择子的特权级为2
         mov [es:esi+0x3e],cx               ;登记2特权级堆栈选择子到TCB
         mov dword [es:esi+0x40],0          ;登记2特权级堆栈初始ESP到TCB
      
         ;在GDT中登记LDT描述符
         mov eax,[es:esi+0x0c]              ;LDT的起始线性地址
         movzx ebx,word [es:esi+0x0a]       ;LDT段界限
         mov ecx,0x00408200                 ;LDT描述符,特权级0
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [es:esi+0x10],cx               ;登记LDT选择子到TCB中
       
         ;创建用户程序的TSS
         mov ecx,104                        ;tss的基本尺寸
         mov [es:esi+0x12],cx              
         dec word [es:esi+0x12]             ;登记TSS界限值到TCB 
         call sys_routine_seg_sel:allocate_memory
         mov [es:esi+0x14],ecx              ;登记TSS基地址到TCB
      
         ;登记基本的TSS表格内容
         mov word [es:ecx+0],0              ;反向链=0
      
         mov edx,[es:esi+0x24]              ;登记0特权级堆栈初始ESP
         mov [es:ecx+4],edx                 ;到TSS中
      
         mov dx,[es:esi+0x22]               ;登记0特权级堆栈段选择子
         mov [es:ecx+8],dx                  ;到TSS中
      
         mov edx,[es:esi+0x32]              ;登记1特权级堆栈初始ESP
         mov [es:ecx+12],edx                ;到TSS中

         mov dx,[es:esi+0x30]               ;登记1特权级堆栈段选择子
         mov [es:ecx+16],dx                 ;到TSS中

         mov edx,[es:esi+0x40]              ;登记2特权级堆栈初始ESP
         mov [es:ecx+20],edx                ;到TSS中

         mov dx,[es:esi+0x3e]               ;登记2特权级堆栈段选择子
         mov [es:ecx+24],dx                 ;到TSS中

         mov dx,[es:esi+0x10]               ;登记任务的LDT选择子
         mov [es:ecx+96],dx                 ;到TSS中
      
         mov dx,[es:esi+0x12]               ;登记任务的I/O位图偏移
         mov [es:ecx+102],dx                ;到TSS中 
      
         mov word [es:ecx+100],0            ;T=0
      
         mov dword [es:ecx+28],0            ;登记CR3(PDBR)
      
         ;访问用户程序头部,获取数据填充TSS 
         mov ebx,[ebp+11*4]                 ;从堆栈中取得TCB的基地址
         mov edi,[es:ebx+0x06]              ;用户程序加载的基地址 

         mov edx,[es:edi+0x10]              ;登记程序入口点(EIP) 
         mov [es:ecx+32],edx                ;到TSS

         mov dx,[es:edi+0x14]               ;登记程序代码段(CS)选择子
         mov [es:ecx+76],dx                 ;到TSS中

         mov dx,[es:edi+0x08]               ;登记程序堆栈段(SS)选择子
         mov [es:ecx+80],dx                 ;到TSS中

         mov dx,[es:edi+0x04]               ;登记程序数据段(DS)选择子
         mov word [es:ecx+84],dx            ;到TSS中。注意,它指向程序头部段
      
         mov word [es:ecx+72],0             ;TSS中的ES=0

         mov word [es:ecx+88],0             ;TSS中的FS=0

         mov word [es:ecx+92],0             ;TSS中的GS=0

         pushfd
         pop edx
         
         mov dword [es:ecx+36],edx          ;EFLAGS

         ;在GDT中登记TSS描述符
         mov eax,[es:esi+0x14]              ;TSS的起始线性地址
         movzx ebx,word [es:esi+0x12]       ;段长度(界限)
         mov ecx,0x00408900                 ;TSS描述符,特权级0
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [es:esi+0x18],cx               ;登记TSS选择子到TCB

         pop es                             ;恢复到调用此过程前的es段 
         pop ds                             ;恢复到调用此过程前的ds段
      
         popad
      
         ret 8                              ;丢弃调用本过程前压入的参数 
      
;-------------------------------------------------------------------------------
append_to_tcb_link:                         ;在TCB链上追加任务控制块
                                            ;输入:ECX=TCB线性基地址
         push eax
         push edx
         push ds
         push es
         
         mov eax,core_data_seg_sel          ;令DS指向内核数据段 
         mov ds,eax
         mov eax,mem_0_4_gb_seg_sel         ;令ES指向0..4GB段
         mov es,eax
         
         mov dword [es: ecx+0x00],0         ;当前TCB指针域清零,以指示这是最
                                            ;后一个TCB
                                             
         mov eax,[tcb_chain]                ;TCB表头指针
         or eax,eax                         ;链表为空?
         jz .notcb 
         
  .searc:
         mov edx,eax
         mov eax,[es: edx+0x00]
         or eax,eax               
         jnz .searc
         
         mov [es: edx+0x00],ecx
         jmp .retpc
         
  .notcb:       
         mov [tcb_chain],ecx                ;若为空表,直接令表头指针指向TCB
         
  .retpc:
         pop es
         pop ds
         pop edx
         pop eax
         
         ret
         
;-------------------------------------------------------------------------------
start:
         mov ecx,core_data_seg_sel          ;令DS指向核心数据段 
         mov ds,ecx

         mov ecx,mem_0_4_gb_seg_sel         ;令ES指向4GB数据段 
         mov es,ecx

         mov ebx,message_1                    
         call sys_routine_seg_sel:put_string
                                         
         ;显示处理器品牌信息 
         mov eax,0x80000002
         cpuid
         mov [cpu_brand + 0x00],eax
         mov [cpu_brand + 0x04],ebx
         mov [cpu_brand + 0x08],ecx
         mov [cpu_brand + 0x0c],edx
      
         mov eax,0x80000003
         cpuid
         mov [cpu_brand + 0x10],eax
         mov [cpu_brand + 0x14],ebx
         mov [cpu_brand + 0x18],ecx
         mov [cpu_brand + 0x1c],edx

         mov eax,0x80000004
         cpuid
         mov [cpu_brand + 0x20],eax
         mov [cpu_brand + 0x24],ebx
         mov [cpu_brand + 0x28],ecx
         mov [cpu_brand + 0x2c],edx

         mov ebx,cpu_brnd0                  ;显示处理器品牌信息 
         call sys_routine_seg_sel:put_string
         mov ebx,cpu_brand
         call sys_routine_seg_sel:put_string
         mov ebx,cpu_brnd1
         call sys_routine_seg_sel:put_string

         ;以下开始安装为整个系统服务的调用门。特权级之间的控制转移必须使用门
         mov edi,salt                       ;C-SALT表的起始位置 
         mov ecx,salt_items                 ;C-SALT表的条目数量 
  .b3:
         push ecx   
         mov eax,[edi+256]                  ;该条目入口点的32位偏移地址 
         mov bx,[edi+260]                   ;该条目入口点的段选择子 
         mov cx,1_11_0_1100_000_00000B      ;特权级3的调用门(3以上的特权级才
                                            ;允许访问),0个参数(因为用寄存器
                                            ;传递参数,而没有用栈) 
         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              ;指向下一个C-SALT条目 
         pop ecx
         loop .b3

         ;对门进行测试 
         mov ebx,message_2
         call far [salt_1+256]              ;通过门显示信息(偏移量将被忽略) 
      
         ;为程序管理器的TSS分配内存空间 
         mov ecx,104                        ;为该任务的TSS分配内存
         call sys_routine_seg_sel:allocate_memory
         mov [prgman_tss+0x00],ecx          ;保存程序管理器的TSS基地址 
      
         ;在程序管理器的TSS中设置必要的项目 
         mov word [es:ecx+96],0             ;没有LDT。处理器允许没有LDT的任务。
         mov word [es:ecx+102],103          ;没有I/O位图。0特权级事实上不需要。
         mov word [es:ecx+0],0              ;反向链=0
         mov dword [es:ecx+28],0            ;登记CR3(PDBR)
         mov word [es:ecx+100],0            ;T=0
                                            ;不需要0、1、2特权级堆栈。0特级不
                                            ;会向低特权级转移控制。
         
         ;创建TSS描述符,并安装到GDT中 
         mov eax,ecx                        ;TSS的起始线性地址
         mov ebx,103                        ;段长度(界限)
         mov ecx,0x00408900                 ;TSS描述符,特权级0
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [prgman_tss+0x04],cx           ;保存程序管理器的TSS描述符选择子 

         ;任务寄存器TR中的内容是任务存在的标志,该内容也决定了当前任务是谁。
         ;下面的指令为当前正在执行的0特权级任务“程序管理器”后补手续(TSS)。
         ltr cx                              

         ;现在可认为“程序管理器”任务正执行中
         mov ebx,prgman_msg1
         call sys_routine_seg_sel:put_string

         mov ecx,0x46
         call sys_routine_seg_sel:allocate_memory
         call append_to_tcb_link            ;将此TCB添加到TCB链中 
      
         push dword 50                      ;用户程序位于逻辑50扇区
         push ecx                           ;压入任务控制块起始线性地址 
       
         call load_relocate_program         
      
         call far [es:ecx+0x14]             ;执行任务切换。和上一章不同,任务切
                                            ;换时要恢复TSS内容,所以在创建任务
                                            ;时TSS要填写完整 
                                          
         ;重新加载并切换任务 
         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            ;将此TCB添加到TCB链中

         push dword 50                      ;用户程序位于逻辑50扇区
         push ecx                           ;压入任务控制块起始线性地址

         call load_relocate_program

         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:

Je suppose que tu aimes

Origine www.cnblogs.com/chengmf/p/12600400.html
conseillé
Classement