中断处理与特权级转移

问题

8259A在中断特殊完全嵌套方式下,同一个引脚的新中断是否可以打断旧中断的处理?

问题剖析

 假设:时钟请求周期为5ms,对应的中断服务程序执行时间为10ms;那么中断服务程序是否会被新的时钟请求打断?

我们初始化8259A时,将主片的中断方式设置为特殊完全嵌套方式,同一个引脚的新中断可以打断旧中断的处理。理应来说,处理器在处理时钟的中断服务程序时,会响应8259A新发送来的时钟请求,打断当前的执行,从而去执行新的时钟中断服务程序,但事实上却不是这样的。

关于中断优先级

中断优先级由8259A管理 (高优先级中断请求优先送往处理器)

处理器决定是否响应中断请求 (处理器没有中断优先级的概念)

在默认情况下

  • 中断服务程序执行时,屏蔽外部中断请求 (IF = 0)
  • 中断服务程序返回后,重新响应外部中断 (IF = 1)

我们接着上节课的程序,通过断点调试查看IF寄存器的值。

在时钟中断服务程序调用之前,IF为大写,说明处理器的中断总开关是打开的,可以接收外部中断。

在进入时钟中断服务程序时,if为小写,处理器的中断总开关是关闭的,说明了处理器在处理中断服务程序时,会自动把中断总开关关闭,不会响应外部中断。 

中断嵌套

如果希望高优先级中断请求打断当前中断服务程序,可以在中断服务程序中打开IF,即:将IF设置为1 (sti)。 

当代操作系统的设计

应用程序 (DPL3) 执行系统调用后会陷入内核态 (DPL0)

自定义软中断用于系统调用 (int 0x80)

通过软中断陷入内核态以最高特权级 (DPL0) 执行系统调用

中断服务程序运行于内核态 (DPL0)

与调用门类似

如果中断发生时涉及特权级转移,那么必须为不同特权级准备不同的栈

中断特权级转移过程

1. 处理器通过中断向量找到对应的中断描述符

2. 特权级检查:

  • 软中断:(目标代码段DPL <= CPL) && (CPL <= 中断描述符DPL)
  • 外部中断:CPL >= 目标代码段DPL

3. 加载目标代码段选择子到cs,加载偏移地址到ip

中断发生时的压栈

中断服务程序返回

iret使得处理器从内核态返回用户态

返回时进行特权级检查

  • CPL <= 目标代码段DPL (高特权级 -> 低特权级)
  • 对相关寄存器强制清零 (指向高特权级数据的段寄存器)

中断结束时的栈恢复

eflags标志寄存器

 IF:系统标志位,决定是否响应外部中断

  • IF ==1,响应外部中断
  • IF == 0, 屏蔽外部中断

IOPL:系统标志位,决定是否进行IO操作

  • CPL <= IOPL 才允许访问IO端口
  • 当前仅当 CPL == 0 时才能改变 IOPL 的值

设置IOPL的方法

x86汇编语言没有提供指令改变 eflags 寄存器的值,因此,只能利用 pushf 和 popf 指令间接改变。

 当前程序的 IOPL = 0,我们要将 IOPL 改为3。

    pushf
    pop eax
    
    or eax, 0x3000
    
    push eax
    popf

我们加入上述代码后,改变了IOPL的值,IOPL = 3。

目标实验:使用软中断实现系统调用

  1. 定义32位核心代码段 (中断函数,系统函数)
  2. 定义32位任务代码段和数据段 (用户程序)
  3. 通过软中断 (int 0x80) 转移到内核态调用系统函数 (低 -> 高)
  4. 在任务代码段使用软中断 (int 0x80) 实现功能函数

目标实验

注意事项

将IOPL设置为3使得用户态和内核态均可访问IO端口

特权级转移时会发生栈的变化 (定义TSS结构,定义不同栈段)

在用户态通过sti指令使得处理器响应外部中断 (必须用户态)

0x80中断设计 (系统调用设计)

ax == 0:外部设备中断初始化 (InitDevInt)

ax == 1:字符串打印 (Printf)

ax == 2:启动时钟中断 (EnableTimer)

