Chinaunix首页 | 论坛 | 博客
  • 博客访问: 468148
  • 博文数量: 62
  • 博客积分: 1742
  • 博客等级: 中尉
  • 技术积分: 859
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-06 00:13
个人简介

这是一句很长很长而且又很啰嗦并且很无聊的废话...

文章分类

全部博文(62)

文章存档

2013年(1)

2012年(13)

2011年(48)

分类: LINUX

2011-06-24 13:06:49

以下逻辑代码摘自Intel手册,先分析再总结.

(* Linux下看Intel的中断逻辑 *)
IF PE = 0 THEN #判断是否开启保护模式.
GOTO REAL-ADDRESS-MODE; #如果没开启就跳到REAL-ADDRESS-MODE里处理实模式流程.
ELSE (* PE = 1 *) #如果开启了保护模式.
IF (VM = 1 and IOPL < 3 AND INT n) THEN #判断EFLAGS的VM位和IOPL位.
  #GP(0); #在保护模式下,运行在虚拟8086模式(段寄存器纯粹作为基址x16使用)下,并且当前进程的IO特权值小于3,则产生异常.
ELSE (* Protected mode, IA-32e mode, or virtual-8086 mode interrupt *) #保护模式,IA-32e扩展模式,虚拟8086模式的中断.
IF (IA32_EFER.LMA = 0) THEN (* Protected mode, or virtual-8086 mode interrupt *) #如果是保护模式或者是虚拟8086模式就跳到PROTECTED-MODE流程.
GOTO PROTECTED-MODE; #我们暂时只关注这个,往下看.
ELSE (* IA-32e mode interrupt *) #否则就是IA-32e扩展模式(64位)
GOTO IA-32e-MODE; #跳到兼容模式IA-32e-MODE流程中.
FI;
FI;
FI;

(* 实模式 *)
REAL-ADDRESS-MODE:
IF ((vector_number ? 4) + 3) is not within IDT limit THEN
#GP;
FI;
IF stack not large enough for a 6-byte return information THEN
#SS;
FI;
Push (EFLAGS[15:0]);
IF ← 0; (* Clear interrupt flag *)
TF ← 0; (* Clear trap flag *)
AC ← 0; (* Clear AC flag *)
Push(CS);
Push(IP);
(* No error codes are pushed *)
CS ← IDT(Descriptor (vector_number ? 4), selector));
EIP ← IDT(Descriptor (vector_number ? 4), offset)); (* 16 bit offset AND 0000FFFFH *)
END;

