android之恢復出廠設置的關機流程

意外發現:setprop sys.powerctl reboot,recovery 將重啓進入有菜單選擇界面的recovery模式,在此模式下有root權限,不過要通過串口模式操作。

在系統設置應用裏有恢復出廠設置的菜單,點擊彈出對話框,確認後調用恢復出廠設置廣播:

Settings/src/com/android/tv/settings/device/privacy/PrivacyActivity.java:171

    private void onFactoryResetConfirm(ActionBehavior behaviour) {
        switch (behaviour) {
            case INIT:
                setContentAndActionFragments(ContentFragment.newInstance(
                        ActionType.FACTORY_RESET_CONFIRM.getTitle(getResources()),
                        getString(R.string.device_reset),
                        getString(R.string.confirm_factory_reset_description),
                        R.drawable.ic_settings_backuprestore,
                        getResources().getColor(R.color.icon_background)),
                        ActionFragment.newInstance(getFactoryResetConfirmActions()));
                break;
            case OK:
                if (!ActivityManager.isUserAMonkey()) {
                    Intent resetIntent = new Intent("android.intent.action.MASTER_CLEAR");
                    if (getIntent().getBooleanExtra(SHUTDOWN_INTENT_EXTRA, false)) {
                        resetIntent.putExtra(SHUTDOWN_INTENT_EXTRA, true);
                    }
                    sendBroadcast(resetIntent);
                }
                break;
            case CANCEL:
                getFragmentManager().popBackStack(null, 0);
                getFragmentManager().popBackStack(null, 0);
                break;
            default:
                break;
        }
    }

搜索字段MASTER_CLEAR,frameworks/base/core/res/AndroidManifest.xml裏有註冊receiver

frameworks\base\services\core\java\com\android\server\MasterClearReceiver.java

    public void onReceive(final Context context, final Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
            if (!"google.com".equals(intent.getStringExtra("from"))) {
                Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
                return;
            }
        }

        final boolean shutdown = intent.getBooleanExtra("shutdown", false);
        final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
        final boolean wipeExternalStorage = intent.getBooleanExtra(
                Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);

        Slog.w(TAG, "!!! FACTORY RESET !!!");
        // The reboot call is blocking, so we need to do it on another thread.
        Thread thr = new Thread("Reboot") {
            @Override
            public void run() {
                try {
                    RecoverySystem.rebootWipeUserData(context, shutdown, reason);
                    Log.wtf(TAG, "Still running after master clear?!");
                } catch (IOException e) {
                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
                } catch (SecurityException e) {
                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
                }
            }
        };

        if (wipeExternalStorage) {
            // thr will be started at the end of this task.
            new WipeAdoptableDisksTask(context, thr).execute();
        } else {
            thr.start();
        }
    }

frameworks\base\core\java\android\os\RecoverySystem.java

    public static void rebootWipeUserData(Context context, boolean shutdown, String reason)
            throws IOException {
        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
        if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
            throw new SecurityException("Wiping data is not allowed for this user.");
        }
        final ConditionVariable condition = new ConditionVariable();

        Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
                android.Manifest.permission.MASTER_CLEAR,
                new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        condition.open();
                    }
                }, null, 0, null, null);

        // Block until the ordered broadcast has completed.
        condition.block();

        String shutdownArg = null;
        if (shutdown) {
            shutdownArg = "--shutdown_after";
        }

        String reasonArg = null;
        if (!TextUtils.isEmpty(reason)) {
            reasonArg = "--reason=" + sanitizeArg(reason);
        }

        final String localeArg = "--locale=" + Locale.getDefault().toString();
        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
    }
    private static void bootCommand(Context context, String... args) throws IOException {
        RECOVERY_DIR.mkdirs();  // In case we need it
        COMMAND_FILE.delete();  // In case it's not writable
        LOG_FILE.delete();

        FileWriter command = new FileWriter(COMMAND_FILE);
        try {
            for (String arg : args) {
                if (!TextUtils.isEmpty(arg)) {
                    // MStar Android Patch Begin
                    StorageManager mStorageManager = context.getSystemService(StorageManager.class);
                    String cmd = arg;
                    String label = null;
                    String uuid = null;

                    if (cmd.startsWith("--update_package")) {
                        cmd = arg.substring(17, arg.length() -1);
                        if (cmd.startsWith("/cache")) {
                            command.write("--uuid=mstar-cache");
                            command.write("\n");
                            command.write("--label=mstar-cache");
                            command.write("\n");
                        } else {
                            StorageVolume vol = mStorageManager.getStorageVolume(new File(cmd));
                            uuid = "--uuid=" + vol.getUuid();
                            label = "--label=" + utf8ToUnicode(vol.getFsLabel());
                            command.write(uuid);
                            command.write("\n");
                            command.write(label);
                            command.write("\n");
                        }
                    }
                    // MStar Android Patch End
                    command.write(arg);
                    command.write("\n");
                }
            }
        } finally {
            command.close();
        }

		Log.i(TAG, "bootCommand /cache");
        // Having written the command file, go ahead and reboot
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        pm.reboot(PowerManager.REBOOT_RECOVERY);

        throw new IOException("Reboot failed (no permissions?)");
    }

