第14章特权级(控制转换)和任务

任务和特权级保护

任务的隔离和特权级保护

程序:记录在载体上的指令和数据,正在执行的一个副本叫做任务(task)

  • 为了有效在任务间实施隔离,每一个任务具有自己的描述符表,称为局部描述符表LDT(local):存放自己的段

那么:

  1. 类似GDTR,处理器中LDTR也是用来追踪LDT的
  2. LDT的数量依据任务的多少/
  • 段选择子中的TI(table indicator) 0=GDT 1=LDT

任务状态段 TSS

多任务环境下,任务切换保护现场:通用寄存器,段寄存器,ESP,EIP,EFLAGS 等,每个任务都用额外的内存区域保护相关的信息,叫做 任务状态段(Task State segment: TSS),

处理器用TR寄存器指向当前任务的TSS,和GDTR,LDTR寄存器一样只有一个,但任务有多个,随着任务改变

流程:处理器将当前任务现场信息保存到TR指向的TSS,再使TR指向新任务的TSS,并从新任务的TSS中恢复现场

每个任务包含2个部分:全局+私有,全局是所有任务共有的(包含OS的软件和例程)

特权级保护概述

处理器正在一个代码段中取指令和执行指令时,当前代码段的特权级叫做当前特权级(Current Privilege Level:CPL)

只有在特权级CPL=0才能执行的指令称为特权指令,

处理器的EFLAGS中,位13,12是IOPL位(输入输出特权级),代表当前任务的IO特权级别,TSS中有EFLAGS的副本

处理器不限制0特权级别的程序的IO访问,但是可以限制特权级别低的程序IO访问权限,


代码段的特权级别检查

控制转移只允许发生在2个特权级别相同的代码段之间,例如CPL=2时,只能转移到DPL=2的代码段接着执行,不能转移到DPL=0,1,3的代码段执行,

特权级别低的应用程序如何调用特权级别高的操作系统例程?

  1. 依从
  • 代码段描述符的Type子段中的C位,C=0:代码段只能供同特权级别的程序使用,C=1:代码段可以从特权级比自己低的程序调用并且进入

调用条件:

  • CPL >= 目标代码段的DPL:意思是当前执行的程序的特权级别只能比它低或者等于,不能将控制从高特权级别转移到低特权级别

并且:依从的代码段不是在自己的DPL特权级上运行,而是在原来的调用程序的特权级别上运行,意思是当前代码段调用了比自己特权级别高的代码段,并不改变当前特权级别CPL,(被调用过程的特权级别依从)

  • 任何时候都不允许将控制从较高的特权级别转移到较低的特权级.下面的调用门是特例
  1. 调用门

门:另一种形式的描述符,称为门描述符,简称门,门描述符描述可执行的代码,

根据不同的用途分为:中断门/陷阱门:中断过程使用,任务门:执行任务切换.

通过调用门进行控制转移:

jmp far:转移到比当前特权级别高的代码段,不改变当前特权级别

call far:当前特权级别提升到目标代码段的特权级别

  • 除了从高特权级别例程()返回外,不允许从特权级高的代码段转移到特权级低的代码段

特权级保护机制只在保护模式下启用,进入保护模式后,处理器自动将当前特权级CPL设定为0,

不管是实施控制转移访问数据段,都是一个请求,RPL:指请求者的特权级别

段选择子的RPL指请求者的特权级别(多数情况下请求者=自己,所以RPL=CPL),RPL由编程人员决定然后给处理器看


特权检查总结

  1. 直接转移到非依从代码段,要求

    CPL = DPL

    RPL = DPL

  2. 直接转移到依从的代码段

    CPL >= DPL

    RPL >= DPL

  • (门控制转移略)
  1. 高特权级别程序可以访问低特权级别的数据段,反之不行,访问数据段要修改(DS ES FS GS)寄存器

    CPL <= DPL

    RPL <= DPL

  2. 任何时候栈段特权级别必须等于CPL,对SS进行修改,必须

    CPL = DPL

    RPL = DPL


接13章的core程序,将用户程序的特权级定为3