(* 保护模式 *)
PROTECTED-MODE:
IF ((vector_number ? 8) + 7) is not within IDT limits or selected IDT descriptor is not an interrupt-, trap-, or task-gate type THEN #判断中断向量号.
#GP((vector_number ? 8) + 2 + EXT); #如果中断号大于IDT的界限,或者中断号对应的中断描述符中的门类型不是中断门,任务门,陷阱门中的一个,则产生异常.
FI;(* EXT is bit 0 in error code *) #EXT是错误代码0.
IF software interrupt (* Generated by INT n, INT 3, or INTO *) THEN #如果是软中断(通过int指令引发的中断).
IF gate descriptor DPL < CPL THEN #判断当前模式的CS选择子中的RPL是否小于中断号对应的中断描述符中的特权等级(模式保护的关键之一).
#GP((vector_number ? 8) + 2 ); #用户模式下则产生异常(CS->RPL = 3)(但也有中断描述符的DPL为3的,例如0x80号中断,就是专门给在用户模式下使能的中断).
FI;(* PE = 1, DPL
FI;
IF gate not present THEN #判断要中断的门是否存在.
#NP((vector_number ? 8) + 2 + EXT); #不存在就产生异常.
FI;
IF task gate (* Specified in the selected interrupt table descriptor *) THEN #判断门类型.
GOTO TASK-GATE; #任务门则跳到TASK-GATE流程.
ELSE
GOTO TRAP-OR-INTERRUPT-GATE; (* PE = 1, trap/interrupt gate *) #陷阱门或者中断门则跳到TRAP-OR-INTERRUPT-GATE流程.
FI;
END;

(* IA-32e扩展模式,兼容32位和64位,LMA=0(32),LMA=1(64) *)
IA-32e-MODE:
IF ((vector_number ? 16) + 15) is not in IDT limits or selected IDT descriptor is not an interrupt-, or trap-gate type THEN
#GP((vector_number ? 16) + 2 + EXT); 
FI;(* EXT is bit 0 in error code *)
IF software interrupt (* Generated by INT n, INT 3, but not INTO *) THEN
IF gate descriptor DPL < CPL THEN
#GP((vector_number ? 16) + 2 );
FI;(* PE = 1, DPL < CPL, software interrupt *)
ELSE (* Generated by INTO *)THEN
#UD;
FI;
IF gate not present THEN
#NP((vector_number ? 16) + 2 + EXT); 
FI;
IF ((vector_number * 16)[IST] ≠ 0) 
NewRSP ← TSS[ISTx];
FI;
GOTO TRAP-OR-INTERRUPT-GATE; (* Trap/interrupt gate *)
END;

(* 任务门 进程调度 *)
TASK-GATE: (* PE = 1, task gate *)Read segment selector in task gate (IDT descriptor); #触发进程调度的时钟中断的一种中断类型,任务门.
IF local/global bit is set to local or index not within GDT limits THEN #判断中断号对应的中断描述符.
#GP(TSS selector); #如果中断描述符中的段选择子的TI位等于LDT或者(GDT)段选择子的索引超出了GDT的界限,则产生异常.
FI;
Access TSS descriptor in GDT; #访问在GDT里面的TSS描述符.
IF TSS descriptor specifies that the TSS is busy (low-order 5 bits set to 00001) THEN #判断当前任务是否忙.
#GP(TSS selector); #忙则产生异常.
FI;
IF TSS not present THEN #判断任务状态描述符是否存在.
#NP(TSS selector); #不存在就产生异常.
FI;
SWITCH-TASKS (with nesting) to TSS; #执行任务切换(进程调度).
IF interrupt caused by fault with error code THEN #判断切换过程中是否产生故障.
IF stack limit does not allow push of error code THEN #判断栈还能否放进错误号.
#SS(0); #超过界限大小则产生异常.
FI;
Push(error code); #错误号进栈.
FI;
IF EIP not within code segment limit THEN #任务成功切换后,判断一下新任务的EIP是否超出了代码段描述符的寻址范围(4G).
#GP(0); #超出则产生异常.
FI;
END;

(* 陷阱门或者中断门 *)
TRAP-OR-INTERRUPT-GATE: Read segment selector for trap or interrupt gate (IDT descriptor);
IF segment selector for code segment is NULL THEN #判断代码段选择子是否为空.
#GP(0H + EXT); #为空则产生异常.
FI;(* NULL selector with EXT flag set *)
IF segment selector is not within its descriptor table limits THEN #判断代码段选择子的索引是否超出了他所在的描述符的范围界限(一般为GDT).
#GP(selector + EXT); #超出则产生异常.
FI;
Read trap or interrupt handler descriptor; #根据中断号读取对应的中断描述符.
IF descriptor does not indicate a code segment or code segment descriptor DPL > CPL THEN #判断中断描述符的Selector是不是一个代码段,并且中断描述符中的Base的Index对应的GDT或者LDT描述中的DPL是否大于当前CS的RPL(不允许使用中断方式从内核模式切换到用户模式).
#GP(selector + EXT); #上述条件成则产生异常.
FI;
IF trap or interrupt gate segment is not present THEN #判断陷阱或中断门段是否存在.
#NP(selector + EXT); #不存在则产生异常.
FI;
IF code segment is non-conforming and DPL < CPL THEN #如果当前模式为用户模式(非一致性的,当前模式跟目标模式不一致).
IF VM = 0 THEN #并且不是虚拟8086模式. 
GOTO INTER-PRIVILEGE-LEVEL-INTERRUPT;(* PE = 1, interrupt or trap gate, nonconforming code segment, DPL < CPL, VM = 0 *) #就跳到INTER-PRIVILEGE-LEVEL-INTERRUPT流程中(例如用户模式下执行的Int指令或者产生了一个硬中断).
ELSE (* VM = 1 *) #如果处于虚拟8086模式.
IF code segment DPL ≠ 0 THEN #如果切换的目标模式不是内核模式.
#GP;(new code segment selector);
GOTO INTERRUPT-FROM-VIRTUAL-8086-MODE; #就跳到INTERRUPT-FROM-VIRTUAL-8086-MODE流程中.
FI;(* PE = 1, interrupt or trap gate, DPL < CPL, VM = 1 *)
FI;
ELSE(* PE = 1, interrupt or trap gate, DPL ≥ CPL *) #如果当前模式为内核模式.(例如在内核模式时产生了一个硬中断或者异常)
IF VM = 1 THEN #如果处于虚拟8086模式.
#GP(new code segment selector); #直接产生异常.
FI;
IF code segment is conforming or code segment DPL = CPL THEN #判断中断例程要切换的模式(一致性的,同一模式).
GOTO INTRA-PRIVILEGE-LEVEL-INTERRUPT; #如果还是内核模式下运行则跳到INTRA-PRIVILEGE-LEVEL-INTERRUPT流程中.
ELSE
#GP(CodeSegmentSelector + EXT);(* PE = 1, interrupt or trap gate, nonconforming code segment, DPL > CPL *) #如果是从内核模式切换到用户模式执行中断则产生异常.
FI;
FI;
END;

(" 在特权等级模式中间,在用户模式下发生了中断,需要切换栈 ")
INTER-PRIVILEGE-LEVEL-INTERRUPT: (* PE = 1, interrupt or trap gate, non-conforming code segment, DPL < CPL *)(* Check segment selector and descriptor for stack of new privilege level in current TSS *)
IF current TSS is 32-bit TSS THEN #如果在32位环境下.
TSSstackAddress ← (new code segment DPL ? 8) + 4;
IF (TSSstackAddress + 7) > TSS limit THEN #如果栈段的段选择子索引超出GDT描述符表的界限
#TS(current TSS selector); #就产生异常.
FI;
NewSS ← TSSstackAddress + 4; #把NewSS(临时变量)置为TSS->SS0地址.
NewESP ← stack address; #把NewESP(临时变量)置为TSS->ESP0地址.
ELSE
IF current TSS is 16-bit TSS THEN(* TSS is 16-bit *) #如果在16位环境下.不管...
TSSstackAddress ← (new code segment DPL ? 4) + 2
IF (TSSstackAddress + 4) > TSS limit THEN
#TS(current TSS selector);
FI;
NewESP ← TSSstackAddress;
NewSS ← TSSstackAddress + 2;
ELSE (* TSS is 64-bit *) #如果在64位环境下.也不管...
NewESP ← TSS[RSP FOR NEW TARGET DPL];
NewSS ← 0;
FI;
FI;
IF segment selector is NULL THEN #判断段选择子SS0是否为空.
#TS(EXT); #为空则产生异常.
FI;
IF segment selector index is not within its descriptor table limits or segment selector's RPL ≠ DPL of code segment, THEN #如果段选择子SS0指向的堆栈段描述符的DPL不等于中断门或陷阱门的代码段描述符的DPL(因为CS和SS的RPL是CPU当前的CPL,所以需要一致).
#TS(SS selector + EXT); #就产生异常.
FI;
IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *) #如果是32位环境下.
Read segment descriptor for stack segment in GDT or LDT; #就在GDT中读取段描述符赋给SS段选择子.(是给选择子还是给选择子对应的非编程高速寄存器?求认证...)
IF stack segment DPL ≠ DPL of code segment, or stack segment does not indicate writable data segment THEN #如果TSS中的栈段的DPL跟目标代码段的DPL不一致,或者目标栈段是不可读写的.
#TS(SS selector + EXT); #就产生异常.
FI;
IF stack segment not present THEN #如果TSS中的栈段是不存在的话.
#SS(SS selector + EXT); #也产生异常.
FI;
FI
IF 32-bit gate THEN #如果是32位环境下.
IF new stack does not have room for 24 bytes (error code pushed) or 20 bytes (no error code pushed) THEN #如果切换的目标栈没有足够的空间存放错误代码或者...
#SS(segment selector + EXT); #就产生异常.
FI;
FI ELSE 
IF 16-bit gate THEN #不管...
IF new stack does not have room for 12 bytes (error code pushed) or 10 bytes (no error code pushed);
THEN #SS(segment selector + EXT);
FI;
ELSE (* 64-bit gate*) #也不管...
IF StackAddress is non-canonical THEN
#SS(0);
FI;
FI;
FI;
IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *) THEN #如果是32位环境下.
IF instruction pointer is not within code segment limits THEN #如果目标IDT->Offset不在IDT->Selector->Index->GDT->Limit范围内的话.
#GP(0); #产生异常.
FI;
SS:ESP ← TSS(NewSS:NewESP);(* Segment descriptor information also loaded *) #设置栈选择子和栈顶指针为内核模式的栈(TSS->SS0,TSS->ESP0).
ELSE #16位和64位的,不管...
IF instruction pointer points to non-canonical address THEN
#GP(0);
FI:
FI;
IF 32-bit gate THEN #如果是32位环境的门.
CS:EIP ← Gate(CS:EIP);(* Segment descriptor information also loaded *) #设置代码段选择子为内核模式(IDT的Selector),设置指令指针为IDT的Offset(至此,加上上面的栈段切换,用户模式已转为内核模式).
ELSE #16位或64位,不管...
IF 16-bit gate THEN 
CS:IP ← Gate(CS:IP);(* Segment descriptor information also loaded *)
ELSE (* 64-bit gate *)
CS:RIP ← Gate(CS:RIP);(* Segment descriptor information also loaded *)
FI;
FI;
IF 32-bit gate THEN #如果是32位环境的门. 
Push(far pointer to old stack);(* Old SS and ESP, 3 words padded to 4 *) #保存之前的SS和ESP(用户模式),因为SS是16位的,不过进栈(内核栈)操作会把它当做32位.共占8个字节.
Push(EFLAGS); #保存之前的(用户模式)标志寄存器的值到内核栈.
Push(far pointer to return instruction);(* Old CS and EIP, 3 words padded to 4 *) #保存之前的CS和EIP(用户模式),因为CS是16位的,不过进栈(内核栈)操作会把它当做32位.共占8个字节.
Push(ErrorCode);(* If needed, 4 bytes *) #如果需要,也会把错误代码也一起进栈(内核栈).
ELSE #16位和64位的,不管...
IF 16-bit gate THEN
Push(far pointer to old stack);(* Old SS and SP, 2 words *)
Push(EFLAGS(15-0]);
Push(far pointer to return instruction);(* Old CS and IP, 2 words *)
Push(ErrorCode);(* If needed, 2 bytes *)
ELSE (* 64-bit gate *)
Push(far pointer to old stack);(* Old SS and SP, each an 8-byte push *)
Push(RFLAGS);(* 8-byte push *)
Push(far pointer to return instruction);(* Old CS and RIP, each an 8-byte push *)
Push(ErrorCode);(* If needed, 8-bytes *)
FI;
FI;
CPL ← CodeSegmentDescriptor(DPL); #把CPL改为内核特权(IDT->Selector->Index->GDT->DPL).
CS(RPL) ← CPL; #再把CS的RPL设为CPL.
IF interrupt gate THEN #如果这是一个中断门(硬中断,由外部硬件触发).
IF ← 0 (* Interrupt flag set to 0: disabled *); #就把EFLAGS的IF位置0(屏蔽外部中断).
FI;
TF ← 0; #屏蔽单步中断.
VM ← 0; #屏蔽虚拟8086模式.
RF ← 0; #许可产生指令断点故障.
NT ← 0; #嵌套任务标志位置为0(因为这不是一个任务门).
END;

