reset.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /*++
  2. Copyright (c) 2014 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. reset.c
  9. Abstract:
  10. This module implements support for rebooting the system.
  11. Author:
  12. Evan Green 16-Apr-2014
  13. Environment:
  14. Kernel
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include <minoca/kernel/kdebug.h>
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // Define the amount of time to wait for processes to end after a signal was
  26. // sent to them, in seconds.
  27. //
  28. #define RESET_SYSTEM_PROCESS_SIGNAL_TIMEOUT 30
  29. //
  30. // Define the amount of time to wait between checking the process count to see
  31. // if all processes have exited, in microseconds.
  32. //
  33. #define RESET_SYSTEM_SIGNAL_POLL_INTERVAL (20 * MICROSECONDS_PER_MILLISECOND)
  34. //
  35. // ------------------------------------------------------ Data Type Definitions
  36. //
  37. //
  38. // ----------------------------------------------- Internal Function Prototypes
  39. //
  40. VOID
  41. KepSysResetSystemWorkItem (
  42. PVOID Parameter
  43. );
  44. //
  45. // -------------------------------------------------------------------- Globals
  46. //
  47. //
  48. // ------------------------------------------------------------------ Functions
  49. //
  50. KERNEL_API
  51. KSTATUS
  52. KeResetSystem (
  53. SYSTEM_RESET_TYPE ResetType
  54. )
  55. /*++
  56. Routine Description:
  57. This routine attempts to reboot the system. This routine must be called
  58. from low level.
  59. Arguments:
  60. ResetType - Supplies the desired system reset type. If the given type is
  61. not supported and a cold reset is, then a cold reset will be
  62. performed.
  63. Return Value:
  64. Does not return on success, the system is reset.
  65. STATUS_INVALID_PARAMETER if an invalid reset type was supplied.
  66. STATUS_NOT_SUPPORTED if the system cannot be reset.
  67. STATUS_UNSUCCESSFUL if the system did not reset.
  68. --*/
  69. {
  70. PSTR Description;
  71. ULONG FinalProcessCount;
  72. ULONGLONG Frequency;
  73. ULONG ProcessCount;
  74. KSTATUS Status;
  75. ULONGLONG Timeout;
  76. ASSERT(KeGetRunLevel() == RunLevelLow);
  77. Frequency = HlQueryTimeCounterFrequency();
  78. switch (ResetType) {
  79. case SystemResetWarm:
  80. Description = "warm reset";
  81. break;
  82. case SystemResetShutdown:
  83. Description = "shutdown";
  84. break;
  85. case SystemResetCold:
  86. Description = "cold reset";
  87. break;
  88. default:
  89. ASSERT(FALSE);
  90. return STATUS_INVALID_PARAMETER;
  91. }
  92. //
  93. // Send all processes a polite termination request.
  94. //
  95. RtlDebugPrint("System going down for %s. "
  96. "Sending all processes a termination signal...\n",
  97. Description);
  98. Status = PsSignalAllProcesses(TRUE, SIGNAL_REQUEST_TERMINATION, NULL);
  99. if (KSUCCESS(Status)) {
  100. Timeout = KeGetRecentTimeCounter() +
  101. (Frequency * RESET_SYSTEM_PROCESS_SIGNAL_TIMEOUT);
  102. //
  103. // Wait for the number of processes to drop to one (just the kernel
  104. // process).
  105. //
  106. do {
  107. ProcessCount = PsGetProcessCount();
  108. if (ProcessCount <= 1) {
  109. break;
  110. }
  111. KeDelayExecution(TRUE, FALSE, RESET_SYSTEM_SIGNAL_POLL_INTERVAL);
  112. } while (KeGetRecentTimeCounter() <= Timeout);
  113. }
  114. ProcessCount = PsGetProcessCount();
  115. if (ProcessCount != 1) {
  116. RtlDebugPrint("Still %d processes alive. Sending kill signal...\n",
  117. ProcessCount - 1);
  118. PsSignalAllProcesses(TRUE, SIGNAL_KILL, NULL);
  119. Timeout = KeGetRecentTimeCounter() +
  120. (Frequency * RESET_SYSTEM_PROCESS_SIGNAL_TIMEOUT);
  121. //
  122. // Wait for the number of processes to drop to one (just the kernel
  123. // process).
  124. //
  125. do {
  126. ProcessCount = PsGetProcessCount();
  127. if (ProcessCount <= 1) {
  128. break;
  129. }
  130. KeDelayExecution(TRUE, FALSE, RESET_SYSTEM_SIGNAL_POLL_INTERVAL);
  131. } while (KeGetRecentTimeCounter() <= Timeout);
  132. ProcessCount = PsGetProcessCount();
  133. if (ProcessCount != 1) {
  134. RtlDebugPrint("Warning: Still %d processes alive after kill "
  135. "signal!\n",
  136. ProcessCount - 1);
  137. RtlDebugPrint("Data loss is possible. Proceeding with reset "
  138. "anyway.\n");
  139. ASSERT(FALSE);
  140. }
  141. }
  142. Status = IoFlush(INVALID_HANDLE, 0, 0, FLUSH_FLAG_ALL_SYNCHRONOUS);
  143. if (!KSUCCESS(Status)) {
  144. RtlDebugPrint("Warning: Flush failure!\n");
  145. RtlDebugPrint("Data loss is possible. Proceeding with reset anyway.\n");
  146. ASSERT(FALSE);
  147. }
  148. //
  149. // Do a final check to make sure no processes sprung up.
  150. //
  151. if (ProcessCount <= 1) {
  152. FinalProcessCount = PsGetProcessCount();
  153. if (FinalProcessCount != 1) {
  154. RtlDebugPrint("Warning: Process count increased to %d after kill "
  155. "signal was sent!\n",
  156. FinalProcessCount - 1);
  157. ASSERT(FALSE);
  158. }
  159. }
  160. KdDisconnect();
  161. Status = HlResetSystem(ResetType, NULL, 0);
  162. KdConnect();
  163. RtlDebugPrint("System reset unsuccessful: %d\n", Status);
  164. return Status;
  165. }
  166. INTN
  167. KeSysResetSystem (
  168. PVOID SystemCallParameter
  169. )
  170. /*++
  171. Routine Description:
  172. This routine implements the system call for resetting the system.
  173. Arguments:
  174. SystemCallParameter - Supplies a pointer to the parameters supplied with
  175. the system call. This stores the system reset type. It is passed to the
  176. kernel in a register.
  177. Return Value:
  178. STATUS_SUCCESS or positive integer on success.
  179. Error status code on failure.
  180. --*/
  181. {
  182. SYSTEM_RESET_TYPE ResetType;
  183. KSTATUS Status;
  184. ResetType = (SYSTEM_RESET_TYPE)SystemCallParameter;
  185. //
  186. // Perform some validation here since the actual return status won't be
  187. // waited on by this thread.
  188. //
  189. if ((ResetType == SystemResetInvalid) ||
  190. (ResetType >= SystemResetTypeCount)) {
  191. Status = STATUS_INVALID_PARAMETER;
  192. goto SysResetSystemEnd;
  193. }
  194. Status = PsCheckPermission(PERMISSION_REBOOT);
  195. if (!KSUCCESS(Status)) {
  196. goto SysResetSystemEnd;
  197. }
  198. Status = KeCreateAndQueueWorkItem(NULL,
  199. WorkPriorityNormal,
  200. KepSysResetSystemWorkItem,
  201. (PVOID)ResetType);
  202. SysResetSystemEnd:
  203. return Status;
  204. }
  205. //
  206. // --------------------------------------------------------- Internal Functions
  207. //
  208. VOID
  209. KepSysResetSystemWorkItem (
  210. PVOID Parameter
  211. )
  212. /*++
  213. Routine Description:
  214. This routine implements the work item used to get the reset system call off
  215. of a user mode thread.
  216. Arguments:
  217. Parameter - Supplies a parameter that in this case represents the actual
  218. reset type itself.
  219. Return Value:
  220. None.
  221. --*/
  222. {
  223. SYSTEM_RESET_TYPE ResetType;
  224. ResetType = (SYSTEM_RESET_TYPE)Parameter;
  225. KeResetSystem(ResetType);
  226. return;
  227. }