从函数实现上看,使用死循环,根据不同的重启类型做转变:
BOOT_ACPI->BOOT_KBD->BOOT_ACPI->BOOT_EFI->BOOT_BIOS->BOOT_CF9_SAFE->BOOT_TRIPLE...首先是ACPI复位,调用acpi_reboot函数(driver/acpi/reboot.c),在该函数中通过写PCI寄存器方式复位。这涉及ACPI相关知识,笔者未做深入研究。笔者在一台安装BIOS的机器上测试发现是使用ACPI复位的。
如果ACPI被禁止(即无法复位成功),则使用keyboad复位,主要是写0xfe到端口0x64,关于它的作用,直接引述网络资料:
“0x64端口是i8042键盘控制器的控制端口,0xfe命令字的意思是将P32-P21三个针脚拉为低电平,持续6usec。这段代码的实际效果就相当于你按下机箱上的 RESET键。”
值得一提的是BOOT_CF9_SAFE分支,当port_cf9_safe为true时,会通过写0xcf9寄存器的方式复位。
其余的暂略过不提。
(注:上面分析顺序根据笔者测试结果来描述,理论上并不严谨)
下面看看系统重启过程的调试信息(注:此时机器无法使用reset重启):
- * Will now restart
- [ 716.203870] reboot: Restarting system
- [ 716.208485] reboot: reboot_type: 97(a)
- [ 716.212675] reboot: 11111reboot_type: 97(a)
- [ 716.217350] acpi_reboot() acpi is disable...
- [ 716.222121] reboot: 11111reboot_type: 107(k)
- [ 716.226900] reboot: native_machine_emergency_restart() in KBD reboot...
- [ 718.637262] reboot: 11111reboot_type: 97(a)
- [ 718.641936] acpi_reboot() acpi is disable...
- [ 718.646707] reboot: 11111reboot_type: 107(k)
- [ 718.651484] reboot: native_machine_emergency_restart() in KBD reboot...
- [ 721.061846] reboot: 11111reboot_type: 101(e)
- [ 721.066616] reboot: 11111reboot_type: 98(b)
四、u-boot环境reset作为扩展,本文顺便也看看u-boot源码下重启的流程。reset命令位于cmd/boot.c文件:
- U_BOOT_CMD(
- reset, 1, 0, do_reset,
- "Perform RESET of the CPU",
- ""
- );
do_reset函数实现在arch/x86/cpu/cpu.c文件:
- int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
- {
- printf("resetting ...\n");
- /* wait 50 ms */
- udelay(50000);
- disable_interrupts();
- reset_cpu(0);
- /*NOTREACHED*/
- return 0;
- }
- __weak void reset_cpu(ulong addr)
- {
- /* Do a hard reset through the chipset's reset control register */
- outb(SYS_RST | RST_CPU, IO_PORT_RESET);
- for (;;)
- cpu_hlt();
- }
- void x86_full_reset(void)
- {
- outb(FULL_RST | SYS_RST | RST_CPU, IO_PORT_RESET);
- }
上面列出2个复位的函数:reset_cpu、x86_full_reset。它们只有细微区别,但都是往IO_PORT_RESET这个端口上写数值。接着看看这些宏定义是什么。它们的定义位于文件arch/x86/include/asm/processor.h:
- /*
- * This register is documented in (for example) the Intel Atom Processor E3800
- * Product Family Datasheet in "PCU - Power Management Controller (PMC)".
- *
- * RST_CNT: Reset Control Register (RST_CNT) Offset cf9.
- *
- * The naming follows Intel's naming.
- */
- #define IO_PORT_RESET 0xcf9
- enum {
- SYS_RST = 1 << 1, /* 0 for soft reset, 1 for hard reset */
- RST_CPU = 1 << 2, /* initiate reset */
- FULL_RST = 1 << 3, /* full power cycle */
- };
从注释上看,0xcf9是Intel Atom的E3800系列SOC的RST_CNT寄存器(在内核中亦出现此寄存器)。复位类型有3种:SYS_RST、RST_CPU、FULL_RST。
本着打破沙锅问到底的钻研精神,找到E3800的datasheet,查看关于复位的说明章节(7.4章节),截图如下:
三种复位类型组合即为0x0e,亦即表格第一行所述操作。而RST_CNT寄存器的说明位于30.7章节,其寄存器地址正是CF9h。
这样,终于和u-boot源码对应起来了,对源码的复位函数也能解释得通了。网络上有资料表明可以在ICH手册中找到CF9寄存器,有兴趣的可以自行查阅。
五、所遇问题及解决细心的读者可能会发现在native_machine_emergency_restart函数中,笔者在BOOT_KBD分支中写0xcf9寄存器,而不是使用BOOT_CF9_SAFE分支。因为笔者在使用u-boot引导启动(非BIOS)的x86机器上遇到无法复位的问题,当执行到BOOT_BIOS时机器死了,但没有复位——应该是板子上并没有BIOS,无法执行对应的指令吧,这方面没有深入研究。选择在BOOT_KBD中实现是因为同一份内核镜像文件需要在有BIOS的机器和有u-boot的机器上运行。——无论是设计还是维护角度,笔者希望能尽量兼容。
六、小结要使系统重启,直接调用reboot即可,但由于系统重启还需要进行其它清理操作,一般不建议直接使用。在内核空间中实现的重启机制,首先使用ACPI,其次使用keyboad,再次使用BIOS,最后使用CF9。从u-boot源码分析看到,x86机器的重启是通过将0x0e写到0xcf9寄存器来实现的,在内核内核也有此方式,但差不多是在最后才执行,优先级较低。另外关于ACPI的研究,待日后有空闲再议。 |