(* 虚拟8086模式的中断 *)
INTERRUPT-FROM-VIRTUAL-8086-MODE:(* Check segment selector and descriptor for privilege level 0 stack in current TSS *)
IF current TSS is 32-bit TSS THEN 
TSSstackAddress ← (new code segment DPL ? 8) + 4;
IF (TSSstackAddress + 7) > TSS limit THEN
#TS(current TSS selector);
FI;
NewSS ← TSSstackAddress + 4;
NewESP ← stack address;
ELSE (* TSS is 16-bit *)
TSSstackAddress ← (new code segment DPL ? 4) + 2;
IF (TSSstackAddress + 4) > TSS limit THEN
#TS(current TSS selector);
FI;
NewESP ← TSSstackAddress;
NewSS ← TSSstackAddress + 2;
FI;
IF segment selector is NULL THEN
#TS(EXT);
FI;
IF segment selector index is not within its descriptor table limits or segment selector's RPL ≠ DPL of code segment THEN
#TS(SS selector + EXT);
FI;
Access segment descriptor for stack segment in GDT or LDT;
IF stack segment DPL ≠ DPL of code segment,or stack segment does not indicate writable data segment THEN
#TS(SS selector + EXT);
FI;
IF stack segment not present THEN
#SS(SS selector + EXT);
FI;
IF 32-bit gate THEN
IF new stack does not have room for 40 bytes (error code pushed) or 36 bytes (no error code pushed) THEN
#SS(segment selector + EXT);
FI;
ELSE
IF 16-bit gate THEN
IF new stack does not have room for 20 bytes (error code pushed) or 18 bytes (no error code pushed) THEN
#SS(segment selector + EXT);
FI;
ELSE(* 64-bit gate*)
IF StackAddress is non-canonical THEN
#SS(0);
FI;
FI;
IF instruction pointer is not within code segment limits THEN
#GP(0);
FI;
tempEFLAGS ← EFLAGS;
VM ← 0;
TF ← 0;
RF ← 0;
NT ← 0;
IF service through interrupt gate THEN
IF = 0;
FI;
TempSS ← SS;
TempESP ← ESP;
SS:ESP ← TSS(SS0:ESP0);(* Change to level 0 stack segment *)(* Following pushes are 16 bits for 16-bit gate and 32 bits for 32-bit gates;Segment selector pushes in 32-bit mode are padded to two words *)
Push(GS);
Push(FS);
Push(DS);
Push(ES);
Push(TempSS);
Push(TempESP);
Push(TempEFlags);
Push(CS);
Push(EIP);
GS ← 0;(* Segment registers NULLified, invalid in protected mode *)
FS ← 0;
DS ← 0;
ES ← 0;
CS ← Gate(CS);
IF OperandSize = 32 THEN
EIP ← Gate(instruction pointer);
ELSE(* OperandSize is 16 *)
EIP ← Gate(instruction pointer) AND 0000FFFFH;
FI;(* Start execution of new routine in Protected Mode *)
END;

