reboot recovery的执行过程及syscall系统调用分析

 

要进入recovery模式,可以在java层调用函数,也可以执行shell命令“reboot recovery”进入。

这里分析“reboot recovery”流程。

(由于水平有限,笔记过程可能存在不妥之处)

system/core/reboot/reboot.c

int main(int argc, char *argv[])

{

// .......

ret = property_set(ANDROID_RB_PROPERTY, property_val);

}

#define ANDROID_RB_PROPERTY "sys.powerctl"

也就是设置了sys.powerctl属性为“recovery”,

此属性的改变,触发init rc的调用:

system/core/rootdir/init.rc

on property:sys.powerctl=*

powerctl ${sys.powerctl}

按init的调用过程,即是在init里调用了do_powerctl 函数。

 

system/core/init/builtins.cpp

int do_powerctl(int nargs, char **args)

{

//.....

    return android_reboot(cmd, 0, reboot_target);

}

system/core/libcutils/android_reboot.c

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

{ //......

case ANDROID_RB_RESTART2:

            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

                           LINUX_REBOOT_CMD_RESTART2, arg);

}

这里,使用syscall执行了一个系统调用,

bionic/libc/include/unistd.h里的定义:long syscall(long number, ...);

参数number为系统调用号。

函数的实现在哪里呢?是在汇编里实现的!

bionic/libc/arch-arm/bionic/syscall.S

#include <private/bionic_asm.h>

ENTRY(syscall)  //定义了汇编函数syscall

    mov     ip, sp

    stmfd   sp!, {r4, r5, r6, r7}

    //......

    mov     r7, r0  /*参数1为系统调用号,写到r7*/

    mov     r0, r1  

    mov     r1, r2

    mov     r2, r3

    ldmfd   ip, {r3, r4, r5, r6}

    swi     #0  /*产生软中段,进入内核模式*/

//.......

END(syscall)

其中ENTRY和END宏,定义于bionic/libc/private/bionic_asm.h

比如ENTRY宏,用于导出汇编函数等:

#define ENTRY(f) \

    .text; \

    .globl f; \  //导出汇编函数f

    .align __bionic_asm_align; \

    .type f, __bionic_asm_function_type; \

    f: \

    __bionic_asm_custom_entry(f); \

.cfi_startproc \

 

syscall.S里,syscall做了一系列参数处理后,通过swi产生软中断。

linux启动时,early_trap_init里使用__vectors_start和__vectors_end地址初始化了中断向量,kernel-3.18/arch/arm/kernel/entry-armv.S 里定义了中断向量处理:

__vectors_start:

W(b) vector_rst

W(b) vector_und

W(ldr) pc, __vectors_start + 0x1000  //这个是SWI中断处理的跳转

W(b) vector_pabt

W(b) vector_dabt

W(b) vector_addrexcptn

W(b) vector_irq

W(b) vector_fiq

 

产生swi软中断后,就跳到软中断处理:kernel-3.18/arch/arm/kernel/entry-common.S

/*============================================

 * SWI handler

 *-----------------------------------------------------------------------------

 */

.align 5

ENTRY(vector_swi)

//根据CONFIG_OABI_COMPATCONFIG_AEABI不同配置,删掉了很多东西

/*

 * Pure EABI user space always put syscall number into scno (r7).

 */ 这里提示scno的值等于r7的值,

        // kernel-3.18/arch/arm/kernel/entry-header.S :

//  scno .req r7 @ syscall number  //为寄存器r7定义一个别名scno

// .......

adr tbl, sys_call_table @ load syscall table pointer

       //  .......

ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine

ENDPROC(vector_swi)

 

上面,sys_call_table 函数表被加载到 tbl变量,然后通过 ldrcc pc xxx 指令跳转到相应的函数执行。

从注释上可以看到,是调用了 “sys_*”这样的函数的。

那么sys_call_table 这个函数表是怎样生成的?

kernel-3.18/arch/arm/include/asm/unistd.h

#define __NR_syscalls  (388)

kernel-3.18/arch/arm/kernel/entry-common.S :

.equ NR_syscalls,0