此時會在安卓設備裏/cache/recovery 目錄清除,且創建command文件,寫入命令:

#/cache/recovery # ls -l
-rw------- system   system         55 2018-01-01 08:01 command

#cat command                                      
--wipe_data
--reason=MasterClearConfirm
--locale=zh_CN

然後重啓,重啓還需處理,否則不會進入recovery。ShutdownThread.java:

 private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }

        // Throw up a system dialog to indicate the device is rebooting / shutting down.
        ProgressDialog pd = new ProgressDialog(context);

        // Path 1: Reboot to recovery and install the update
        //   Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
        //   (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
        //   UI: progress bar
        //
        // Path 2: Reboot to recovery for factory reset
        //   Condition: mRebootReason == REBOOT_RECOVERY
        //   UI: spinning circle only (no progress bar)
        //
        // Path 3: Regular reboot / shutdown
        //   Condition: Otherwise
        //   UI: spinning circle only (no progress bar)
        if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
            mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
            if (mRebootUpdate) {
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
                pd.setMessage(context.getText(
                        com.android.internal.R.string.reboot_to_update_prepare));
                pd.setMax(100);
                pd.setProgressNumberFormat(null);
                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                pd.setProgress(0);
                pd.setIndeterminate(false);
            } else {
                // Factory reset path. Set the dialog message accordingly.
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
                pd.setMessage(context.getText(
                        com.android.internal.R.string.reboot_to_reset_message));
                pd.setIndeterminate(true);
            }
        } else {
.
.
.
.


        // also make sure the screen stays on for better user experience
        sInstance.mScreenWakeLock = null;
        if (sInstance.mPowerManager.isScreenOn()) {
            try {
                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, "No permission to acquire wake lock", e);
                sInstance.mScreenWakeLock = null;
            }
        }

        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }

調到最後一行,sInstance.start(); 啓動線程的運行,進入線程的run方法。

    /**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
     */
    public void run() {
        BroadcastReceiver br = new BroadcastReceiver() {
            @Override public void onReceive(Context context, Intent intent) {
                // We don't allow apps to cancel this, so ignore the result.
                actionDone();
            }
        };

        /*
         * Write a system property in case the system_server reboots before we
         * get to the actual hardware restart. If that happens, we'll retry at
         * the beginning of the SystemServer startup.
         */
        {
            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
        }

        /*
         * If we are rebooting into safe mode, write a system property
         * indicating so.
         */
        if (mRebootSafeMode) {
            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
        }

        Log.i(TAG, "Sending shutdown broadcast...");

        // First send the high-level shut down broadcast.
        mActionDone = false;
        Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        mContext.sendOrderedBroadcastAsUser(intent,
                UserHandle.ALL, null, br, mHandler, 0, null, null);

        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            while (!mActionDone) {
                long delay = endTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown broadcast timed out");
                    break;
                } else if (mRebootUpdate) {
                    int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                            BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                } catch (InterruptedException e) {
                }
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
        }

        Log.i(TAG, "Shutting down activity manager...");

        final IActivityManager am =
            ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);
            } catch (RemoteException e) {
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
        }

        Log.i(TAG, "Shutting down package manager...");

        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
        }

        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);
        if (mRebootUpdate) {
            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
        }

        // Shutdown MountService to ensure media is in a safe state
        IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
            public void onShutDownComplete(int statusCode) throws RemoteException {
                Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
                actionDone();
            }
        };

        Log.i(TAG, "Shutting down MountService");

        // Set initial variables and time out time.
        mActionDone = false;
        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
        synchronized (mActionDoneSync) {
            try {
                final IMountService mount = IMountService.Stub.asInterface(
                        ServiceManager.checkService("mount"));
                if (mount != null) {
                    mount.shutdown(observer);
                } else {
                    Log.w(TAG, "MountService unavailable for shutdown");
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception during MountService shutdown", e);
            }
            while (!mActionDone) {
                long delay = endShutTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown wait timed out");
                    break;
                } else if (mRebootUpdate) {
                    int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
                            (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
                            MAX_SHUTDOWN_WAIT_TIME);
                    status += RADIO_STOP_PERCENT;
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                } catch (InterruptedException e) {
                }
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

            // If it's to reboot to install update, invoke uncrypt via init service.
            uncrypt();
        }

        rebootOrShutdown(mContext, mReboot, mRebootReason);
    }

