4.4 系统异常

本文主要介绍了如何设置中断描述符表(IDT),并通过ignore_int处理程序处理所有异常。此外,讨论了32位和64位模式下的TSS(任务状态段)结构,以及IST(中断堆栈表)在异常响应中的作用。最后,提到了执行除以零操作时为何触发中断0。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程序4-4 代码功能

 lidt IDT_POINTER(%rip)
 ...

setup_IDT:    // ignore_int(%rip) = 0x1111 2222 3333 4444
 #首先ignore_int中断服务程序的偏移地址(OFFSET)就可以使用lea指令取出ignore_int标号的基地址

 #这个基地址要被拆分成第0-15位和第48-63位,分别放在EAX和EDX寄存器中
 #然後在將EAX的值加載到中斷描述符的低位,將EDX的值加載到中斷描述符的高位。
 leaq ignore_int(%rip), %rdx     ;假如 ignore_int 函数 的地址为 %rdx = 0x2222 3333 4444 5555
 movq $(0x08 << 16), %rax       ; 段选择子  %rax = 0x0000 0000 0008 0000 = 0b 0000 0000 0000 1000   位数: 0-1 RPL 请求特权级  1-2   TI 指示目标段描述符所在描述符表类型   3-15 用于索引目标段描述符  2   #段选择符,我们要选用代码段的段选择符,所以我们使用0008h号GDT段选择符。
 movw %dx, %ax                 ;   段内偏移 15:00  %ax =  ignore_int 函数的低16位 %ax = 0x5555 %rax = 0x0000 0000 0008 5555
 movq $(0x8E00 << 32), %rcx    ;  %rcx = 0x0000 8E00 0000 0000 = 0b0000 0000 0000 0000 1000 1110 0000 0000  32-34 IST ( Interrupt Stack Table,中断枝表)是IA-32e模式为任务状态段引人的新型战指针,其功能与 RSP相同,只不过IST切换中断棋指针时不会考虑特权级切换。 35-39:0 40-43:Type 第40-43位为段描述符类型标志(TYPE),我们设置的是1110.即将此段描述符标记为“386中断门”。  44-44:0 45-46:DPL 描述符特权级  47-48:P 指定调用门描述符是否有效
 addq %rcx, %rax              ;   %rax = 0x0000 8E00 0008 5555  %rcx = 0x0000 8E00 0000 0000        
 movl %edx, %ecx              ;   %ecx = ignore_int 函数的低32位 0x4444 5555    %rcx = 0x0000 8E00 4444 5555
 shrl $16, %ecx               ;   %ecx =  0x0000 4444 %rcx = 0x0000 8E00 0000 4444 
 shlq $48, %rcx               ;   %rcx =  4444 0000 0000 0000
 addq %rcx, %rax              ;   %rax =  0x4444 8E00 0008 5555        
 shrq $32, %rdx              ;    %rdx = 0x0000 0000 2222 3333 
 leaq IDT_Table(%rip), %rdi ;
  mov $256, %rcx
 rp_sidt:
  movq %rax, (%rdi)
  movq %rdx, 8(%rdi)
  addq $0x10, %rdi
  dec %rcx
  jne rp_sidt

·模块setup_IDT负责往IDT(中断描述符表)填入中断描述符(单个表项占用16Byte),中断描述符的格式参考下图:

在这里插入图片描述

ignore_int:
/*#CLD指令即告訴程序si,di向內存地址增大的方向走。*/
 cld
 pushq %rax
 pushq %rbx
 pushq %rcx
 pushq %rdx
 pushq %rbp
 pushq %rdi
 pushq %rsi

 pushq %r8
 pushq %r9
 pushq %r10
 pushq %r11
 pushq %r12
 pushq %r13
 pushq %r14
 pushq %r15

 movq %es, %rax
 pushq %rax
 movq %ds, %rax
 pushq %rax

 movq $0x10, %rax
 movq %rax, %ds
 movq %rax, %es

 leaq int_msg(%rip), %rax
 pushq %rax
 movq %rax, %rdx
 movq $0x00000000, %rsi
 movq $0x00ff0000, %rdi
 movq $0, %rax
 callq color_printk
 addq $0x8, %rsp

Loop:
 jmp Loop

 popq %rax
 movq %rax, %ds
 popq %rax
 movq %rax, %es

 popq %r15
 popq %r14
 popq %r13
 popq %r12
 popq %r11
 popq %r10
 popq %r9
 popq %r8

 popq %rsi
 popq %rdi
 popq %rbp
 popq %rdx
 popq %rcx
 popq %rbx
 popq %rax
 iretq

int_msg:
 .asciz "Unknown interrupt or fault at RIP\n"

·目前,只写了一个中断处理程序ignore_int,功能是在发生中断时在屏幕显示黑底红字的错误信息"Unknown interrupt or fault at RIP\n",并随即进入死循环;

·根据描述符格式要求,现在利用ignore_int所在的内核数据段选择子0x08以及ignore_int的入口偏移地址,来创建中断描述符,并且往目前的IDT表的全部256个表项都填入这个相同的描述符(即目前无论发什么异常错误,都调用的是ignore_int)

设置TSS

TSS段主要用于任务切换(或特权级切换) 时,保存处理器的寄存器状态,以及切换至对应的 特权级栈空间,从而使得任务返回时能够还原执行现场。 下图是一个32位的任务状态段的内部结 构。 (16位的TSS段只用在Intel286处理中。 )