#define CALL(x) .equ NR_syscalls,NR_syscalls+1  //这个CALL宏用于计算的calls.S里定义了多少系统调用

#include "calls.S"

/*

 * Ensure that the system call table is equal to __NR_syscalls,

 * which is the value the rest of the system sees

 */

.ifne NR_syscalls - __NR_syscalls

.error "__NR_syscalls is not equal to the size of the syscall table" //提示系统调用数目不符合要求

.endif

#undef CALL

#define CALL(x) .long x  //这个CALL宏用于定义一系列long变量,并分配空间

.type sys_call_table, #object

ENTRY(sys_call_table)  //导出sys_call_table符号

#include "calls.S" 

 

kernel-3.18/arch/arm/kernel/calls.S 的内容是:

/* 0 */ CALL(sys_restart_syscall)

CALL(sys_exit)

CALL(sys_fork)

CALL(sys_read)

CALL(sys_write)

//.....

/* 85 */ CALL(sys_readlink)

CALL(sys_uselib)

CALL(sys_swapon)

CALL(sys_reboot)  //88号系统调用

//..................

/* 380 */ CALL(sys_sched_setattr)

CALL(sys_sched_getattr)

CALL(sys_renameat2)

CALL(sys_seccomp)

CALL(sys_getrandom)

/* 385 */ CALL(sys_memfd_create)

CALL(sys_bpf)

#ifndef syscalls_counted

.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls

#define syscalls_counted

#endif

.rept syscalls_padding

CALL(sys_ni_syscall)

.endr

 

因此,通过宏 “#define CALL(x) .long x”的定义,sys_call_table 函数表就生成了。

ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine  这句就是根据scno (即r7)的值计算tbl函数表的偏移,然后调用相应的 “sys_xxx”函数了。

也就是说,

上面执行的syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

                           LINUX_REBOOT_CMD_RESTART2, arg);

kernel-3.18/arch/arm/include/uapi/asm/unistd.h 里

#if defined(__thumb__) || defined(__ARM_EABI__)  //生效

#define __NR_SYSCALL_BASE 0  

#else

#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE

#endif

 

#define __NR_reboot (__NR_SYSCALL_BASE+ 88) //

这就相当于: syscall (88,........),执行了88号系统调用。

call.S 里查找,88号对应 sys_reboot函数。

因此,经过 swi异常处理后,调用了 sys_reboot函数。

 

sys_reboot函数在哪里定义? 搜索了一下,没发现。

原来,是通过 SYSCALL_DEFINEx 宏定义的。

(相关可以参考:https://blog.csdn.net/hxmhyp/article/details/22699669 )

 

sys_reboot就是如下宏展开的生成的:SYSCALL_DEFINE4(reboot ...) 就得到 sys_reboot函数。

kernel-3.18/kernel/reboot.c

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

void __user *, arg)

{

//.......

case LINUX_REBOOT_CMD_RESTART2: //syscall()的时候,cmd就是这个值

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;

}

因此,syscall()  -->>  kernel_restart ()

kernel-3.18/kernel/reboot.c

void kernel_restart(char *cmd)

{

//....

machine_restart(cmd);

}

接下来,本篇笔记目的不是这个,不再一一跟踪,可从执行reboot recovery时打印的调用栈了解执行过程:

[ 4505.396695] Backtrace:                                                                                                                                                         
[ 4505.397009] <0>-(0)[1:init][] (dump_backtrace) from [] (show_stack+0x18/0x1c)                                        [ 4505.398820] <0>-(0)[1:init][] (show_stack) from [] (dump_stack+0x88/0xc8)                                                  
[ 4505.399883] <0>-(0)[1:init][] (dump_stack) from [] (arch_reset+0x3c/0x118)                                                 
[ 4505.401661] <0>-(0)[1:init][] (arch_reset) from [] (mtk_arch_reset_handle+0x2c/0x40)                               
[ 4505.403549] <0>-(0)[1:init][] (mtk_arch_reset_handle) from [] (notifier_call_chain+0x6c/0x134)              
[ 4505.405283] <0>-(0)[1:init][] (notifier_call_chain) from [] (atomic_notifier_call_chain+0x3c/0x50)              [ 4505.407909] <0>-(0)[1:init][] (atomic_notifier_call_chain) from [] (do_kernel_restart+0x24/0x2c)             
[ 4505.409796] <0>-(0)[1:init][] (do_kernel_restart) from [] (machine_restart+0x90/0x94)                              
[ 4505.410989] <0>-(0)[1:init][] (machine_restart) from [] (kernel_restart+0x44/0x58)                                         [ 4505.412593] <0>-(0)[1:init][] (kernel_restart) from [] (SyS_reboot+0x1a4/0x1d8)                                            [ 4505.414167] <0>-(0)[1:init][] (SyS_reboot) from [] (ret_fast_syscall+0x0/0x38)

