高通平臺關機流程

 

使用adb輸入以下命令,可以實現機器的重啓,進入到recovery,或者進入到bootloader裏

adb reboot

adb reboot recovery

adb reboot bootloader

 

抽絲剝繭,查看reboot命令的實現

 

system/core/reboot/reboot.c

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <cutils/properties.h>
#include <cutils/android_reboot.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int ret;
    size_t prop_len;
    char property_val[PROPERTY_VALUE_MAX];
    const char *cmd = "reboot";
    char *optarg = "";

    opterr = 0;
    do {
        int c;

        c = getopt(argc, argv, "p");

        if (c == -1) {
            break;
        }

        switch (c) {
        case 'p':
            cmd = "shutdown";
            break;
        case '?':
            fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    } while (1);

    if(argc > optind + 1) {
        fprintf(stderr, "%s: too many arguments\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (argc > optind)
        optarg = argv[optind];

    prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
    if (prop_len >= sizeof(property_val)) {
        fprintf(stderr, "reboot command too long: %s\n", optarg);
        exit(EXIT_FAILURE);
    }

    ret = property_set(ANDROID_RB_PROPERTY, property_val);
    if(ret < 0) {
        perror("reboot");
        exit(EXIT_FAILURE);
    }

    // Don't return early. Give the reboot command time to take effect
    // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
    while(1) { pause(); }

    fprintf(stderr, "Done\n");
    return 0;
}

其中

prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);

cmd爲shutdown(在加了-p選項時的情況)或者reboot

if (prop_len >= sizeof(property_val)) {

fprintf(stderr, "reboot command too long: %s\n", optarg);

exit(EXIT_FAILURE);

}

 

ret = property_set(ANDROID_RB_PROPERTY, property_val);

if(ret < 0) {

perror("reboot");

exit(EXIT_FAILURE);

}

解析完參數後,會設置一個屬性值ANDROID_RB_PROPERTY

 

ANDROID_RB_PROPERTY在哪裏定義呢?

查看reboot.c的頭文件,猜測是#include <cutils/android_reboot.h>這個頭文件裏

system\core\include\cutils\android_reboot.h 裏有定義

#define ANDROID_RB_PROPERTY "sys.powerctl"

 

在代碼裏搜索sys.powerctl

system\core\rootdir裏有搜索到

on property:sys.powerctl=*

powerctl ${sys.powerctl}

 

可以看到設置sys.power後這個觸發器會導致 powerctl 執行

 

powerctrl在哪裏呢?代碼裏搜索powerctrl

system\core\init\keywords.h裏可以搜索到

KEYWORD(powerctl, COMMAND, 1, do_powerctl)

 

KEYWORD宏的定義system\core\init\init_parser.h

#define KEYWORD(symbol, flags, nargs, func) \

[ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

 

展開後

KEYWORD(powerctl, COMMAND, 1, do_powerctl)

即爲

K_powerctl = {powerctl,do_powerctl, 2, 2}

 

lookup_keyword函數裏有

case 'p':

if (!strcmp(s, "owerctl")) return K_powerctl;

 

parse_line_action會執行

kw = lookup_keyword(args[0]);

...............

    n = kw_nargs(kw);
    if (nargs < n) {
        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
            n > 2 ? "arguments" : "argument");
        return;
    }
    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
    cmd->func = kw_func(kw);
    cmd->line = state->line;
    cmd->filename = state->filename;
    cmd->nargs = nargs;
    memcpy(cmd->args, args, sizeof(char*) * nargs);
    list_add_tail(&act->commands, &cmd->clist);

 

最終會導致do_powercrl執行

do_powercrl定義在system/core/init/builtins.c

do_powerctl

........................

if (strncmp(command, "shutdown", 8) == 0) {

cmd = ANDROID_RB_POWEROFF;

len = 8;

} else if (strncmp(command, "reboot", 6) == 0) {

cmd = ANDROID_RB_RESTART2;

len = 6;

} else {

ERROR("powerctl: unrecognized command '%s'\n", command);

return -EINVAL;

}

.....................

android_reboot(cmd, 0, reboot_target)

會調用android_reboot

 

android_reboot定義在system\core\libcutils\android_reboot.c

int android_reboot(int cmd, int flags UNUSED, char *arg)

{

int ret;



sync();

remount_ro();



switch (cmd) {

case ANDROID_RB_RESTART:

ret = reboot(RB_AUTOBOOT);

break;



case ANDROID_RB_POWEROFF:

ret = reboot(RB_POWER_OFF);

break;



case ANDROID_RB_RESTART2:

ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

LINUX_REBOOT_CMD_RESTART2, arg);

break;



default:

ret = -1;

}



return ret;

}