调用门(Call Gate)格式(可安装到GDT或者LDT中):

高32bit:
31 16 15 14 - 13 12 11 - 8 7 6 5 4 - 0
段内偏移量31-16 P DPL 0 TYPE 0 0 0 参数个数
低32bit:
31 16 15 0
例程所在代码段的段选择子 段内偏移量15-0

TYPE:标识门的类型,1100=调用门

P位:有效位,1=有效,0=调用门时产生中断(故障中断,中断返回后处理器会重新执行)


通过调用门实施特权级之间的控制转移时,注意jmp farcall far指令,CPL是否改变,

因为栈的特权级别必须和当前特权级别一致,控制转移时栈也要切换,供对应的特权级使用,

每个任务除了固定的栈外,需要定义几套栈,特权级1需要定义DPL=0的栈,特权级2可能需要定义0,1级别的栈,同理特权级3可能需要定义0,1,2级别的栈.

这些任务的不同级别的栈都需要登记在当前任务的TSS中(意思是的使用的当前TSS的r0,1,2栈),在控制转移后使用,并且栈切换由处理器固件自动完成的,在通过调用门使用高特权级别例程时,传递的参数可能用当前的栈(旧栈),处理器会根据调用门描述符中的参数个数来复制到新的栈中,切换后处理器自动替换SS和ESP的内容


第14章特权级(控制转换)和任务_第1张图片

调用门规则:

  1. 当前CPL和RPL权限大于等于调用门描述符DPL,(我现在权限比你调用门描述符权限大才行)

    数值上:

    CPL <= 调用门描述符的DPL

    RPL <= 调用门描述符的DPL

  2. 当前特权级CPL低于或者等于目标代码段描述符的DPL,

    数值上:

    CPL >= DPL

  • 总结: 调用门相当于下限,目标代码段描述符的DPL相当于上限

  • 调用门是给那个等级的例程当做桥梁的,那么就放在哪个描述符表中(dt),给OS例程就放在GDT中

第14章特权级(控制转换)和任务_第2张图片

LDT的格式和GDT一样,而且某些位是固定的

  1. D/B位置,操作数大小 = 0,
  2. L位
  3. S固定=0,表示系统的段描述符or门描述符, 因为LDT属于系统的段描述符.
  4. TYPE固定=0010

第14章特权级(控制转换)和任务_第3张图片

TSS描述符和LDT差不多:

  • TYPE中的B(BUSY)位,B=0,表示刚刚创建不忙,B=1表示任务在执行或者处于挂起状态

第14章特权级(控制转换)和任务_第4张图片

arpl指令(adjust RPL field of segment selector):

因为程序编写者可以将RPL写成高特权级,在通过调用门控制转移到高特权级别后,如果有某些数段选择子作为参数传过来使用,CPL<=选择子对应描述符DPL,又因为RPL被认为改成高特权级,则RPL<=DPL,那么可以使坏传入OS的数据段来修改

arpl a,b:

  1. RPL(a) < RPL(b):则a的RPL增加到b的RPL,并且设置ZF
  2. 否则ZF清零

  • 加入了TSS TCB LDT,将用户程序放在r3执行,通过调用门进入历程(mbr,和用户程序和上一章一样)
    core_code_seg_sel   equ 0x38
    core_data_seg_sel   equ 0x30
    sys_routine_seg_sel equ 0x28
    video_ram_seg_sel   equ 0x20 
    core_stack_seg_sel  equ 0x18
    mem_0_4_gb_seg_sel  equ 0x08

    core_length         dd core_end 

    sys_routine_seg     dd section.sys_routine.start 

    core_data_seg       dd section.core_data.start 

    core_code_seg       dd section.core_code.start 

    core_entry          dd start 
                        dw core_code_seg_sel

    [bits 32]
SECTION sys_routine vstart=0
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~2423~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                                   
                       
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  return_point
                          dw  core_code_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
         
         message_3        db  0x0d,0x0a,'  Loading user program...',0
         
         do_status        db  'Done.',0x0d,0x0a,0
         
         message_6        db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
                          db  '  User program terminated,control returned.',0

         bin_hex          db '0123456789ABCDEF'
                                            ;put_hex_dword子过程用的查找表 

         core_buf   times 2048 db 0         ;内核用的缓冲区

         esp_pointer      dd 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