上面看的出调用过程,最后到了 kernel-3.18/drivers/watchdog/mediatek/wdk/wd_api.c

 

static int mtk_arch_reset_handle(struct notifier_block *this, unsigned long mode, void *cmd)

{

arch_reset(mode, cmd);

}

void arch_reset(char mode, const char *cmd)

{  //....

if (cmd && !strcmp(cmd, "charger")) {

/* do nothing */

} else if (cmd && !strcmp(cmd, "recovery")) {

rtc_mark_recovery(); //reboot recovery,传入的就是recovery

} else if (cmd && !strcmp(cmd, "bootloader")) {

rtc_mark_fast();

} else if (cmd && !strcmp(cmd, "kpoc")) {

}

void rtc_mark_recovery(void)

{ //....

hal_rtc_set_spare_register(RTC_FAC_RESET, 0x1);

}

最后,往 RTC写入RTC_FAC_RESET标志。

 

系统重启后,在lk里,就是判断这个标志,然后启动recovery的:

boot_mode_select  ->

if(Check_RTC_Recovery_Mode()){ //由此判断为recovery模式

          g_boot_mode = RECOVERY_BOOT; 

          return;

}

bool Check_RTC_Recovery_Mode(void)

{

pdn1 = RTC_Read(RTC_PDN1);

if( (pdn1 & RTC_PDN1_RECOVERY_MASK)==RTC_PDN1_FAC_RESET ) 

return true;

else

return false;

}

mt_boot_init  ->  boot_linux_from_storage

int boot_linux_from_storage(void)

{

switch (g_boot_mode) {

case NORMAL_BOOT:

case META_BOOT:

case ADVMETA_BOOT:

case SW_REBOOT:

case ALARM_BOOT:

case KERNEL_POWER_OFF_CHARGING_BOOT:

case LOW_POWER_OFF_CHARGING_BOOT:

mboot_android_load_bootimg_hdr(PART_BOOTIMG, CFG_BOOTIMG_LOAD_ADDR);

mboot_android_load_bootimg(PART_BOOTIMG, kimg_load_addr);

break;

case RECOVERY_BOOT:

mboot_android_load_recoveryimg_hdr(PART_RECOVERY, CFG_BOOTIMG_LOAD_ADDR);

mboot_android_load_recoveryimg(PART_RECOVERY, kimg_load_addr);

break;

}

}

如上,根据不同的启动模式,加载不同的分区的image。

 

-------------------------------------------  

另外,也看看kill.s的系统调用:

external/toybox/toys/posix/kill.c

void kill_main(void)

{

kill(procpid, signum);

}

bionic/libc/arch-arm/syscalls/kill.S

#include <private/bionic_asm.h>

ENTRY(kill)

    mov     ip, r7

    ldr     r7, =__NR_kill  @#define __NR_kill (__NR_SYSCALL_BASE+ 37)

    swi     #0

    mov     r7, ip

    cmn     r0, #(MAX_ERRNO + 1)

    bxls    lr

    neg     r0, r0

b       __set_errno_internal

END(kill)

引用 https://blog.csdn.net/feixin620/article/details/78416560 里的:

kill() -> kill.S -> swi陷入内核态 -> 从sys_call_table查看到sys_kill -> ret_fast_syscall -> 回到用户态执行kill()下一行代码。

你可能感兴趣的:(Android,Linux,recovery,系统调用,linux)