poweroff /halt /reboot操作通常由用户空间的systemd或其他初始化系统通过sys_reboot()系统调用触发
sys_reboot() 在内核中定义,通常位于kernel/reboot.c文件中。当传递特定的magic值如 LINUX_REBOOT_CMD_POWER_OFF时,内核会执行关机并尝试触发硬件层面的电源关闭
/*
* Reboot system call: for obvious reasons only root may call it,
* and even root needs to set up some magic numbers in the registers
* so that some mistake won't make this reboot the whole machine.
* You can also set the meaning of the ctrl-alt-del-key here.
*
* reboot doesn't sync: do that yourself before calling this.
*/
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
struct pid_namespace *pid_ns = task_active_pid_ns(current);
char buffer[256];
int ret = 0;
/* We only trust the superuser with rebooting the system. */
if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
return -EPERM;
/* For safety, we require "magic" arguments. */
if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 &&
magic2 != LINUX_REBOOT_MAGIC2A &&
magic2 != LINUX_REBOOT_MAGIC2B &&
magic2 != LINUX_REBOOT_MAGIC2C))
return -EINVAL;
/*
* If pid namespaces are enabled and the current task is in a child
* pid_namespace, the command is handled by reboot_pid_ns() which will
* call do_exit().
*/
ret = reboot_pid_ns(pid_ns, cmd);
if (ret)
return ret;
/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;
mutex_lock(&system_transition_mutex);
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
case LINUX_REBOOT_CMD_CAD_ON:
C_A_D = 1;
break;
case LINUX_REBOOT_CMD_CAD_OFF:
C_A_D = 0;
break;
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
do_exit(0);
panic("cannot halt");
case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
do_exit(0);
break;
case LINUX_REBOOT_CMD_RESTART2:
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
#ifdef CONFIG_KEXEC_CORE
case LINUX_REBOOT_CMD_KEXEC:
ret = kernel_kexec();
break;
#endif
#ifdef CONFIG_HIBERNATION
case LINUX_REBOOT_CMD_SW_SUSPEND:
ret = hibernate();
break;
#endif
default:
ret = -EINVAL;
break;
}
mutex_unlock(&system_transition_mutex);
return ret;
}
poweroff流程,pm_power_off函数被各平台赋值到具体函数,比如通过操作PMIC来实现关机
void kernel_power_off(void)
{
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("Power down\n");
kmsg_dump(KMSG_DUMP_POWEROFF);
machine_power_off();
}
void machine_power_off(void)
{
local_irq_disable();
smp_send_stop();
if (pm_power_off)
pm_power_off();
}
void machine_power_off(void)
{
local_irq_disable();
smp_send_stop();
if (pm_power_off)
pm_power_off();
}
pm_power_off = pm802_sw_poweroff;
static int __pm802_sw_poweroff(void)
{
u32 val;
int ret = 0;
struct pm80x_chip *chip = pm80x_chip_g;
if (!chip) {
WARN(1, "__pm802_sw_poweroff: chip is NULL\n");
return -EINVAL;
}
/* clear fault wakeup */
ret = pmic_rw_reg(PM802_RTC_MISC5, (0x1 << 3), 0x0);
if (ret < 0) {
pr_err("%s, set PM803_RTC_MISC5 fail\n", __func__);
return ret;
}
if (CHIP_PM802S_ID_A0 == chip->chip_id
|| CHIP_PM802S_ID_A1 == chip->chip_id
|| CHIP_PM802S_ID_A2 == chip->chip_id)
val = 0x3;
else
val = 0x1;
/* discharge timer should be set greater than or equal to 1s */
ret = pmic_rw_reg(PM802_RTC_MISC2, 0xf, val);
if (ret < 0) {
pr_err("%s, set PM802_RTC_MISC2 fail\n", __func__);
return ret;
}
ret = pmic_rw_reg(PM802_RTC_MISC4, (0x1 << 1)+1, 0x0);
if (ret < 0) {
pr_err("%s, set PM802_RTC_MISC4 fail\n", __func__);
return ret;
}
ret = pmic_rw_reg(PM802_WAKEUP1, PM802_SW_PDOWN, PM802_SW_PDOWN);
if (ret < 0)
pr_err("%s, turn off power fail\n", __func__);
return ret;
}
reboot流程,arm_pm_restart函数可能被具体平台赋值到具体函数,比如操作WDT实现重启
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);
migrate_to_reboot_cpu();
syscore_shutdown();
if (!cmd)
pr_emerg("Restarting system\n");
else
pr_emerg("Restarting system with command '%s'\n", cmd);
kmsg_dump(KMSG_DUMP_RESTART);
machine_restart(cmd);
}
void machine_restart(char *cmd)
{
local_irq_disable();
smp_send_stop();
if (arm_pm_restart)
arm_pm_restart(reboot_mode, cmd);
else
do_kernel_restart(cmd);
/* Give a grace period for failure to restart of 1s */
mdelay(1000);
/* Whoops - the platform was unable to reboot. Tell the user! */
printk("Reboot failed -- System halted\n");
while (1);
}
arm_pm_restart = do_pxa_reset;
static void do_pxa_reset(enum reboot_mode mode, const char *cmd)
{
u32 backup;
int i;
if (cmd && (!strcmp(cmd, "recovery")
|| !strcmp(cmd, "bootloader") || !strcmp(cmd, "boot")
|| !strcmp(cmd, "product") || !strcmp(cmd, "prod")
|| !strcmp(cmd, "fastboot") || !strcmp(cmd, "fast"))) {
for (i = 0, backup = 0; i < 4; i++) {
backup <<= 8;
backup |= *(cmd + i);
}
do {
writel(backup, rtc_br0_reg);
} while (readl(rtc_br0_reg) != backup);
}
pxa_wdt_reset(wdt_base, mpmu_base);
/* Give a grace period for failure to restart of 1s */
mdelay(1000);
}