core_data_end:

SECTION core_code vstart=0 
fill_descriptor_in_ldt:

    push eax 
    push edx 
    push edi 
    push ds 

    mov ecx,mem_0_4_gb_seg_sel              ;全局段
    mov ds,ecx 

    mov edi,[ebx+0x0c]                      ;获得LDT基地址

    xor ecx,ecx 
    mov cx,[ebx+0x0a]                       ;LDT界限
    inc cx                                  ;长度

    mov [edi+ecx+0x00],eax 
    mov [edi+ecx+0x04],edx                  ;在LDT末尾安装描述符

    add cx,8
    dec cx              ;新界限

    mov [ebx+0x0a],cx                       ;更新新界限

    mov ax,cx 
    xor dx,dx 
    mov cx,8
    div cx 

    mov cx,ax           ;ax=第几个
    shl cx,3
    or cx,0000_0000_0000_0100B              ;T1=1 RPL=0 (默认,程序返回后自己修改)

    pop ds
    pop edi 
    pop edx 
    pop eax 

    ret 

load_relocate_program:
    pushad 

    push ds 
    push es 

    mov ebp,esp 

    mov ecx,mem_0_4_gb_seg_sel
    mov es,ecx 
;ESI=TCB的基地址
    mov esi,[ebp+11*4]

;分配LDT空间
    mov ecx,160
    call sys_routine_seg_sel:allocate_memory
    mov [es:esi+0x0c],ecx               ;登记LDT基地址在TCB中
    mov word [es:esi+0x0a],0xffff       ;登记LDT界限到TCB中

;ds=内核数据段
    mov eax,core_data_seg_sel
    mov ds,eax 

    mov eax,[ebp+12*4]                  ;从栈中得到程序逻辑扇区号
    mov ebx,core_buf                    ;读入内核数据中的buffer
    call sys_routine_seg_sel:read_hard_disk_0

;
    mov eax,[core_buf]              ;程序大小
    mov ebx,eax 
    and ebx,0xfffffe00
    add ebx,512
    test eax,0x000001ff
    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
    mov ds,eax                      ;ds指向全局段描述符

    mov eax,[ebp+12*4]              ;eax=程序起始扇区号

.b1:
    call sys_routine_seg_sel:read_hard_disk_0
    inc eax 
    loop .b1

    mov edi,[es:esi+0x06]           ;从TCB中取到程序要加载到的地址

;建立程序head描述符(信息)
    mov eax,edi                     ;
    mov ebx,[edi+0x04]              ;段长度
    dec ebx                         ;段界限
    mov ecx,0x0040f200              ;DPL=3
    call sys_routine_seg_sel:make_seg_descriptor

;安装程序head描述符到LDT中
    mov ebx,esi                     ;esi=TCB基础地址
    call fill_descriptor_in_ldt

    or cx,0000_0000_0000_0011B      ;修改RPL=3
    mov [es:esi+0x44],cx            ;登记head选择子到TCB中
    mov [edi+0x04],cx               ;写回程序头部

;建立程序code段描述符在LDT中
    mov eax,edi 
    add eax,[edi+0x14]              ;eax=程序起始地址+代码段偏移地址
    mov ebx,[edi+0x18]              ;段长度
    dec ebx                         ;界限
    mov ecx,0x0040f800              ;RPL=3
    call sys_routine_seg_sel:make_seg_descriptor
    mov ebx,esi                     ;TCB基础地址
    call fill_descriptor_in_ldt
    or cx,0000_0000_0000_0011B      ;RPL=3
    mov [edi+0x14],cx               ;写回到程序head

