zoukankan      html  css  js  c++  java
  • Linux内核源码分析 -- 中断描述符表初始化

    今天看了 CSAPP (深入理解计算机系统)的异常控制流篇,直接来简单分析一下 Linux 内核的 中断描述符表 的初始化

    源码版本:Linux kernel 5.6

    内核启动时会在 start_kernel 里面调用 trap_init 去初始化中断向量表

    以此为入口,开始分析

    start_kernel

    /init/main.c

    	trap_init();
    

    trap_init

    /arch/x86/kernel/traps.c

    void __init trap_init(void)
    {
        .......
        
    	idt_setup_traps(); // 使用默认 trap(陷阱) 初始化 中断描述符表
    
    	/*
    	 * Set the IDT descriptor to a fixed read-only location, so that the
    	 * "sidt" instruction will not leak the location of the kernel, and
    	 * to defend the IDT against arbitrary memory write vulnerabilities.
    	 * It will be reloaded in cpu_init() */
    	cea_set_pte(CPU_ENTRY_AREA_RO_IDT_VADDR, __pa_symbol(idt_table),
    		    PAGE_KERNEL_RO);
    	idt_descr.address = CPU_ENTRY_AREA_RO_IDT;
    
    	/*
    	 * Should be a barrier for any external CPU state:
    	 */
    	cpu_init();
    
    	idt_setup_ist_traps();
        ...........
    }
    

    idt_setup_traps

    核心就在这里了

    struct idt_data { // 中断描述符表 idt 的每一个表项都由一个 idt_data 结构去描述
    	unsigned int	vector;  // 中断向量
    	unsigned int	segment; // 中断代码处于的代码段
    	struct idt_bits	bits; // 应该是标志位
    	const void	*addr; // 指向中断处理函数
    };
    
    #define G(_vector, _addr, _ist, _type, _dpl, _segment)	
    	{						
    		.vector		= _vector,		
    		.bits.ist	= _ist,			
    		.bits.type	= _type,		
    		.bits.dpl	= _dpl,			
    		.bits.p		= 1,			
    		.addr		= _addr,		
    		.segment	= _segment,		
    	}
    
    // __KERNEL_CS 表示内核代码段
    
    /* Interrupt gate */
    // 中断陷阱
    #define INTG(_vector, _addr)				
    	G(_vector, _addr, DEFAULT_STACK, GATE_INTERRUPT, DPL0, __KERNEL_CS)
    
    /* System interrupt gate */
    // 系统中断陷阱
    #define SYSG(_vector, _addr)				
    	G(_vector, _addr, DEFAULT_STACK, GATE_INTERRUPT, DPL3, __KERNEL_CS)
    
    /*
     * Interrupt gate with interrupt stack. The _ist index is the index in
     * the tss.ist[] array, but for the descriptor it needs to start at 1.
     */
    // 使用中断栈(可以去看看 struct cpu_entry_area)
    #define ISTG(_vector, _addr, _ist)			
    	G(_vector, _addr, _ist + 1, GATE_INTERRUPT, DPL0, __KERNEL_CS)
    
    /* Task gate */
    // 任务陷阱
    #define TSKG(_vector, _gdt)				
    	G(_vector, NULL, DEFAULT_STACK, GATE_TASK, DPL0, _gdt << 3)
    
    /* Interrupts/Exceptions */
    // 中断向量,其实是一个 枚举,0~32 号由 intel 规定
    enum {
        // 除 0 异常
    	X86_TRAP_DE = 0,	/*  0, Divide-by-zero */ 
        // 发生调试异常
    	X86_TRAP_DB,		/*  1, Debug */
    	X86_TRAP_NMI,		/*  2, Non-maskable Interrupt */
        // 断点(int3)
    	X86_TRAP_BP,		/*  3, Breakpoint */
        // 算术溢出
    	X86_TRAP_OF,		/*  4, Overflow */
        // 越界访问
    	X86_TRAP_BR,		/*  5, Bound Range Exceeded */
        // cpu 执行到无效操作码
    	X86_TRAP_UD,		/*  6, Invalid Opcode */
        // 在 没有 浮点运算单元 时(或者设置了 cr0 的标志位标识  FPU/MMX/SSE 不可用)进行浮点运算
    	X86_TRAP_NM,		/*  7, Device Not Available */
        // 双重异常(比如在发生异常后调用异常处理函数时又触发了异常)
    	X86_TRAP_DF,		/*  8, Double Fault */
    	X86_TRAP_OLD_MF,	/*  9, Coprocessor Segment Overrun */
        // TSS 无效
    	X86_TRAP_TS,		/* 10, Invalid TSS */
    	X86_TRAP_NP,		/* 11, Segment Not Present */
    	X86_TRAP_SS,		/* 12, Stack Segment Fault */
    	X86_TRAP_GP,		/* 13, General Protection Fault */
        // 缺页
    	X86_TRAP_PF,		/* 14, Page Fault */
    	X86_TRAP_SPURIOUS,	/* 15, Spurious Interrupt */
    	X86_TRAP_MF,		/* 16, x87 Floating-Point Exception */
    	X86_TRAP_AC,		/* 17, Alignment Check */
    	X86_TRAP_MC,		/* 18, Machine Check */
    	X86_TRAP_XF,		/* 19, SIMD Floating-Point Exception */
    	X86_TRAP_IRET = 32,	/* 32, IRET Exception */
    };
    
    /*
     * The default IDT entries which are set up in trap_init() before
     * cpu_init() is invoked. Interrupt stacks cannot be used at that point and
     * the traps which use them are reinitialized with IST after cpu_init() has
     * set up TSS.
     */
    static const __initconst struct idt_data def_idts[] = {
        // 每一个 INTG 就是设置一个 idt_data 表项(第一个参数是中断向量)
    	INTG(X86_TRAP_DE,		divide_error),
    	INTG(X86_TRAP_NMI,		nmi),
    	INTG(X86_TRAP_BR,		bounds),
    	INTG(X86_TRAP_UD,		invalid_op),
    	INTG(X86_TRAP_NM,		device_not_available),
    	INTG(X86_TRAP_OLD_MF,		coprocessor_segment_overrun),
    	INTG(X86_TRAP_TS,		invalid_TSS),
    	INTG(X86_TRAP_NP,		segment_not_present),
    	INTG(X86_TRAP_SS,		stack_segment),
    	INTG(X86_TRAP_GP,		general_protection),
    	INTG(X86_TRAP_SPURIOUS,		spurious_interrupt_bug),
    	INTG(X86_TRAP_MF,		coprocessor_error),
    	INTG(X86_TRAP_AC,		alignment_check),
    	INTG(X86_TRAP_XF,		simd_coprocessor_error),
    
    #ifdef CONFIG_X86_32
    	TSKG(X86_TRAP_DF,		GDT_ENTRY_DOUBLEFAULT_TSS),
    #else
    	INTG(X86_TRAP_DF,		double_fault),
    #endif
    	INTG(X86_TRAP_DB,		debug),
    
    #ifdef CONFIG_X86_MCE
    	INTG(X86_TRAP_MC,		&machine_check),
    #endif
    
    	SYSG(X86_TRAP_OF,		overflow),
    #if defined(CONFIG_IA32_EMULATION)
    	SYSG(IA32_SYSCALL_VECTOR,	entry_INT80_compat),
    #elif defined(CONFIG_X86_32)
    	SYSG(IA32_SYSCALL_VECTOR,	entry_INT80_32), // x86_32 的系统调用 int 0x80
    #endif
    };
    
    /**
     * idt_setup_traps - Initialize the idt table with default traps
     */
    void __init idt_setup_traps(void)
    {
        // 使用预设的 def_idts 来设置 中断描述符表 idt_table
    	idt_setup_from_table(idt_table, def_idts, ARRAY_SIZE(def_idts), true);
    }
    
    Name Vector nr. Type Mnemonic Error code?
    Divide-by-zero Error 0 (0x0) Fault #DE No
    Debug 1 (0x1) Fault/Trap #DB No
    Non-maskable Interrupt 2 (0x2) Interrupt - No
    Breakpoint 3 (0x3) Trap #BP No
    Overflow 4 (0x4) Trap #OF No
    Bound Range Exceeded 5 (0x5) Fault #BR No
    Invalid Opcode 6 (0x6) Fault #UD No
    Device Not Available 7 (0x7) Fault #NM No
    Double Fault 8 (0x8) Abort #DF Yes (Zero)
    Coprocessor Segment Overrun 9 (0x9) Fault - No
    Invalid TSS 10 (0xA) Fault #TS Yes
    Segment Not Present 11 (0xB) Fault #NP Yes
    Stack-Segment Fault 12 (0xC) Fault #SS Yes
    General Protection Fault 13 (0xD) Fault #GP Yes
    Page Fault 14 (0xE) Fault #PF Yes
    Reserved 15 (0xF) - - No
    x87 Floating-Point Exception 16 (0x10) Fault #MF No
    Alignment Check 17 (0x11) Fault #AC Yes
    Machine Check 18 (0x12) Abort #MC No
    SIMD Floating-Point Exception 19 (0x13) Fault #XM/#XF No
    Virtualization Exception 20 (0x14) Fault #VE No
    Reserved 21-29 (0x15-0x1D) - - No
    Security Exception 30 (0x1E) - #SX Yes
    Reserved 31 (0x1F) - - No
    Triple Fault - - - No
    FPU Error Interrupt IRQ 13 Interrupt #FERR No
    static inline void idt_init_desc(gate_desc *gate, const struct idt_data *d)
    {
    	unsigned long addr = (unsigned long) d->addr;
    
    	gate->offset_low	= (u16) addr;
    	gate->segment		= (u16) d->segment;
    	gate->bits		= d->bits;
    	gate->offset_middle	= (u16) (addr >> 16);
    #ifdef CONFIG_X86_64
    	gate->offset_high	= (u32) (addr >> 32);
    	gate->reserved		= 0;
    #endif
    }
    
    #define write_idt_entry(dt, entry, g)		native_write_idt_entry(dt, entry, g)
    
    static inline void native_write_idt_entry(gate_desc *idt, int entry, const gate_desc *gate)
    {
    	memcpy(&idt[entry], gate, sizeof(*gate));
    }
    
    static void
    idt_setup_from_table(gate_desc *idt, const struct idt_data *t, int size, bool sys)
    {
    	gate_desc desc;
        
        // 循环拷贝每个表项
    	for (; size > 0; t++, size--) {
    		idt_init_desc(&desc, t);  // 把每个表项的数据拷贝到 desc
    		write_idt_entry(idt, t->vector, &desc); // 直接使用 memcpy 拷贝,t->vector 就是 对应 desc 的中断向量,拷贝到对应的 idt 的中断向量是 t->vector 的表项
    		if (sys)
    			set_bit(t->vector, system_vectors);
    	}
    }
    

    总结

    其实开机时初始化中断描述符表(idt_table)就是从默认的描述符表(def_idts)拷贝需要的表项

    参考

    osdev Exceptions

  • 相关阅读:
    ffmpeg文档22-混合器(复用器)
    ffmpeg文档21-分离器
    ffmpeg文档20-格式选项
    ffmpeg文档19-比特流滤镜
    ffmpeg文档18-字幕编码器
    ffmpeg文档17-视频编码器
    ffmpeg文档16-音频编码器
    python 学习笔记,selenium 自动化测试 更新开始
    python 遇到 'unicode' object is not callable 错误
    字符串函数汇总
  • 原文地址:https://www.cnblogs.com/crybaby/p/14316147.html
Copyright © 2011-2022 走看看