要进入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_COMPAT和CONFIG_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][
[ 4505.399883] <0>-(0)[1:init][
[ 4505.401661] <0>-(0)[1:init][
[ 4505.403549] <0>-(0)[1:init][
[ 4505.405283] <0>-(0)[1:init][
[ 4505.409796] <0>-(0)[1:init][
[ 4505.410989] <0>-(0)[1:init][
上面看的出调用过程,最后到了 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()下一行代码。