最終會調用reboot或者syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

LINUX_REBOOT_CMD_RESTART2, arg);

 

這兩個最終都會調用到__reboot

對於32bit在目錄bionic\libc\arch-arm\syscalls\__reboot.S

/* Generated by gensyscalls.py. Do not edit. */

#include <private/bionic_asm.h>

ENTRY(__reboot)
    mov     ip, r7
    ldr     r7, =__NR_reboot
    swi     #0
    mov     r7, ip
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno_internal
END(__reboot)

對於64bit在目錄bionic\libc\arch-arm64\syscalls\__reboot.S

/* Generated by gensyscalls.py. Do not edit. */



#include <private/bionic_asm.h>



ENTRY(__reboot)

mov x8, __NR_reboot

svc #0



cmn x0, #(MAX_ERRNO + 1)

cneg x0, x0, hi

b.hi __set_errno_internal



ret

END(__reboot)

.hidden __reboot

無論32位還是64位最終都會調用到kernel裏的reboot系統調用

 

kernel/reboot.c 

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,

void __user *, arg)

.........

switch (cmd) {

.................

case LINUX_REBOOT_CMD_RESTART:

kernel_restart(NULL);

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;

.....................

調用kernel_restart(buffer);

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);

}

 

調用machine_restart

machine_restart
...........

      if (arm_pm_restart)

               arm_pm_restart(reboot_mode, cmd);

如果arm_pm_restart不空的話調用arm_pm_restart

 

 

drivers\power\reset\msm-poweroff.c

 

在msm_restart_probe裏設置了arm_pm_restart和pm_power_off

pm_power_off = do_msm_poweroff;

arm_pm_restart = do_msm_restart;

 

static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)

    msm_restart_prepare(cmd);

    scm_disable_sdi();

    halt_spmi_pmic_arbiter();

    deassert_ps_hold();

    mdelay(10000);

 

調用msm_restart_prepare

static void msm_restart_prepare(const char *cmd)

if (cmd != NULL) {

if (!strncmp(cmd, "bootloader", 10)) {

qpnp_pon_set_restart_reason(

PON_RESTART_REASON_BOOTLOADER);

__raw_writel(0x77665500, restart_reason);

} else if (!strncmp(cmd, "recovery", 8)) {

qpnp_pon_set_restart_reason(

PON_RESTART_REASON_RECOVERY);

__raw_writel(0x77665502, restart_reason);

} else if (!strcmp(cmd, "rtc")) {

qpnp_pon_set_restart_reason(

PON_RESTART_REASON_RTC);

__raw_writel(0x77665503, restart_reason);

} else if (!strcmp(cmd, "dm-verity device corrupted")) {

qpnp_pon_set_restart_reason(

PON_RESTART_REASON_DMVERITY_CORRUPTED);

__raw_writel(0x77665508, restart_reason);

} else if (!strcmp(cmd, "dm-verity enforcing")) {

qpnp_pon_set_restart_reason(

PON_RESTART_REASON_DMVERITY_ENFORCE);

__raw_writel(0x77665509, restart_reason);

} else if (!strcmp(cmd, "keys clear")) {

qpnp_pon_set_restart_reason(

PON_RESTART_REASON_KEYS_CLEAR);

__raw_writel(0x7766550a, restart_reason);

} else if (!strncmp(cmd, "oem-", 4)) {

unsigned long code;

int ret;

ret = kstrtoul(cmd + 4, 16, &code);

if (!ret)

__raw_writel(0x6f656d00 | (code & 0xff),

restart_reason);

} else if (!strncmp(cmd, "edl", 3)) {

enable_emergency_dload_mode();

} else {

__raw_writel(0x77665501, restart_reason);

}

}

flush_cache_all();

 

