123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /*++
- Copyright (c) 2014 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- reset.c
- Abstract:
- This module implements support for rebooting the system.
- Author:
- Evan Green 16-Apr-2014
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include <minoca/kernel/kdebug.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the amount of time to wait for processes to end after a signal was
- // sent to them, in seconds.
- //
- #define RESET_SYSTEM_PROCESS_SIGNAL_TIMEOUT 30
- //
- // Define the amount of time to wait between checking the process count to see
- // if all processes have exited, in microseconds.
- //
- #define RESET_SYSTEM_SIGNAL_POLL_INTERVAL (20 * MICROSECONDS_PER_MILLISECOND)
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- KepSysResetSystemWorkItem (
- PVOID Parameter
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- KERNEL_API
- KSTATUS
- KeResetSystem (
- SYSTEM_RESET_TYPE ResetType
- )
- /*++
- Routine Description:
- This routine attempts to reboot the system. This routine must be called
- from low level.
- Arguments:
- ResetType - Supplies the desired system reset type. If the given type is
- not supported and a cold reset is, then a cold reset will be
- performed.
- Return Value:
- Does not return on success, the system is reset.
- STATUS_INVALID_PARAMETER if an invalid reset type was supplied.
- STATUS_NOT_SUPPORTED if the system cannot be reset.
- STATUS_UNSUCCESSFUL if the system did not reset.
- --*/
- {
- PSTR Description;
- ULONG FinalProcessCount;
- ULONGLONG Frequency;
- ULONG ProcessCount;
- KSTATUS Status;
- ULONGLONG Timeout;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Frequency = HlQueryTimeCounterFrequency();
- switch (ResetType) {
- case SystemResetWarm:
- Description = "warm reset";
- break;
- case SystemResetShutdown:
- Description = "shutdown";
- break;
- case SystemResetCold:
- Description = "cold reset";
- break;
- default:
- ASSERT(FALSE);
- return STATUS_INVALID_PARAMETER;
- }
- //
- // Send all processes a polite termination request.
- //
- RtlDebugPrint("System going down for %s. "
- "Sending all processes a termination signal...\n",
- Description);
- Status = PsSignalAllProcesses(TRUE, SIGNAL_REQUEST_TERMINATION, NULL);
- if (KSUCCESS(Status)) {
- Timeout = KeGetRecentTimeCounter() +
- (Frequency * RESET_SYSTEM_PROCESS_SIGNAL_TIMEOUT);
- //
- // Wait for the number of processes to drop to one (just the kernel
- // process).
- //
- do {
- ProcessCount = PsGetProcessCount();
- if (ProcessCount <= 1) {
- break;
- }
- KeDelayExecution(TRUE, FALSE, RESET_SYSTEM_SIGNAL_POLL_INTERVAL);
- } while (KeGetRecentTimeCounter() <= Timeout);
- }
- ProcessCount = PsGetProcessCount();
- if (ProcessCount != 1) {
- RtlDebugPrint("Still %d processes alive. Sending kill signal...\n",
- ProcessCount - 1);
- PsSignalAllProcesses(TRUE, SIGNAL_KILL, NULL);
- Timeout = KeGetRecentTimeCounter() +
- (Frequency * RESET_SYSTEM_PROCESS_SIGNAL_TIMEOUT);
- //
- // Wait for the number of processes to drop to one (just the kernel
- // process).
- //
- do {
- ProcessCount = PsGetProcessCount();
- if (ProcessCount <= 1) {
- break;
- }
- KeDelayExecution(TRUE, FALSE, RESET_SYSTEM_SIGNAL_POLL_INTERVAL);
- } while (KeGetRecentTimeCounter() <= Timeout);
- ProcessCount = PsGetProcessCount();
- if (ProcessCount != 1) {
- RtlDebugPrint("Warning: Still %d processes alive after kill "
- "signal!\n",
- ProcessCount - 1);
- RtlDebugPrint("Data loss is possible. Proceeding with reset "
- "anyway.\n");
- ASSERT(FALSE);
- }
- }
- Status = IoFlush(INVALID_HANDLE, 0, 0, FLUSH_FLAG_ALL_SYNCHRONOUS);
- if (!KSUCCESS(Status)) {
- RtlDebugPrint("Warning: Flush failure!\n");
- RtlDebugPrint("Data loss is possible. Proceeding with reset anyway.\n");
- ASSERT(FALSE);
- }
- //
- // Do a final check to make sure no processes sprung up.
- //
- if (ProcessCount <= 1) {
- FinalProcessCount = PsGetProcessCount();
- if (FinalProcessCount != 1) {
- RtlDebugPrint("Warning: Process count increased to %d after kill "
- "signal was sent!\n",
- FinalProcessCount - 1);
- ASSERT(FALSE);
- }
- }
- KdDisconnect();
- Status = HlResetSystem(ResetType, NULL, 0);
- KdConnect();
- RtlDebugPrint("System reset unsuccessful: %d\n", Status);
- return Status;
- }
- INTN
- KeSysResetSystem (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine implements the system call for resetting the system.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This stores the system reset type. It is passed to the
- kernel in a register.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- {
- SYSTEM_RESET_TYPE ResetType;
- KSTATUS Status;
- ResetType = (SYSTEM_RESET_TYPE)SystemCallParameter;
- //
- // Perform some validation here since the actual return status won't be
- // waited on by this thread.
- //
- if ((ResetType == SystemResetInvalid) ||
- (ResetType >= SystemResetTypeCount)) {
- Status = STATUS_INVALID_PARAMETER;
- goto SysResetSystemEnd;
- }
- Status = PsCheckPermission(PERMISSION_REBOOT);
- if (!KSUCCESS(Status)) {
- goto SysResetSystemEnd;
- }
- Status = KeCreateAndQueueWorkItem(NULL,
- WorkPriorityNormal,
- KepSysResetSystemWorkItem,
- (PVOID)ResetType);
- SysResetSystemEnd:
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- KepSysResetSystemWorkItem (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine implements the work item used to get the reset system call off
- of a user mode thread.
- Arguments:
- Parameter - Supplies a parameter that in this case represents the actual
- reset type itself.
- Return Value:
- None.
- --*/
- {
- SYSTEM_RESET_TYPE ResetType;
- ResetType = (SYSTEM_RESET_TYPE)Parameter;
- KeResetSystem(ResetType);
- return;
- }
|