rebootOrShutdown(mContext, mReboot, mRebootReason);

    public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator(context);
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }

            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException unused) {
            }
        }

        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown();
    }

PowerManagerService.lowLevelReboot(reason);

    /**
     * Low-level function to reboot the device. On success, this
     * function doesn't return. If more than 20 seconds passes from
     * the time a reboot is requested, this method returns.
     *
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     */
    public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }
        if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
            // If we are rebooting to go into recovery, instead of
            // setting sys.powerctl directly we'll start the
            // pre-recovery service which will do some preparation for
            // recovery and then reboot for us.
            SystemProperties.set("ctl.start", "pre-recovery");
        } else {
            SystemProperties.set("sys.powerctl", "reboot," + reason);
        }
        try {
            Thread.sleep(20 * 1000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
    }

SystemProperties.set("ctl.start", "pre-recovery"); 此處將導致系統重啓,啓動init.rc裏註冊的服務:

service pre-recovery /system/bin/uncrypt --reboot
    class main
    disabled
    oneshot

service pre-recovery /system/bin/uncrypt --reboot    system/bin/uncrypt 這個程序會被執行,傳入的參數是--reboot

進入bootable/recovery/uncrypt/uncrypt.cpp 

int main(int argc, char** argv) {
    const char* input_path;
    const char* map_file;

    if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) {
        fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]);
        return 2;
    }

    // When uncrypt is started with "--reboot", it wipes misc and reboots.
    // Otherwise it uncrypts the package and writes the block map.
    if (argc == 2) {
		printf("\r\n uncrypt to wipe_misc");
        if (read_fstab() == NULL) {
            return 1;
        }
        wipe_misc();
        reboot_to_recovery();
    } else {
        // The pipe has been created by the system server.
        int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
        if (status_fd == -1) {
            ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno));
            return 1;
        }

        if (argc == 3) {
            // when command-line args are given this binary is being used
            // for debugging.
            input_path = argv[1];
            map_file = argv[2];
        } else {
            std::string package;
            if (!find_uncrypt_package(package)) {
                android::base::WriteStringToFd("-1\n", status_fd);
                close(status_fd);
                return 1;
            }
            input_path = package.c_str();
            map_file = cache_block_map.c_str();
        }

        int status = uncrypt(input_path, map_file, status_fd);
        if (status != 0) {
            android::base::WriteStringToFd("-1\n", status_fd);
            close(status_fd);
            return 1;
        }

        android::base::WriteStringToFd("100\n", status_fd);
        close(status_fd);
    }

    return 0;
}
static void reboot_to_recovery() {
    ALOGI("rebooting to recovery");
    property_set("sys.powerctl", "reboot,recovery");
    sleep(10);
    ALOGE("reboot didn't succeed?");
}

    property_set("sys.powerctl", "reboot,recovery");

sys.powerctl屬性出發開關在init.rc中

on property:sys.powerctl=*
    powerctl ${sys.powerctl}

system/core/init/keywords.h
KEYWORD(powerctl,    COMMAND, 1, do_powerctl
system/core/init/builtins.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);
}
android_reboot(cmd, 0, reboot_target);    參數爲(ANDROID_RB_RESTART2, 0, "recovery")
system/core/libcutils/android_reboot.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;
}


        case ANDROID_RB_RESTART2:
            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);

在用戶空間和內核空間之間,有一個叫做Syscall(系統調用, system call)的中間層,是連接用戶態和內核態的橋樑。這樣即提高了內核的安全型,也便於移植,只需實現同一套接口即可。Linux系統,用戶空間通過向內核空間發出Syscall,產生軟中斷,從而讓程序陷入內核態,執行相應的操作。對於每個系統調用都會有一個對應的系統調用號,比很多操作系統要少很多。