软中断实现系统调用

%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]
; GDT definition
;                                 段基址,       段界限,       段属性
GDT_ENTRY		: Descriptor		0, 			    0,		      0
CODE32_DESC		: Descriptor		0, 	   Code32SegLen - 1,  DA_32 + DA_C + DA_DPL3
VIDEO_DESC	    : Descriptor     0xb8000,       0x7fff,       DA_32 + DA_DRWA + DA_DPL3
DATA32_DESC		: Descriptor	    0,     Data32SegLen - 1,  DA_32 + DA_DRW + DA_DPL3
STACK32U_DESC	: Descriptor        0,      TopOfStack32U,    DA_32 + DA_DRW +DA_DPL3
STACK32K_DESC	: Descriptor        0,      TopOfStack32K,    DA_32 + DA_DRW + DA_DPL0
TSS_DESC	    : Descriptor        0,        TssLen - 1,     DA_32 + DA_386TSS + DA_DPL0
KERNEL32_DESC   : Descriptor        0,     KernelSegLen - 1,  DA_32 + DA_C + DA_DPL0
; GDT end  

GdtLen	  equ	   $ - GDT_ENTRY
GdtPtr:    
		dw	GdtLen - 1
		dd	0

; GDT Selector
Code32Selector	 equ    (0x0001 << 3) + SA_TIG + SA_RPL3    
VideoSelector	 equ    (0x0002 << 3) + SA_TIG + SA_RPL3
Data32Selector   equ    (0x0003 << 3) + SA_TIG + SA_RPL3
Stack32USelector equ    (0x0004 << 3) + SA_TIG + SA_RPL3
Stack32KSelector equ    (0x0005 << 3) + SA_TIG + SA_RPL0
TssSelector      equ    (0x0006 << 3) + SA_TIG + SA_RPL0
Kernel32Selector equ    (0x0007 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]

[section .idt]
align 32
[bits 32]
IDT_ENTRY:
; IDT definition
;                           Selector,           Offset,            DCount,           Attribut
%rep 32
             Gate        Kernel32Selector,     DefaultHandler,        0,             DA_386IGate + DA_DPL3
%endrep
Int0x20 :    Gate        Kernel32Selector,     TimerHandler,          0,             DA_386IGate + DA_DPL3
%rep 95
             Gate        Kernel32Selector,     DefaultHandler,        0,             DA_386IGate + DA_DPL3
%endrep
Int0x80 :    Gate        Kernel32Selector,     Int0x80Handler,        0,             DA_386IGate + DA_DPL3
%rep 127
             Gate        Kernel32Selector,     DefaultHandler,        0,             DA_386IGate + DA_DPL3
%endrep
; IDT end

IdtLen      equ     $ - IDT_ENTRY
IdtPtr:
        dw  IdtLen - 1
        dd  0

TopOfStack16	equ    0x7c00

[section .tss]
[bits 32]
TSS_SEGMENT:
    dd  0
    dd  TopOfStack32K       ; 0
    dd  Stack32KSelector    ; 
    dd  0                   ; 1
    dd  0                   ;
    dd  0                   ; 2
    dd  0                   ; 
    times 4 * 18 dd 0
    dw  0
    dw  $ - TSS_SEGMENT + 2
    db  0xFF
    
 TssLen     equ     $ - TSS_SEGMENT

[section .dat]
[bits 32]
DATA32_SEGMENT:
	DTOS		        db    "D.T.OS!", 0
	DTOS_Offset	        equ	  DTOS - $$ 

	HELLOWORLD		    db    "Hello, World!", 0
	HELLOWORLD_Offset   equ   HELLOWORLD - $$
	
	INT_0X80            db    "int 0x80", 0
	INT_0X80_Offset     equ   INT_0X80 - $$

Data32SegLen	equ   $ - DATA32_SEGMENT

