zoukankan      html  css  js  c++  java
  • 知识复习(LDT+TSS+GATE+INTERRUPT)

    【1】README

    • 1.0)由于实现进程的切换任务,其功能涉及到 LDT + TSS +GATE + INTERRUPT;下面我们对这些内容进行复习;
    • 1.1) source code from orange’s implemention of a os .

    ##**【2】知识复习(LDT+TSS+GATE +** INTERRUPT) **2.1)LDT的复习**
    • (1)在GDT中定义 LDT 描述符;
    • (2)然后在实模式下,初始化 GDT中的LDT描述符;
    • (3)还要初始化 LDT中的段描述符(用局部任务代码去初始化 LDT 中 段描述符的基地址);
    • (4)加载GDT到GDTR;
    • (5)切换到保护模式;
    • (6)做完任务后,跳转到局部任务(jmp SelectorLDTCodeA:0),SelectorLDTCodeA作为LDT的选择子,用于索引LDT中段描述符,其初始化在实模式下完成;
    • (7)紧接着就跳转到该选择子对应的任务代码段去执行;

    2.2)对于GDT和LDT的结构,我们再做个总结
    LABEL_GDT:
    LABEL_DESC_LDT : Descriptor 0, LDTLen - 1, DA_LDT ; LDT
    SelectorLDT equ LABEL_DESC_LDT- LABEL_GDT

    LABEL_LDT
    LABEL_LDT_DESC_CODEA : Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位
    SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT: + SA_TIL

    ; 初始化 LDT 在 GDT 中的描述符
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_LDT
    mov word [LABEL_DESC_LDT + 2], ax
    shr eax, 16
    mov byte [LABEL_DESC_LDT + 4], al
    mov byte [LABEL_DESC_LDT + 7], ah

    ; 初始化 LDT 中的描述符
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_CODE_A
    mov word [LABEL_LDT_DESC_CODEA+ 2], ax
    shr eax, 16
    mov byte [LABEL_LDT_DESC_CODEA+ 4], al
    mov byte [LABEL_LDT_DESC_CODEA+ 7], ah

    ; Load LDT
    mov ax, SelectorLDT
    lldt ax
    jmp SelectorLDTCodeA :0 ; 跳入局部任务

    ; CodeA (LDT, 32 位代码段)
    [SECTION .la]
    ALIGN 32
    [BITS 32]
    LABEL_CODE_A :
    mov ax, SelectorVideo
    mov gs, ax ; 视频段选择子(目的)
    mov edi, (80 * 12 + 0) * 2 ; 屏幕第 10 行, 第 0 列。
    mov ah, 0Ch ; 0000: 黑底 1100: 红字
    mov al, 'L'
    mov [gs:edi],

    (Attention)显然,我们发现,加载到 ldt 寄存器 的 选择子是 GDT中 LDT段描述符 的选择子, 而调用局部描述符对应的目标代码时,我们用的是 LDT 中的该代码对应的选择子;(干货)


    **2.2)TSS的复习**
    • 由于每个任务可能在4个特权级间转移,故每个任务实际上需要4个堆栈;
    • 问题是:我们只有一个ss 和 esp, 那么当发生堆栈切换,我们该从哪里获取其他堆栈的ss 和 esp 呢?
      我们引入TSS, 它可以解决这个问题。
    • 我们再总结一下就是:不同特权级的代码段间的转移(更具体点,是从低特权级->高特权级),会发生堆栈切换,使得调用者的入栈的堆栈是针对调用者本身的堆栈, 而出栈操作是针对被调用者的堆栈,即入栈和出栈的堆栈不一致,使得特权级间跳转出错,故引入了 TSS;

    (Conclusion)我们再理一理 TSS 和 GDT 的结构关系
    ; 任务状态段描述符 LABEL_DESC_TSS + 选择子
    LABEL_GDT:
    ...........
    LABEL_DESC_TSS : Descriptor 0, TSSLen-1, DA_386TSS ; ( DA_386TSS == 89h )
    SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT

    ; TSS [add] (任务状态段的定义)
    [SECTION .tss]
    ALIGN 32
    [BITS 32]
    LABEL_TSS :
    DD 0 ; Back
    DD TopOfStack ; 0 级堆栈
    DD SelectorStack ;
    DD 0 ; 1 级堆栈
    DD 0 ;
    DD 0 ; 2 级堆栈
    DD 0 ;
    DD 0 ; CR3
    DD 0 ; EIP
    DD 0 ; EFLAGS
    DD 0 ; EAX
    DD 0 ; ECX
    DD 0 ; EDX
    DD 0 ; EBX
    DD 0 ; ESP
    DD 0 ; EBP
    DD 0 ; ESI
    DD 0 ; EDI
    DD 0 ; ES
    DD 0 ; CS
    DD 0 ; SS
    DD 0 ; DS
    DD 0 ; FS
    DD 0 ; GS
    DD 0 ; LDT
    DW 0 ; 调试陷阱标志
    DW $ - LABEL_TSS+ 2 ; I/O位图基址
    DB 0ffh ; I/O位图结束标志
    TSSLen equ $ - LABEL_TSS

    ; 初始化 TSS 描述符,实模式
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_TSS
    mov word [LABEL_DESC_TSS+ 2], ax
    shr eax, 16
    mov byte [LABEL_DESC_TSS+ 4], al
    mov byte [LABEL_DESC_TSS+ 7], ah

    ; Load TSS, 在保护模式中,从ring3->ring0之前
    ; 因为是先通过retf实现 ring0->ring3,然后通过门实现ring3->ring0(门目标段的特权级为0),ring3->ring0会发生堆栈切换,所以在这之前需要加载TSS进入 tr-任务寄存器
    mov ax, SelectorTSS
    ltr ax ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。

    push SelectorStack3
    push TopOfStack3
    push SelectorCodeRing3 ; 打印 '3'
    push 0
    retf
    (Attention) 从以上代码,初始化TSS的内存空间,创建 GDT中的 TSS 描述符 以及 在实模式下初始化TSS 的描述符, 最后跳转到 保护模式,在特权级切换之前,我们把 TSS段描述符在GDT 中的选择子加载到了 tr-任务寄存器中,这样方便 不同特权级代码间的切换 进行堆栈切换;


    **2.3)GATE的复习**
    • (1)在GDT中定义门描述符+门选择子 + 该门对应的代码段描述符及其选择子,从以下 门和门对应的代码段描述符 的定义可以看到,门描述符存储着该代码段描述符的选择子以建立它们间的联系;
      LABEL_DESC_CODE_DEST: Descriptor 0,SegCodeDestLen-1, DA_C+DA_32; 非一致代码段,32
      SelectorCodeDest equ LABEL_DESC_CODE_DEST- LABEL_GDT
      ; 门 目标选择子,偏移,DCount, 属性
      LABEL_CALL_GATE_TEST : Gate SelectorCodeDest, 0, 0, DA_386CGate+DA_DPL0
      SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT

    • (2)在实模式中初始化测试调用门的代码段描述符;(门对应的代码段,我们称其为调用门目标段)

        ; 初始化测试调用门的代码段描述符
         xor	eax, eax
         mov	ax, cs
         shl	eax, 4
         add	eax, LABEL_SEG_CODE_DEST ; (调用门目标段基地址)
         mov	word [LABEL_DESC_CODE_DEST + 2], ax
         shr	eax, 16
         mov	byte [LABEL_DESC_CODE_DEST + 4], al
         mov	byte [LABEL_DESC_CODE_DEST + 7], ah
      
    • (3)加载GDT到GDTR,并进入保护模式;

    • (4)做完任务后,测试调用门(call SelectorCallGateTest:0),注意,它这里的调用地址用的是 调用门选择子,通过调用门选择子->调用门描述符->门目标段选择子->门目标段描述符->门目标段基地址,即通过调用门选择子寻址到门目标段基地址去运行;
      ; 测试调用门(无特权级变换),将打印字母 'C'
      call SelectorCallGateTest:0


    **2.4)中断复习**
    • step0)构建中断处理程序函数(在32位代码段的保护模式中):

        _UserIntHandler:
        UserIntHandler	equ	_UserIntHandler - $$
         mov	ah, 0Ch	 ; 0000: 黑底    1100: 红字
         mov	al, 'I'
         mov	[gs:((80 * 0 + 70) * 2)], ax	; 屏幕第 0 行, 第 70 列。
         iretd
      
        _SpuriousHandler:
        SpuriousHandler	equ	_SpuriousHandler - $$
         mov	ah, 0Ch	 ; 0000: 黑底    1100: 红字
         mov	al, '!'
         mov	[gs:((80 * 0 + 75) * 2)], ax	; 屏幕第 0 行, 第 75 列。
         jmp	$
         iretd
      
    • step1)构建IDT,IDT表项也就是门(中断门+陷阱门),主要是为中断向量号(依据表项索引)绑定中断处理程序,(为演示方便,特别为向量号 80h 绑定了中断处理程序),要知道,中断向量号 把中断异常的处理程序 与 中断异常类型联系了起来;

        [SECTION .idt]
        ALIGN	32
        [BITS	32]
        LABEL_IDT:
        ; 门                        目标选择子,            偏移, DCount, 属性
        %rep 128
          Gate	SelectorCode32, SpuriousHandler, 0, DA_386IGate
        %endrep
        .080h:	 Gate	SelectorCode32, UserIntHandler,  0, DA_386IGate
        IdtLen	 equ	$ - LABEL_IDT
        IdtPtr	 dw	IdtLen - 1	; 段界限
          dd	0	 ; 基地址
      
    • step2)实模式下,为加载IDTR做准备, 并将IDT(基地址+段界限)加载到 IDTR;

        ; 为加载 IDTR 作准备
         xor	eax, eax
         mov	ax, ds
         shl	eax, 4
         add	eax, LABEL_IDT	 ; eax <- idt 基地址
         mov	dword [IdtPtr + 2], eax	; [IdtPtr + 2] <- idt 基地址
         ; 加载 GDTR
         lgdt	[GdtPtr]
         ; 关中断
         cli
         ; 加载 IDTR
         lidt	[IdtPtr]
      
    • step4)向主8259A写入OCW1,以开启定时器中断, 然后向从8259A写入OCW1 以屏蔽从8259A所有中断;

        ; start Init8259A  
        Init8259A:
        	mov	al, 011h
        	out	020h, al	; 主8259, ICW1.
        	call	io_delay
        
        	out	0A0h, al	; 从8259, ICW1.
        	call	io_delay
        
        	mov	al, 020h	; IRQ0 对应中断向量 0x20
        	out	021h, al	; 主8259, ICW2.
        	call	io_delay
        
        	mov	al, 028h	; IRQ8 对应中断向量 0x28
        	out	0A1h, al	; 从8259, ICW2.
        	call	io_delay
        
        	mov	al, 004h	; IR2 对应从8259
        	out	021h, al	; 主8259, ICW3.
        	call	io_delay
        
        	mov	al, 002h	; 对应主8259的 IR2
        	out	0A1h, al	; 从8259, ICW3.
        	call	io_delay
        
        	mov	al, 001h
        	out	021h, al	; 主8259, ICW4.
        	call	io_delay
        
        	out	0A1h, al	; 从8259, ICW4.
        	call	io_delay
        
        	mov	al, 11111110b	; 仅仅开启定时器中断
        	;mov	al, 11111111b	; 屏蔽主8259所有中断
        	out	021h, al	; 主8259, OCW1.
        	call	io_delay
        
        	mov	al, 11111111b	; 屏蔽从8259所有中断
        	out	0A1h, al	; 从8259, OCW1.
        	call	io_delay
        
        	ret
        ; over Init8259A  
      
    • step5)触发中断 int 080h;

  • 相关阅读:
    前端总结(二)跨域
    前端总结(一)闭包
    html2canvas截图白边显示问题
    阻止回车刷新页面的方法
    mac下搭建redis环境
    Mac上的Apache 开启,停止,重启
    mac系统下安装、启动、停止mongodb
    Python实战之制作瘟疫传播实验
    Pyhton表白代码——浪漫圣诞节
    Python网络爬虫——BeautifulSoup4库的使用
  • 原文地址:https://www.cnblogs.com/pacoson/p/4833475.html
Copyright © 2011-2022 走看看