kernel/include/uapi/asm-generic/unistd.h
/* kernel/sys.c */
#define __NR_setpriority 140
__SYSCALL(__NR_setpriority, sys_setpriority)
#define __NR_getpriority 141
__SYSCALL(__NR_getpriority, sys_getpriority)
#define __NR_reboot 142
__SYSCALL(__NR_reboot, sys_reboot)
#define __NR_setregid 143

/* kernel/sys.c */表明是在/* kernel/sys.c */的文件裏定義的方法

宏定義SYSCALL_DEFINEx(xxx,…),展開後對應的方法則是sys_xxx

kernel/sys.c
/*
 * 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 (MP_PLATFORM_PM == 0)
	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
		cmd = LINUX_REBOOT_CMD_HALT;

#endif /* MP_PLATFORM_PM */
	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:
		if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 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;
}
kernel_restart(buffer); 參數爲("recovery")
/**
 *	kernel_restart - reboot the system
 *	@cmd: pointer to buffer containing command to execute for restart
 *		or %NULL
 *
 *	Shutdown everything and perform a clean reboot.
 *	This is not safe to call in interrupt context.
 */
void kernel_restart(char *cmd)
{

    extern ptrdiff_t   mstar_pm_base;
	kernel_restart_prepare(cmd);
	migrate_to_reboot_cpu();
	syscore_shutdown();
	if (!cmd)
		printk(KERN_EMERG "Restarting system.\n");
	else
		printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);

	#if defined(CONFIG_ARM)
	*((volatile unsigned int*) (0xfd000000 + (0x3008 << 1))) = 0x0e00;
	*((volatile unsigned int*) (0xfd000000 + (0x300a << 1))) = 0x0727;
	#elif defined(CONFIG_ARM64)

	*((volatile unsigned int*) (mstar_pm_base + (0x3008 << 1))) = 0x0e00;
	*((volatile unsigned int*) (mstar_pm_base + (0x300a << 1))) = 0x0727;
	#endif

	kmsg_dump(KMSG_DUMP_RESTART);
	machine_restart(cmd);
}

machine_restart(cmd); 參數爲("recovery"),machine_restart 函數在很多文件裏都有,具體是哪個文件暫不研究,猜測可能走下面這條路,不過也有可能搞錯了。

arch/arm64/kernel/process.c
void machine_restart(char *cmd)
{
	machine_shutdown();

	/* Disable interrupts first */
	local_irq_disable();
	local_fiq_disable();

	/* Now call the architecture specific reboot code. */
	if (arm_pm_restart)
		arm_pm_restart('h', cmd);

	/*
	 * Whoops - the architecture was unable to reboot.
	 */
	printk("Reboot failed -- System halted\n");
	while (1);
}

        arm_pm_restart('h', cmd); cmd="recovery"

arch/arm64/kernel/process.c
void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;
EXPORT_SYMBOL_GPL(arm_pm_restart);
arch/arm64/kernel/process.c
#if (MP_PLATFORM_PM == 1)
static void arm_machine_restart(char mode, const char *cmd)
{
	/*
	 * Tell the mm system that we are going to reboot -
	 * we may need it to insert some 1:1 mappings so that
	 * soft boot works.
	 */
	setup_mm_for_reboot();

#ifndef CONFIG_MP_PLATFORM_ARM
	/* Clean and invalidate caches */
	flush_cache_all();

	/* Turn D-cache off */
	cpu_cache_off();

	/* Push out any further dirty data, and ensure cache is empty */
	flush_cache_all();
#endif
	/*
	 * Now call the architecture specific reboot code.
	 */
	arch_reset(mode, cmd);
}
#else
static void arm_machine_restart(char mode, const char *cmd){}
#endif
kernel\linaro\mstar2\hal\mainz\cpu\arm64\include\mach\system.h
static inline void arch_reset(char mode, const char *cmd)
{
#ifdef CONFIG_SMP
	if(magic_number_address != 0)
		*(volatile unsigned short *)(magic_number_address) =  0x0;
	*(volatile unsigned long*)(REG_PM_MISC_BASE + 0xB8) = 0x79;    // reg_top_sw_rst
#endif
#if 0
// set WDT count down timer
*(volatile unsigned long*)(REG_WDT_BASE + 0x10) = BOOT_DELAY_MILLISECOND & 0x0000FFFF;
*(volatile unsigned long*)(REG_WDT_BASE + 0x14) = BOOT_DELAY_MILLISECOND  >> 16  ;
#endif
}

arch_reset(mode, cmd); 參數爲('h',"recovery")

#define REG_PM_MISC_BASE                (0x2e00UL)

0x2eB8 寫 0x79

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