[section .s16]
[bits 16]
ENTRY_SEGMENT:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, TopOfStack16

	; initialize GDT for 32 bits code segment
	mov esi, CODE32_SEGMENT
	mov edi, CODE32_DESC
	call InitDescItem

	mov esi, DATA32_SEGMENT
	mov edi, DATA32_DESC
	call InitDescItem

	mov esi, STACK32U_SEGMENT
	mov edi, STACK32U_DESC
	call InitDescItem
	
	mov esi, STACK32K_SEGMENT
	mov edi, STACK32K_DESC
	call InitDescItem
	
	mov esi, TSS_SEGMENT
	mov edi, TSS_DESC
	call InitDescItem
	
	mov esi, KERNEL32_SEGMENT
	mov edi, KERNEL32_DESC
	call InitDescItem

	; initialize GDT pointer struct
	mov eax, 0
	mov ax, ds
	shl eax, 4
	add eax, GDT_ENTRY
	mov dword [GdtPtr + 2], eax
	
	; initialize IDT pointer struct
	mov eax, 0
	mov ax, ds
	shl eax, 4
	add eax, IDT_ENTRY
	mov dword [IdtPtr + 2], eax

	; 1. load GDT
    lgdt [GdtPtr]
    
    ; 2. close interrupt
    ;    load IDT
    ;    change IOPL
    cli 
    
    lidt [IdtPtr]
    
    pushf
    pop eax
    
    or eax, 0x3000
    
    push eax
    popf
    
    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al
    
    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax
    
    ; 5. load TSS
    mov ax, TssSelector
    ltr ax
    
    ; 6. jump to 32 bits code
    ; jmp dword Code32Selector : 0
    push Stack32USelector
    push TopOfStack32U
    push Code32Selector
    push 0
    retf
    

; esi    --> code segment labelBACK_ENTRY_SEGMENT
; edi    --> descriptor label
InitDescItem:
	push eax

	mov eax, 0
	mov ax, cs
	shl eax, 4
	add eax, esi
	mov word [edi + 2], ax
	shr eax, 16
	mov byte [edi + 4], al
	mov byte [edi + 7], ah
	
	pop eax

	ret

[section .s32]
[bits 32]
CODE32_SEGMENT:
	mov ax, VideoSelector
	mov gs, ax

	mov ax, Stack32USelector
	mov ss, ax

	mov eax, TopOfStack32U
	mov esp, eax

	mov ax, Data32Selector
	mov ds, ax

	mov ebp, DTOS_Offset
	mov dh, 13
	mov dl, 33
	call Printf
	
	call InitDevInt
	
	call EnableTimer

	jmp $

InitDevInt:
    push ax
    
    sti
    
    mov ax, 0
    int 0x80
    
    pop ax
    
    ret

; ds:ebp    --> string address
; dx        --> dh : row, dl : col
Printf:
    push ax
    push bx
    
    mov ax, 1
    mov bx, 0x0C
    
    int 0x80
    
    pop bx
    pop ax
    
    ret

EnableTimer:
    push ax
    
    mov ax, 2
    int 0x80
    
    pop ax
    
    ret

Code32SegLen	equ    $ - CODE32_SEGMENT

[section .knl]
[bits 32]
KERNEL32_SEGMENT:

DefaultHandlerFunc:

    iret
    
DefaultHandler      equ     DefaultHandlerFunc - $$

Int0x80HandlerFunc:
ax0:
    cmp ax, 0
    jnz ax1
    call InitDevIntFunc
    
    iret
ax1:
    cmp ax, 1
    jnz ax2 
    call PrintString

    iret
        
ax2:
    cmp ax, 2
    jnz ax3
    
    call EnableTimerFunc
    iret
ax3:
    iret
    
Int0x80Handler      equ     Int0x80HandlerFunc - $$

TimerHandlerFunc:
    push ax
    push dx
    
    mov ax, [gs : ((15 * 80 + 32) * 2)]
    cmp al, '9'
    je throtate
    inc al
    jmp thshow

throtate:
    mov al, '0'
    
thshow:  
    mov [gs : ((15 * 80 + 32) * 2)], ax
    
    mov dx, MASTER_OCW2_PORT
    call WriteEOI

    pop ax
    pop dx
    
    iret

TimerHandler        equ     TimerHandlerFunc - $$

; ds:ebp    --> string address
; bx        --> attribute
; dx        --> dh : row, dl : col
PrintString:
	push ebp
	push cx
	push eax
	push dx
	push edi