;建立程序数据段描述符
    mov eax,edi                     ;程序加载地址
    add eax,[edi+0x1c]              ;程序起始地址+数据偏移地址
    mov ebx,[edi+0x20]              ;段长度
    dec ebx                         ;
    mov ecx,0x0040f200              ;RPL=3
    call sys_routine_seg_sel:make_seg_descriptor
    mov ebx,esi 
    call fill_descriptor_in_ldt
    or cx,0000_0000_0000_0011B      ;RPL=3
    mov [edi+0x1c],cx 

;建立程序栈描述符
    mov ecx,[edi+0x0c]
    mov ebx,0x000fffff
    sub ebx,ecx 
    mov eax,4096
    mul ecx 
    mov ecx,eax                     ;eax=实际byte数量
    call sys_routine_seg_sel:allocate_memory
    add eax,ecx                     ;栈的高地址
    mov ecx,0x00c0f600              ;RPL=3 
    call sys_routine_seg_sel:make_seg_descriptor
    mov ebx,esi                     ;TCB基地址
    call fill_descriptor_in_ldt
    or cx,0000_0000_0000_0011B      ;RPL=3
    mov [edi+0x08],cx               ;写回到程序head

;重定位程序的salt
    mov eax,mem_0_4_gb_seg_sel
    mov es,eax 

    mov eax,core_data_seg_sel
    mov ds,eax 

    cld 

    mov ecx,[es:edi+0x24]           ;从程序head得用户salt条数
    add edi,0x28                    ;edi = 从u-salt开始

.b2:
    push ecx 
    push edi 

    mov ecx,salt_items
    mov esi,salt 

.b3:
    push edi 
    push esi 
    push ecx 

    mov ecx,64
    repe cmpsd 
    jnz .b4 
    mov eax,[esi]                   ;偏移地址(有了门描述符用不到了)
    mov [es:edi-256],eax            ;写到用户salt中
    mov ax,[esi+4]                  ;内核例程的门描述符
    or ax,0000000000000011B         ;RPL=3 门代表下限
    mov [es:edi-252],ax             ;写到用户salt中

.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           ;登记0栈大小到TCB
    shr dword [es:esi+0x1a],12      ;4KB为单位
    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 
    call fill_descriptor_in_ldt
    ;or cx,0000_                    ;当前本来就是0特权级
    mov [es:esi+0x22],cx            ;登记栈选择子到TCB
    mov dword [es:esi+0x24],0       ;登记ESP

;1
    mov ecx,4096
    mov eax,ecx 
    mov [es:esi+0x28],ecx 
    shr word [es:esi+0x28],12
    call sys_routine_seg_sel:allocate_memory
    add eax,ecx 
    mov [es:esi+0x2c],eax 
    mov ebx,0xffffe
    mov ecx,0x00c0b600
    call sys_routine_seg_sel:make_seg_descriptor
    mov ebx,esi 
    call fill_descriptor_in_ldt
    or cx,0000_0000_0000_0001 
    mov [es:esi+0x30],cx 
    mov dword [es:esi+0x32],0

;2
    mov ecx,4096
    mov eax,ecx 
    mov [es:esi+0x36],ecx 
    shr word [es:esi+0x36],12 
    call sys_routine_seg_sel:allocate_memory
    add eax,ecx 
    mov [es:esi+0x3a],ecx 
    mov ebx,0xffffe
    mov ecx,0x00c0d600
    call sys_routine_seg_sel:make_seg_descriptor
    or cx,0000_0000_0000_0010
    mov [es:esi+0x3e],cx 
    mov dword [es:esi+0x40],0

;在GDT中登记LDT
    mov eax,[es:esi+0x0c]               ;LDT起始地址
    movzx ebx,word [es:esi+0x0a]        ;LDT段界限
    mov ecx,0x00408200                  ;LDT描述符 RPL=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                         ;最小大小
    mov [es:esi+0x12],cx                ;
    dec word [es:esi+0x12]              ;登记TCB中TSS界限
    call sys_routine_seg_sel:allocate_memory
    mov [es:esi+0x14],ecx               ;登记TSS起始地址到TCB

