因为客户需要增加factory mode,而且只能用命令进入,而不让用户感知这个模式的存在,所以决定在系统中增加命令: reboot factory,从而进入factory mode
先看一下reboot命令是如何实现的。
我们在串口输入或者在cmd命令行输入 reboot后,系统会重启,如果后面跟的参数是 bootloader,机器会进入fastboot模式,而如果后面跟的参数是 recovery,则机器进入recovery模式。
一、用户空间部分
查看下reboot命令的代码:
#include
#include
#include
#include
#include
#include
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;
}
代码比较简单,而且在代码中没有看到直接调用reboot函数或者其他函数来实现reboot。我们可以看到它只是设置了一个属性,这个属性为宏 “ANDROID_RB_PROPERTY”,而这个宏定义为:
#define ANDROID_RB_PROPERTY "sys.powerctl"
由此可见,只是设置了sys.powerctl这个属性,而这个属性会触发什么事情呢?
我们可以再init.rc中找到答案:
on property:sys.powerctl=*
powerctl ${sys.powerctl}
这里调用了powerct方法,而我们得知init.rc中,这些叫做关键字,所以我们去init的源码中,找到对应的关键字处理:init/keywords.h
KEYWORD(powerctl, COMMAND, 1, do_powerctl)
进而找到do_powerctl函数: 定义在init/builtin.cpp中
int do_powerctl(int nargs, char **args)
{
char command[PROP_VALUE_MAX];
int res;
int len = 0;
int cmd = 0;
const char *reboot_target;
res = expand_props(command, args[1], sizeof(command));
if (res) {
ERROR("powerctl: cannot expand '%s'\n", args[1]);
return -EINVAL;
}
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;
}
if (command[len] == ',') {
reboot_target = &command[len + 1];
} else if (command[len] == '\0') {
reboot_target = "";
} else {
ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
return -EINVAL;
}
return android_reboot(cmd, 0, reboot_target);
}
通过传入的值分别处理:
reboot 和 shutdown 对应的处理不同,最后执行android_reboot函数
/system/core/libcutils.c
int android_reboot(int cmd, int flags UNUSED, const 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;
}
这里调用了syscall,这个会触发系统进入内核调用,关于syscall如何陷入内核进行处理,我们参考别的资料
https://blog.csdn.net/rikeyone/article/details/91047118
二、内核部分
我们这里直接贴出内核中reboot的实现:
kernel/reboot.c
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(&reboot_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
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(&reboot_mutex);
return ret;
}
1、检查用户空间传入的magic值
2、根据传入的cmd,分别处理
我们传入的cmd为 LINUX_REBOOT_CMD_RESTART2,接着调用kernel_restart(buffer)函数,buff中的内容为附加的参数 :“bootloader”或者“recovery”,
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);
}
这里做kernel restart之前的准备,最后调用machine_restart
arch/arm/process.c
void machine_restart(char *cmd)
{
/* Disable interrupts first */
local_irq_disable();
smp_send_stop();
/* Now call the architecture specific reboot code. */
pr_emerg("machine_restart, arm_pm_restart(%p)\n", arm_pm_restart);
if (arm_pm_restart)
arm_pm_restart(reboot_mode, cmd);
else
do_kernel_restart(cmd);
/*
* Whoops - the architecture was unable to reboot.
*/
printk("Reboot failed -- System halted\n");
while (1);
}
当定义了arm_pm_restart函数,就执行arm_pm_restart函数,如果没有定义,则执行do_kernel_restart函数。
我们这里没有定义arm_pm_restart,所以执行do_kernel_restart
void do_kernel_restart(char *cmd)
{
atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
}
这个函数主要是发送通知给到各个注册了reboot通知的模块,我们这里主要看wtd中的通知处理
drivers/watchdog/mediatek/wtd/wd_api.c
这里注册notifier,设置notifier handler
static int __init mtk_arch_reset_init(void)
{
int ret;
mtk_restart_handler.notifier_call = mtk_arch_reset_handle;
mtk_restart_handler.priority = 128;
pr_alert("\n register_restart_handler- 0x%p, Notify call: - 0x%p\n",
&mtk_restart_handler, mtk_restart_handler.notifier_call);
ret = register_restart_handler(&mtk_restart_handler);
if (ret)
pr_err("ARCH_RESET cannot register mtk_restart_handler!!!!\n");
pr_alert("ARCH_RESET register mtk_restart_handler ok!!!!\n");
return ret;
}
pure_initcall(mtk_arch_reset_init);
当有通知过来后,执行mtk_restart_handler
static int mtk_arch_reset_handle(struct notifier_block *this, unsigned long mode, void *cmd)
{
pr_alert("ARCH_RESET happen!!!\n");
arch_reset(mode, cmd);
pr_alert("ARCH_RESET end!!!!\n");
return NOTIFY_DONE;
}
接下来就是调用arch_reset函数了,也是很重要的函数,可以看到我们传入的“bootloader”或者“recovery”等参数的处理过程
void arch_reset(char mode, const char *cmd)
{
#ifdef CONFIG_FPGA_EARLY_PORTING
return;
#else
char reboot = 0;
int res = 0;
struct wd_api *wd_api = NULL;
res = get_wd_api(&wd_api);
pr_alert("arch_reset: cmd = %s\n", cmd ? : "NULL");
dump_stack();
if (cmd && !strcmp(cmd, "charger")) {
/* do nothing */
} else if (cmd && !strcmp(cmd, "recovery")) {
rtc_mark_recovery();
} else if (cmd && !strcmp(cmd, "bootloader")) {
rtc_mark_fast();
} else if (cmd && !strcmp(cmd, "kpoc")) {
#ifdef CONFIG_MTK_KERNEL_POWER_OFF_CHARGING
rtc_mark_kpoc();
#endif
#if defined(CONFIG_ARCH_MT8163) || defined(CONFIG_ARCH_MT8173)
} else if (cmd && !strcmp(cmd, "rpmbpk")) {
mtk_wd_SetNonResetReg2(0x0, 1);
#endif
} else {
reboot = 1;
}
if (res)
pr_err("arch_reset, get wd api error %d\n", res);
else
wd_api->wd_sw_reset(reboot);
#endif
}
这里对传入的参数分别进行处理,如果参数为 “recovery”,则执行rtc_mark_recovery函数,
drivers/watchdog/mediatek/wtd/mtk_rtc_common.c
void rtc_mark_recovery(void)
{
unsigned long flags;
struct rtc_time defaulttm;
rtc_xinfo("rtc_mark_recovery\n");
spin_lock_irqsave(&rtc_lock, flags);
hal_rtc_set_spare_register(RTC_FAC_RESET, 0x1);
/* Clear alarm setting when doing factory reset. */
defaulttm.tm_year = RTC_DEFAULT_YEA - RTC_MIN_YEAR;
defaulttm.tm_mon = RTC_DEFAULT_MTH;
defaulttm.tm_mday = RTC_DEFAULT_DOM;
defaulttm.tm_wday = 1;
defaulttm.tm_hour = 0;
defaulttm.tm_min = 0;
defaulttm.tm_sec = 0;
rtc_save_pwron_time(false, &defaulttm, false);
hal_rtc_clear_alarm(&defaulttm);
spin_unlock_irqrestore(&rtc_lock, flags);
}
这里主要是设置RTC_FAC_RESET寄存器的值,等到重启后,机器执行bootloader的时候,取出对应的值来判断该执行哪种模式。
所以,到此之后,我们知道reboot bootloader和reboot recovery在kernel中的执行过程。所以我们如果要想新建一个参数 reboot factory
来使机器进入factory mode,我们只需要在arch_reset中增加对应的处理,并且在RTC中找到一个合适的寄存器来保存值即可
修改代码后的补丁:
diff --git a/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h b/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h
old mode 100644
new mode 100755
index 2181e99..131ea65
--- a/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h
+++ b/drivers/misc/mediatek/include/mt-plat/mtk_rtc.h
@@ -51,6 +51,7 @@ extern void rtc_mark_recovery(void);
extern void rtc_mark_kpoc(void);
#endif/*if defined(CONFIG_MTK_KERNEL_POWER_OFF_CHARGING)*/
extern void rtc_mark_fast(void);
+extern void rtc_mark_factory_mode(void);
extern u16 rtc_rdwr_uart_bits(u16 *val);
extern void rtc_bbpu_power_down(void);
extern void rtc_read_pwron_alarm(struct rtc_wkalrm *alm);
diff --git a/drivers/misc/mediatek/rtc/mtk_rtc_common.c b/drivers/misc/mediatek/rtc/mtk_rtc_common.c
old mode 100644
new mode 100755
index 87fe9d7..6de7d77
--- a/drivers/misc/mediatek/rtc/mtk_rtc_common.c
+++ b/drivers/misc/mediatek/rtc/mtk_rtc_common.c
@@ -386,6 +386,25 @@ void rtc_mark_fast(void)
spin_unlock_irqrestore(&rtc_lock, flags);
}
+void rtc_mark_factory_mode(void)
+{
+ unsigned long flags;
+
+ printk("rtc_mark_dbgt\n");
+ spin_lock_irqsave(&rtc_lock, flags);
+ hal_rtc_set_spare_register(RTC_ANDROID, 0x5);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+}
+
+void rtc_clr_factory_mode(void)
+{
+ unsigned long flags;
+ printk("rtc_mark_dbgt\n");
+ spin_lock_irqsave(&rtc_lock, flags);
+ hal_rtc_set_spare_register(RTC_ANDROID, 0x0);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+}
+
u16 rtc_rdwr_uart_bits(u16 *val)
{
u16 ret = 0;
diff --git a/drivers/watchdog/mediatek/wdk/wd_api.c b/drivers/watchdog/mediatek/wdk/wd_api.c
old mode 100644
new mode 100755
index 7043b25..8aaca5b
--- a/drivers/watchdog/mediatek/wdk/wd_api.c
+++ b/drivers/watchdog/mediatek/wdk/wd_api.c
@@ -606,7 +606,10 @@ void arch_reset(char mode, const char *cmd)
} else if (cmd && !strcmp(cmd, "rpmbpk")) {
mtk_wd_SetNonResetReg2(0x0, 1);
#endif
- } else {
+ }else if(cmd && !strcmp(cmd, "factory")){
+ rtc_mark_factory_mode();
+ }
+ else {
reboot = 1;
}
三、bootloader部分
diff --git a/platform/mt8163/boot_mode.c b/platform/mt8163/boot_mode.c
old mode 100644
new mode 100755
index ba857f1..a75a11a
--- a/platform/mt8163/boot_mode.c
+++ b/platform/mt8163/boot_mode.c
@@ -96,7 +96,7 @@ void boot_mode_select(void)
return;
}
mrdump_check();
-
+
#if defined (HAVE_LK_TEXT_MENU)
/*Check RTC to know if system want to reboot to Fastboot*/
if(Check_RTC_PDN1_bit13())
@@ -129,6 +129,13 @@ void boot_mode_select(void)
g_boot_mode = RECOVERY_BOOT;
return;
}
+
+ if(Check_RTC_FAC_Mode())
+ {
+ Set_Clr_RTC_FAC_mode(false);
+ g_boot_mode = FACTORY_BOOT;
+ return;
+ }
/*If MISC Write has not completed in recovery mode
before system reboot, go to recovery mode to
finish remain tasks*/
diff --git a/platform/mt8163/mt_rtc.c b/platform/mt8163/mt_rtc.c
old mode 100644
new mode 100755
index 9a76aa4..1255a19
--- a/platform/mt8163/mt_rtc.c
+++ b/platform/mt8163/mt_rtc.c
@@ -249,6 +249,34 @@ bool Check_RTC_PDN1_bit13(void)
return false;
}
+#define RTC_PDN1_FAC_MODE 0x000f
+bool Check_RTC_FAC_Mode(void)
+{
+ U16 pdn1;
+
+ pdn1 = RTC_Read(RTC_PDN1);
+ if( (pdn1 & RTC_PDN1_ANDROID_MASK)== 0x05 )
+ return true;
+ else
+ return false;
+}
+
+void Set_Clr_RTC_FAC_mode(bool flag)
+{
+ U16 pdn1;
+
+ rtc_writeif_unlock();
+ //use PDN1 bit13 for LK
+ pdn1 = RTC_Read(RTC_PDN1);
+ if(flag==true)
+ pdn1 = pdn1 | RTC_PDN1_FAC_MODE;
+ else if(flag==false)
+ pdn1 = pdn1 & ~RTC_PDN1_FAC_MODE;
+ RTC_Write(RTC_PDN1, pdn1);
+ rtc_write_trigger();
+}
+
+
bool Check_RTC_Recovery_Mode(void)
{
U16 pdn1;