print:
	mov cl, [ds:ebp]
	cmp cl, 0
	je end
	mov eax, 80
	mul dh
	add al, dl
	shl eax, 1
	mov edi, eax
	mov ah, bl
	mov al, cl
	mov [gs:edi], ax
	inc ebp
	inc dl
	jmp print
	
end:
	pop edi
	pop dx
	pop eax
	pop cx
	pop ebp

	ret

;
;
EnableTimerFunc:
    push ax
    push dx
    
    mov ah, 0x0C
    mov al, '0'
    mov [gs : ((15 * 80 + 32) * 2)], ax
    
    mov dx, MASTER_IMR_PORT
    call ReadIMR
    and ax, 0xFE
    call WriteIMR
    
    pop ax
    pop dx
    
    ret

;
;
Delay:
    %rep 5
    nop
    %endrep
    
    ret
    
;
;
Init8259A:
    push ax
    
    ; Master
    ; ICW1
    mov al, 00010001B
    out MASTER_ICW1_PORT, al
    call Delay
    
    ; ICW2
    mov al, 0x20
    out MASTER_ICW2_PORT, al
    call Delay
    
    ; ICW3
    mov al, 00000100B
    out MASTER_ICW3_PORT, al
    call Delay
    
    ; ICW4
    mov al, 00010001B
    out MASTER_ICW4_PORT, al
    call Delay
    
    ; Slave
    ; ICW1
    mov al, 00010001B
    out SLAVE_ICW1_PORT, al
    call Delay
    
    ; ICW2
    mov al, 0x28
    out SLAVE_ICW2_PORT, al
    call Delay
    
    ; ICW3
    mov al, 00000010B
    out SLAVE_ICW3_PORT, al
    call Delay
    
    ; ICW4
    mov al, 00000001B
    out SLAVE_ICW4_PORT, al
    call Delay
    
    pop ax
    
    ret

;al  --> IMR register value
;dx  --> 8259A port
WriteIMR:
    out dx, al
    call Delay
    
    ret
    
; dx  --> 8259A port
; return:
;    al  --> IMR register value
ReadIMR:
    in al, dx
    call Delay
    
    ret

; 
; dx  --> 8259A port
WriteEOI:
    push ax
    
    mov al, 0x20
    out dx, al
    call Delay
    
    pop ax
    
    ret

InitDevIntFunc:
    push ax
    push dx
    
    call Init8259A
    
    mov al, 0xFF
    mov dx, MASTER_IMR_PORT
    call WriteIMR
    
    mov al, 0xFF
    mov dx, SLAVE_IMR_PORT
    call WriteIMR
    
    pop dx
    pop ax
    
    ret

KernelSegLen     equ    $ - KERNEL32_SEGMENT

[section .gsu]
[bits 32]
STACK32U_SEGMENT:
	times 1024 * 4 db 0

Stack32USegLen	 equ    $ - STACK32U_SEGMENT
TopOfStack32U    equ    Stack32USegLen - 1

[section .gsk]
[bits 32]
STACK32K_SEGMENT:
	times 1024 * 4 db 0

Stack32KSegLen	 equ    $ - STACK32K_SEGMENT
TopOfStack32K    equ    Stack32KSegLen - 1

因为当前程序需要用户态和内核态之间的相互切换,所以我们需要定义DPL为0的内核态对应的栈段,DPL为3的用户态对应的栈段,并且需要定义TSS。

我们需要将中断描述符的DPL设置为3,这里中断门和调用门的作用类似,就像蹦床一样,将我们从用户态转移到内核态,这里的中断描述符的DPL应当 >= CPL,否则就无法从低特权级转移到高特权级去执行。

我们在任务代码段实现了3个系统调用,这3个系统调用都是通过0x80号中断来从用户态陷入内核态来执行系统函数。

内核代码段的0x80号中断是通过ax的值来执行相应的系统函数,我们可以拓展ax对应更多的系统函数,来为用户提供更多的系统调用。

注意:必须要在用户态通过sti指令使得处理器响应外部中断 。外部中断的特权级判断和中断描述符的DPL没有关系。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值