;登记TSS的基本内容
    mov word [es:ecx+0],0               ;上一个任务TSS=0

    mov edx,[es:esi+0x24]               ;0 esp
    mov [es:ecx+4],edx 

    mov dx,[es:esi+0x22]                ;0 段选择子
    mov [es:ecx+8],dx                     

    mov edx,[es:esi+0x32]               ;1 esp 
    mov [es:ecx+12],edx 

    mov dx,[es:esi+0x30]                ;1 段选择子
    mov [es:ecx+16],dx 

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

    mov dx,[es:esi+0x3e]                ;2 段选择子
    mov [es:ecx+24],dx 

    mov dx,[es:esi+0x10]                ;登记任务的LDT选择子到TSS
    mov [es:ecx+96],dx 

    mov dx,[es:esi+0x12]                ;登记任务的IO bit string
    mov [es:ecx+102],dx                 ;TSS界限值

    mov word [es:ecx+100],0             ;T = 0(任务切换不产生中断)

;在GDT中登记TSS描述符
    mov eax,[es:esi+0x14]               ;TSS起始地址
    movzx ebx,word [es:esi+0x12]        ;段长度
    mov ecx,0x00408900
    call sys_routine_seg_sel:make_seg_descriptor
    call sys_routine_seg_sel:set_up_gdt_descriptor  ;在GDT中安装TSS描述符
    mov [es:esi+0x18],cx                ;在TCB中登记TSS选择子

    pop es 
    pop ds 
    
    popad 

    ret 8                               ;esp+8 丢弃参数

;输入:ECX=当前线性地址
append_to_tcb_link:
    push eax 
    push edx 
    push ds 
    push es 

    mov eax,core_data_seg_sel
    mov ds,eax 

    mov eax,mem_0_4_gb_seg_sel
    mov es,eax 

    mov dword [es:ecx+0x00],0

    mov eax,[tcb_chain]             ;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 

.retpc:
    pop es 
    pop ds 
    pop edx 
    pop eax 

    ret 

start:
    mov ecx,core_data_seg_sel
    mov ds,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                    ;salt表起始位置
    mov ecx,salt_items              ;salt表条目数量\
;将core_data中的salt的段选择子+偏移地址换成门描述符,登记在GDT中
.b3:
    push ecx 
    mov eax,[edi+256]
    mov bx,[edi+260]
    mov cx,1_11_0_1100_000_00000B

    call sys_routine_seg_sel:make_gate_descriptor
    call sys_routine_seg_sel:set_up_gdt_descriptor
    mov [edi+260],cx 
    add edi,salt_item_len
    pop ecx 
    loop .b3 

    mov ebx,message_2 
    call far [salt_1+256]

    mov ebx,message_3
    call sys_routine_seg_sel:put_string
;创建TCB(Task control block) 我理解为OS层面上的东西(非处理器和存储器)
    mov ecx,0x46 
    call sys_routine_seg_sel:allocate_memory
    call append_to_tcb_link             ;加入到TCB链

    push dword 50                   ;程序位于50扇区 
    push ecx                        ;TCB起始地址

    call load_relocate_program      ;加载和重定位用户程序

    mov ebx,do_status
    call sys_routine_seg_sel:put_string

    mov eax,mem_0_4_gb_seg_sel
    mov ds,eax 

    ltr [ecx+0x18]                  ;加载TSS register 16 bit 选择子
    lldt [ecx+0x10]                 ;加载LDT

    mov eax,[ecx+0x44]
    mov ds,eax                      ;切换到用户head 

;假装从调用门返回,r0 -> r3
    push dword [0x08]               ;ss 
    push dword 0 

    push dword [0x14]               ;cs 
    push dword [0x10]               ;eip=程序code开始

    retf                            ;
return_point:
    mov eax,core_data_seg_sel
    mov ds,eax;由用户程序jmp来CPL=3导致中断

    mov ebx,message_6 
    call sys_routine_seg_sel:put_string

    hlt 

core_code_end:

SECTION core_trail

core_end:


  • 作业配的代码c14_core中:645,662行操作数大小没有指出来,4096 一个字的大小,加上word
    参考:《X86汇编实模式到保护模式》

你可能感兴趣的:(X86实模式和保护模式)