32位模式下的tss

TSS被分为两部分 : 动态区域和静态区域。 当任务在切换过程中挂起时, 处理器会将执行现场保 存在动态区域内,下图归纳了32位 TSS的动态区域。
在这里插入图片描述

64位模式下的tss

下图的TSS描述符位功能非常好理解, 而且功能也并无变化,但TSS的内部结构却发生革命性的 变化。 既然TSS不再需要保存和还原程序 (或任务)的执行现场环境,那么它只负责不同特权级间的栈切换工作 。
下图是IA-32e模式TSS的 内部结构 。
在这里插入图片描述

为 TSS 设置 IST

void set_tss64(unsigned long rsp0,unsigned long rsp1,unsigned long rsp2,unsigned long ist1,unsigned long ist2,unsigned long ist3,
unsigned long ist4,unsigned long ist5,unsigned long ist6,unsigned long ist7)
{
	*(unsigned long *)(TSS64_Table+1) = rsp0;
	*(unsigned long *)(TSS64_Table+3) = rsp1;
	*(unsigned long *)(TSS64_Table+5) = rsp2;

	*(unsigned long *)(TSS64_Table+9) = ist1;
	*(unsigned long *)(TSS64_Table+11) = ist2;
	*(unsigned long *)(TSS64_Table+13) = ist3;
	*(unsigned long *)(TSS64_Table+15) = ist4;
	*(unsigned long *)(TSS64_Table+17) = ist5;
	*(unsigned long *)(TSS64_Table+19) = ist6;
	*(unsigned long *)(TSS64_Table+21) = ist7;	
}
	set_tss64(_stack_start, _stack_start, _stack_start, 0xffff800000007c00, 0xffff800000007c00, 0xffff800000007c00, 0xffff800000007c00, 0xffff800000007c00, 0xffff800000007c00, 0xffff800000007c00);

IST和IDT的实际使用示例

/*

*/

#define _set_gate(gate_selector_addr,attr,ist,code_addr)	\
do								\
{	unsigned long __d0,__d1;				\
	__asm__ __volatile__	(	"movw	%%dx,	%%ax	\n\t"	\
					"andq	$0x7,	%%rcx	\n\t"	\
					"addq	%4,	%%rcx	\n\t"	\
					"shlq	$32,	%%rcx	\n\t"	\
					"addq	%%rcx,	%%rax	\n\t"	\
					"xorq	%%rcx,	%%rcx	\n\t"	\
					"movl	%%edx,	%%ecx	\n\t"	\
					"shrq	$16,	%%rcx	\n\t"	\
					"shlq	$48,	%%rcx	\n\t"	\
					"addq	%%rcx,	%%rax	\n\t"	\
					"movq	%%rax,	%0	\n\t"	\
					"shrq	$32,	%%rdx	\n\t"	\
					"movq	%%rdx,	%1	\n\t"	\
					:"=m"(*((unsigned long *)(gate_selector_addr)))	,					\
					 "=m"(*(1 + (unsigned long *)(gate_selector_addr))),"=&a"(__d0),"=&d"(__d1)		\
					:"i"(attr << 8),									\
					 "3"((unsigned long *)(code_addr)),"2"(0x8 << 16),"c"(ist)				\
					:"memory"		\
				);				\
}while(0)

void sys_vector_init()
{
	set_trap_gate(0,1,divide_error);
	set_trap_gate(1,1,debug);
	set_intr_gate(2,1,nmi);
	set_system_gate(3,1,int3);
	set_system_gate(4,1,overflow);
	set_system_gate(5,1,bounds);
	set_trap_gate(6,1,undefined_opcode);
	set_trap_gate(7,1,dev_not_available);
	set_trap_gate(8,1,double_fault);
	set_trap_gate(9,1,coprocessor_segment_overrun);
	set_trap_gate(10,1,invalid_TSS);
	set_trap_gate(11,1,segment_not_present);
	set_trap_gate(12,1,stack_segment_fault);
	set_trap_gate(13,1,general_protection);
	set_trap_gate(14,1,page_fault);
	//15 Intel reserved. Do not use.
	set_trap_gate(16,1,x87_FPU_error);
	set_trap_gate(17,1,alignment_check);
	set_trap_gate(18,1,machine_check);
	set_trap_gate(19,1,SIMD_exception);
	set_trap_gate(20,1,virtualization_exception);

	//set_system_gate(SYSTEM_CALL_VECTOR,7,system_call);

}

sys_vector_init 方法里面的所有异常的IST均设置为1,其代表IST1,值为0xffff800000007c00,在发生异常时,会系统会自动将rsp指针设置为 0xffff800000007c00 用于保存寄存器的值!

思考,为什么执行 i = 1/0 时,系统会找到序号为0的中断,而不产生其他中断序号?

异常事件和中断请求的 检测 都是在某一条指令执行过程中进行的,显然由硬件完成;

在 CPU 根据 CS 和 EIP 取下条指令之前,会根据检测的结果判断是否进入中断响应阶段;

异常和中断的响应也都是在某一条指令执行过程中或执行结束时进行的,显然也由硬件完成;

内存示意图

在这里插入图片描述

setup_IDT :格式化中断描述符

在这里插入图片描述

ignore_int :中断处理程序

在这里插入图片描述

setup_TSS64 :格式化TSS描述符,并填入GDT表

在这里插入图片描述

参考资料

TSS Descriptor

在这里插入图片描述

汇编指令 lea

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值