(* 内部特权等级中断,在内核模式下发生了中断,不需要需要切换栈 *)
INTRA-PRIVILEGE-LEVEL-INTERRUPT:(* PE = 1, DPL = CPL or conforming segment *)
IF 32-bit gate and IA32_EFER.LMA = 0 THEN #检查是否为32位环境.
IF current stack does not have room for 16 bytes (error code pushed) or 12 bytes (no error code pushed) THEN #如果当前栈没有足够的空间存放错误代码或者...
#SS(0); #则产生异常.
FI;
ELSE IF 16-bit gate #16位,不管... 
IF current stack does not have room for 8 bytes (error code pushed) or 6 bytes (no error code pushed) THEN
#SS(0);
FI;
ELSE(* 64-bit gate*) #64位,不管...
IF StackAddress is non-canonical THEN
#SS(0);
FI;
FI;
IF instruction pointer not within code segment limit THEN #如果目标IDT->Offset不在IDT->Selector->Index->GDT->Limit范围内的话.
#GP(0); #产生异常.
FI;
IF 32-bit gate THEN #如果是32位环境的门. 
Push (EFLAGS); #保存之前的(内核模式)标志寄存器的值到内核栈.
Push (far pointer to return instruction);(* 3 words padded to 4 *) #设置代码段选择子为IDT->Selector,设置指令指针为IDT->Offset.
CS:EIP ← Gate(CS:EIP);(* Segment descriptor information also loaded *) #保存之前的CS和EIP(内核模式),因为CS是16位的,不过进栈(内核栈)操作会把它当做32位.共占8个字节.
Push (ErrorCode);(* If any *) #如果有的话,也会把错误代码或者中断号也一起进栈(内核栈).
ELSE
IF 16-bit gate THEN #16位的,不管...
Push (FLAGS);
Push (far pointer to return location);(* 2 words *)
CS:IP ← Gate(CS:IP);(* Segment descriptor information also loaded *)
Push (ErrorCode);(* If any *)
ELSE(* 64-bit gate*) #64位的,不管...
Push(far pointer to old stack);(* Old SS and SP, each an 8-byte push *)
Push(RFLAGS);(* 8-byte push *)
Push(far pointer to return instruction);(* Old CS and RIP, each an 8-byte push *)
Push(ErrorCode);(* If needed, 8 bytes *)
CS:RIP ← GATE(CS:RIP);(* Segment descriptor information also loaded *)
FI;
FI;
CS(RPL) ← CPL; #把CS的RPL设为CPL(内核特权).
IF interrupt gate THEN #如果这是一个中断门(硬中断,由外部硬件触发).
IF ← 0; #就把EFLAGS的IF位置0(屏蔽外部中断).
FI;(* Interrupt flag set to 0: disabled *)
TF ← 0; #屏蔽单步中断.
NT ← 0; #嵌套任务标志位置为0(因为这不是一个任务门).
VM ← 0; #屏蔽虚拟8086模式.
RF ← 0; #许可产生指令断点故障.
END;


