1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * PURPOSE: x64 trap handlers 5 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org) 6 */ 7 8 /* INCLUDES ******************************************************************/ 9 10 #include <ntoskrnl.h> 11 12 #define NDEBUG 13 #include <debug.h> 14 15 VOID 16 KiRetireDpcListInDpcStack( 17 PKPRCB Prcb, 18 PVOID DpcStack); 19 20 NTSTATUS 21 KiConvertToGuiThread( 22 VOID); 23 24 _Requires_lock_not_held_(Prcb->PrcbLock) 25 VOID 26 NTAPI 27 KiDpcInterruptHandler(VOID) 28 { 29 PKPRCB Prcb = KeGetCurrentPrcb(); 30 PKTHREAD NewThread, OldThread; 31 KIRQL OldIrql; 32 33 /* Raise to DISPATCH_LEVEL */ 34 OldIrql = KfRaiseIrql(DISPATCH_LEVEL); 35 36 /* Send an EOI */ 37 KiSendEOI(); 38 39 /* Check for pending timers, pending DPCs, or pending ready threads */ 40 if ((Prcb->DpcData[0].DpcQueueDepth) || 41 (Prcb->TimerRequest) || 42 (Prcb->DeferredReadyListHead.Next)) 43 { 44 /* Retire DPCs while under the DPC stack */ 45 KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack); 46 } 47 48 /* Enable interrupts */ 49 _enable(); 50 51 /* Check for quantum end */ 52 if (Prcb->QuantumEnd) 53 { 54 /* Handle quantum end */ 55 Prcb->QuantumEnd = FALSE; 56 KiQuantumEnd(); 57 } 58 else if (Prcb->NextThread) 59 { 60 /* Acquire the PRCB lock */ 61 KiAcquirePrcbLock(Prcb); 62 63 /* Capture current thread data */ 64 OldThread = Prcb->CurrentThread; 65 NewThread = Prcb->NextThread; 66 67 /* Set new thread data */ 68 Prcb->NextThread = NULL; 69 Prcb->CurrentThread = NewThread; 70 71 /* The thread is now running */ 72 NewThread->State = Running; 73 OldThread->WaitReason = WrDispatchInt; 74 75 /* Make the old thread ready */ 76 KxQueueReadyThread(OldThread, Prcb); 77 78 /* Swap to the new thread */ 79 KiSwapContext(APC_LEVEL, OldThread); 80 } 81 82 /* Disable interrupts and go back to old irql */ 83 _disable(); 84 KeLowerIrql(OldIrql); 85 } 86 87 VOID 88 KiNmiInterruptHandler( 89 _In_ PKTRAP_FRAME TrapFrame, 90 _In_ PKEXCEPTION_FRAME ExceptionFrame) 91 { 92 BOOLEAN ManualSwapGs = FALSE; 93 94 /* Check if the NMI came from kernel mode */ 95 if ((TrapFrame->SegCs & MODE_MASK) == 0) 96 { 97 /* Check if GS base is already kernel mode. This is needed, because 98 we might be interrupted during an interrupt/exception from user-mode 99 before the swapgs instruction. */ 100 if ((LONG64)__readmsr(MSR_GS_BASE) >= 0) 101 { 102 /* Swap GS to kernel */ 103 __swapgs(); 104 ManualSwapGs = TRUE; 105 } 106 } 107 108 /* Check if this is a freeze */ 109 if (KiProcessorFreezeHandler(TrapFrame, ExceptionFrame)) 110 { 111 /* NMI was handled */ 112 goto Exit; 113 } 114 115 /* Handle the NMI */ 116 KiHandleNmi(); 117 118 Exit: 119 /* Check if we need to swap GS back */ 120 if (ManualSwapGs) 121 { 122 /* Swap GS back to user */ 123 __swapgs(); 124 } 125 } 126 127 #define MAX_SYSCALL_PARAMS 16 128 129 NTSTATUS 130 NtSyscallFailure(void) 131 { 132 /* This is the failure function */ 133 return (NTSTATUS)KeGetCurrentThread()->TrapFrame->Rax; 134 } 135 136 PVOID 137 KiSystemCallHandler( 138 VOID) 139 { 140 PKTRAP_FRAME TrapFrame; 141 PKSERVICE_TABLE_DESCRIPTOR DescriptorTable; 142 PKTHREAD Thread; 143 PULONG64 KernelParams, UserParams; 144 ULONG ServiceNumber, TableIndex, Count; 145 ULONG64 UserRsp; 146 147 /* Get a pointer to the trap frame */ 148 TrapFrame = (PKTRAP_FRAME)((PULONG64)_AddressOfReturnAddress() + 1 + MAX_SYSCALL_PARAMS); 149 150 /* Increase system call count */ 151 __addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1); 152 153 /* Get the current thread */ 154 Thread = KeGetCurrentThread(); 155 156 /* Set previous mode */ 157 Thread->PreviousMode = TrapFrame->PreviousMode = UserMode; 158 159 /* We don't have an exception frame yet */ 160 TrapFrame->ExceptionFrame = 0; 161 162 /* Before enabling interrupts get the user rsp from the KPCR */ 163 UserRsp = __readgsqword(FIELD_OFFSET(KIPCR, UserRsp)); 164 TrapFrame->Rsp = UserRsp; 165 166 /* Enable interrupts */ 167 _enable(); 168 169 /* If the usermode rsp was not a usermode address, prepare an exception */ 170 if (UserRsp > MmUserProbeAddress) UserRsp = MmUserProbeAddress; 171 172 /* Get the address of the usermode and kernelmode parameters */ 173 UserParams = (PULONG64)UserRsp + 1; 174 KernelParams = (PULONG64)TrapFrame - MAX_SYSCALL_PARAMS; 175 176 /* Get the system call number from the trap frame and decode it */ 177 ServiceNumber = (ULONG)TrapFrame->Rax; 178 TableIndex = (ServiceNumber >> TABLE_OFFSET_BITS) & ((1 << TABLE_NUMBER_BITS) - 1); 179 ServiceNumber &= SERVICE_NUMBER_MASK; 180 181 /* Check for win32k system calls */ 182 if (TableIndex == WIN32K_SERVICE_INDEX) 183 { 184 ULONG GdiBatchCount; 185 186 /* Read the GDI batch count from the TEB */ 187 _SEH2_TRY 188 { 189 GdiBatchCount = NtCurrentTeb()->GdiBatchCount; 190 } 191 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 192 { 193 GdiBatchCount = 0; 194 } 195 _SEH2_END; 196 197 /* Flush batch, if there are entries */ 198 if (GdiBatchCount != 0) 199 { 200 KeGdiFlushUserBatch(); 201 } 202 } 203 204 /* Get descriptor table */ 205 DescriptorTable = &((PKSERVICE_TABLE_DESCRIPTOR)Thread->ServiceTable)[TableIndex]; 206 207 /* Validate the system call number */ 208 if (ServiceNumber >= DescriptorTable->Limit) 209 { 210 /* Check if this is a GUI call and this is not a GUI thread yet */ 211 if ((TableIndex == WIN32K_SERVICE_INDEX) && 212 (Thread->ServiceTable == KeServiceDescriptorTable)) 213 { 214 /* Convert this thread to a GUI thread. 215 It is invalid to change the stack in the middle of a C function, 216 therefore we return KiConvertToGuiThread to the system call entry 217 point, which then calls the asm function KiConvertToGuiThread, 218 which allocates a new stack, switches to it, calls 219 PsConvertToGuiThread and resumes in the middle of 220 KiSystemCallEntry64 to restart the system call handling. 221 If converting fails, the system call returns a failure code. */ 222 return (PVOID)KiConvertToGuiThread; 223 } 224 225 /* Fail the call */ 226 TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE; 227 return (PVOID)NtSyscallFailure; 228 } 229 230 /* Get stack bytes and calculate argument count */ 231 Count = DescriptorTable->Number[ServiceNumber] / 8; 232 233 _SEH2_TRY 234 { 235 switch (Count) 236 { 237 case 16: KernelParams[15] = UserParams[15]; 238 case 15: KernelParams[14] = UserParams[14]; 239 case 14: KernelParams[13] = UserParams[13]; 240 case 13: KernelParams[12] = UserParams[12]; 241 case 12: KernelParams[11] = UserParams[11]; 242 case 11: KernelParams[10] = UserParams[10]; 243 case 10: KernelParams[9] = UserParams[9]; 244 case 9: KernelParams[8] = UserParams[8]; 245 case 8: KernelParams[7] = UserParams[7]; 246 case 7: KernelParams[6] = UserParams[6]; 247 case 6: KernelParams[5] = UserParams[5]; 248 case 5: KernelParams[4] = UserParams[4]; 249 case 4: 250 case 3: 251 case 2: 252 case 1: 253 case 0: 254 break; 255 256 default: 257 ASSERT(FALSE); 258 break; 259 } 260 } 261 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 262 { 263 TrapFrame->Rax = _SEH2_GetExceptionCode(); 264 return (PVOID)NtSyscallFailure; 265 } 266 _SEH2_END; 267 268 return (PVOID)DescriptorTable->Base[ServiceNumber]; 269 } 270 271 272 // FIXME: we need to 273 VOID 274 KiSystemService(IN PKTHREAD Thread, 275 IN PKTRAP_FRAME TrapFrame, 276 IN ULONG Instruction) 277 { 278 UNIMPLEMENTED; 279 __debugbreak(); 280 } 281 282