drivers\platform\msm\qpnp-power-on.c

int qpnp_pon_set_restart_reason(enum pon_restart_reason reason)

{

int rc = 0;

struct qpnp_pon *pon = sys_reset_dev;



if (!pon)

return 0;



if (!pon->store_hard_reset_reason)

return 0;



rc = qpnp_pon_masked_write(pon, QPNP_PON_SOFT_RB_SPARE(pon),

PON_MASK(7, 2), (reason << 2));

if (rc)

dev_err(&pon->spmi->dev,

"Unable to write to addr=%x, rc(%d)\n",

QPNP_PON_SOFT_RB_SPARE(pon), rc);

return rc;

}

其中#define QPNP_PON_SOFT_RB_SPARE(pon) ((pon)->base + 0x8F)

 

在msm_restart_probe裏設置了restart_reason

msm_restart_probe

of_find_compatible_node(NULL, NULL,

"qcom,msm-imem-restart_reason");

restart_reason = of_iomap(np, 0);

 

在dtsi裏有定義(比如msm8917.dtsi)

qcom,msm-imem@8600000 {

             compatible = "qcom,msm-imem";

             reg = <0x08600000 0x1000>; /* Address and size of IMEM */

              ranges = <0x0 0x08600000 0x1000>;

              #address-cells = <1>;

              #size-cells = <1>;

              ..............

              restart_reason@65c {

              compatible = "qcom,msm-imem-restart_reason";

              reg = <0x65c 4>;

              };

.              ..........

可知在msm8917裏restart_reason保存在0x860065c地址裏

PS:這段地址在msm8917裏落在IMEM的區間裏

 

寫在這裏的值,restart_reason在lk裏會進行讀取,來判斷restart的原因

app/aboot/aboot.c

#if USE_PON_REBOOT_REG

reboot_mode = check_hard_reboot_mode();

#else

reboot_mode = check_reboot_mode();

#endif

if (reboot_mode == RECOVERY_MODE)

{

boot_into_recovery = 1;

}

else if(reboot_mode == FASTBOOT_MODE)

{ if(target_build_variant_user())

boot_into_fastboot = 0;

else

boot_into_fastboot = true;

}

else if(reboot_mode == ALARM_BOOT)

{

boot_reason_alarm = true;

}

#if VERIFIED_BOOT

#if !VBOOT_MOTA

else if (reboot_mode == DM_VERITY_ENFORCING)

{

device.verity_mode = 1;

write_device_info(&device);

}

#if ENABLE_VB_ATTEST

else if (reboot_mode == DM_VERITY_EIO)

#else

else if (reboot_mode == DM_VERITY_LOGGING)

#endif

{

device.verity_mode = 0;

write_device_info(&device);

}

else if (reboot_mode == DM_VERITY_KEYSCLEAR)

{

if(send_delete_keys_to_tz())

ASSERT(0);

}

 

如果是用pmic的寄存器保存的話,使用check_hard_reboot_mode

unsigned check_hard_reboot_mode(void)

{

uint8_t hard_restart_reason = 0;

uint8_t value = 0;



/* Read reboot reason and scrub it

* Bit-5, bit-6 and bit-7 of SOFT_RB_SPARE for hard reset reason

*/

value = pm8x41_reg_read(PON_SOFT_RB_SPARE);

hard_restart_reason = value >> 5;

pm8x41_reg_write(PON_SOFT_RB_SPARE, value & 0x1f);



return hard_restart_reason;

}

其中PON_SOFT_RB_SPARE定義爲

#define PON_SOFT_RB_SPARE 0x88F

 

如果是在IMEM裏保存的話,使用check_reboot_mode();

unsigned check_reboot_mode(void)

{

uint32_t restart_reason = 0;



/* Read reboot reason and scrub it */

restart_reason = readl(RESTART_REASON_ADDR);

writel(0x00, RESTART_REASON_ADDR);



return restart_reason;

}

對於msm8917定義

#define RESTART_REASON_ADDR (MSM_SHARED_IMEM_BASE + 0x65C)

#define MSM_SHARED_IMEM_BASE 0x08600000

所以RESTART_REASON_ADDR 的值爲0x0860065C,與kernel裏是一致的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章