总结下,Linux下x86的中断,总体分为5种情况(不包括虚拟8086和IA32扩展模式).

当前CPU处于用户模式:

执行一条Int指令(软中断),首先会用CPL(CS->RPL和SS->RPL)跟中断号对应的中断
描述的DPL作比较,只有CPL <= DPL(值对比),才能往下走.接着判断门类型,根据门
类型会跳到不同的处理流程中去.陷阱门:一般如Int 0x80指令,经过一系列的检查
后(DPL,RPL,CPL对比,界限超出,无效地址之类检查),会到TSS中取出SS0和ESP0赋
给寄存器SS和ESP,接着到IDT中取出Selector和Offset赋给寄存器CS和EIP,然后改
变RPL和CPL(切换到内核模式),并且保存用户空间的SS,ESP,CS,EIP,EFLAGS到内核
栈中.最后将一些状态位清零,只有外部中断才清零IF位.

外部中断(硬中断),流程跟软中断大致相同,他对应的中断门,但是不会用CPL(CS->
RPL和SS->RPL)跟中断号对应的中断描述符的DPL作比较,直接就往下一步流程走了
.只是到最后一步的,它会把IF位清零(屏蔽外部中断).

当前CPU处于内核模式:

执行一条Int指令(软中断),因为本身就已经处于内核模式,所以不会去切换当前堆
栈,一系列的检查之后,就会到IDT中取出Selector和Offset赋给寄存器CS和EIP,改
变CS->RPL为内核特权,然后保存中断前的环境(内核空间),最后将一些状态位清零
,只有外部中断才清零IF位.

外部中断(硬中断),流程跟软中断大致相同,对应的门是中断门,只是到最后一步的
,它会把IF位清零(屏蔽外部中断).

当前CPU处于内核模式或者用户模式:

外部中断(硬中断),对应的是任务门.因为门得类型是任务门,所以没有过多的权限
检查,值得一提的是TSS中的B(忙)标志,如果任务切换产生自JMP或IRET指令,处理
器就会把当前任务(老任务)TSS描述符中的忙标志B复位;如果任务切换是由CALL
指令、异常或中断产生,则忙标志B不动.当流程检测到当前任务为忙状态,则产生
异常,否则将跳到调度进程的流程中.

阅读(2573) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~