实模式代码go_to_proteced_mode函数

3.4 实模式代码go_to_proteced_mode函数

main函数走到go_to_protected_mode,就是将告别实模式环境,进入保护模式了。保护模式(Protected Mode,或有时简写为 pmode)是一种 80286 系列和之后的 x86 兼容 CPU 操作模式。保护模式有一些新的特色,设计用来增强多功能和系统稳定度,像是内存保护,分页系统,以及硬件支援的虚拟内存。大部分的现今x86操作系统都在保护模式下运行,包含LinuxFreeBSD、以及微软 Windows 2.0 和之后版本。

 

注意,这里还是在实模式下,只不过是进入保护模式相关的代码。有关保护模式编程的详细介绍,请参考博客“保护模式编程”

 

看到main函数中的最后一行代码,注释上写的是激活保护模式,调用

go_to_protected_mode()函数,在linux/arch/x86/boot/pm.c中:

 

101/*

 102 * Actual invocation sequence

 103 */

 104void go_to_protected_mode(void)

 105{

 106        /* Hook before leaving real mode, also disables interrupts */

 107        realmode_switch_hook();

 108

 109        /* Enable the A20 gate */

 110        if (enable_a20()) {

 111                puts("A20 gate not responding, unable to boot.../n");

 112                die();

 113        }

 114

 115        /* Reset coprocessor (IGNNE#) */

 116        reset_coprocessor();

 117

 118        /* Mask all interrupts in the PIC */

 119        mask_all_interrupts();

 120

 121        /* Actual transition to protected mode... */

 122        setup_idt();

 123        setup_gdt();

 124        protected_mode_jump(boot_params.hdr.code32_start,

 125                            (u32)&boot_params + (ds() << 4));

 126}

 

3.4.1 禁止可屏蔽和不可屏蔽中断

107行,调用realmode_switch_hook()函数。这个函数位于同一个文件中,内容如下:

 

  18/*

  19 * Invoke the realmode switch hook if present; otherwise

  20 * disable all interrupts.

  21 */

  22static void realmode_switch_hook(void)

  23{

  24        if (boot_params.hdr.realmode_swtch) {

  25                asm volatile("lcallw *%0"

  26                             : : "m" (boot_params.hdr.realmode_swtch)

  27                             : "eax", "ebx", "ecx", "edx");

  28        } else {

  29                asm volatile("cli");

  30                outb(0x80, 0x70); /* Disable NMI */

  31                io_delay();

  32        }

  33}

 

注释中写得很清楚了,这个函数的目的在于如果hdr参数的realmode_swtch字段被设置,则直接跳到对应的子程序中,一些bootloader程序用该子程序来抓住实模式下最后的执行代码的机会。否则关中断。我们看到header.S的第122行,realmode_swtch字段为0,即没有设置,所以函数直接到29行,执行cli指令关中断。有关x86相关的指令请参考博客“Intel 8086/8088 指令系统”

 

30行是全内核代码中第一次出现跟I/O低级函数,在博文“Linux I/O体系结构”有这些低级函数的介绍,这里讲这些函数的原型:

 

static inline void outb(u8 v, u16 port)

{

       asm volatile("outb %0,%1" : : "a" (v), "dN" (port));

}

 

其实这些低级I/O函数都在arch/x86/boot/Boot.h中定义,以后全代码的所有对这些函数的扩展,最后也都会走到这里,我们就只看outb的源代码,其他的类似。outb()函数就是对outb指令进行封装,将参数v的内容传输到参数port对应的端口中。那么30行的outb(0x80, 0x70)意思是将0x70端口对应接口内部寄存器的值置为0x80

 

注意,Linux启动程序运行到此,还没有建立起内核I/O子系统,所有这里的I/O端口跟我们平常在/proc/ioports文件中查的I/O端口是不一样的。系统上电以后,BIOS程序把0x70号端口置为的不可屏蔽中断寄存器,NMI

 

NMI (Non Maskable Interrupt)——不可屏蔽中断(CPU不能屏蔽),无论状态寄存器中 IF 位的状态如何,CPU收到有效的NMI必须进行响应。NMI中断类型号固定为2,它在被响应时无中断响应周期。不可屏蔽中断通常用于故障处理(如:协处理器运算出错,存储器校验出错,I/O通道校验出错等)。这里执行30行代码就是紧接着把这个NMI也给禁止掉。随后31io_delay()函数,是对outb()函数的扩展

 

static inline void io_delay(void)

{

       const u16 DELAY_PORT = 0x80;

       asm volatile("outb %%al,%0" : : "dN" (DELAY_PORT));

}

 

是把0x80端口对应接口内部寄存器的值也置为0x80。我们知道,0x80其实是个空端口,所以这个io_delay函数的意义仅仅是对I/O端口执行一个空操作,以达到对CPUI/O访问周期的延时工作。

 

你可能感兴趣的:(实模式代码go_to_proteced_mode函数)