1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Kernel-Mode Test Suite Deferred Procedure Call test
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #include <kmt_test.h>
9 
10 //#define NDEBUG
11 #include <debug.h>
12 
13 /* TODO: DPC importance */
14 
15 static volatile LONG DpcCount;
16 static volatile UCHAR DpcImportance;
17 
18 static KDEFERRED_ROUTINE DpcHandler;
19 
20 static
21 VOID
22 NTAPI
DpcHandler(IN PRKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)23 DpcHandler(
24     IN PRKDPC Dpc,
25     IN PVOID DeferredContext,
26     IN PVOID SystemArgument1,
27     IN PVOID SystemArgument2)
28 {
29     PKPRCB Prcb = KeGetCurrentPrcb();
30 
31     ok_irql(DISPATCH_LEVEL);
32     InterlockedIncrement(&DpcCount);
33     ok(DeferredContext == Dpc, "DeferredContext = %p, Dpc = %p, expected equal\n", DeferredContext, Dpc);
34     ok_eq_pointer(SystemArgument1, (PVOID)0xabc123);
35     ok_eq_pointer(SystemArgument2, (PVOID)0x5678);
36 
37     /* KDPC object contents */
38     ok_eq_uint(Dpc->Type, DpcObject);
39     ok_eq_uint(Dpc->Importance, DpcImportance);
40     ok_eq_uint(Dpc->Number, 0);
41     ok(Dpc->DpcListEntry.Blink != NULL, "\n");
42     ok(Dpc->DpcListEntry.Blink != &Dpc->DpcListEntry, "\n");
43     if (!skip(Dpc->DpcListEntry.Blink != NULL, "DpcListEntry.Blink == NULL\n"))
44         ok_eq_pointer(Dpc->DpcListEntry.Flink, Dpc->DpcListEntry.Blink->Flink);
45 
46     ok(Dpc->DpcListEntry.Flink != NULL, "\n");
47     ok(Dpc->DpcListEntry.Flink != &Dpc->DpcListEntry, "\n");
48     if (!skip(Dpc->DpcListEntry.Flink != NULL, "DpcListEntry.Flink == NULL\n"))
49         ok_eq_pointer(Dpc->DpcListEntry.Blink, Dpc->DpcListEntry.Flink->Blink);
50 
51     ok_eq_pointer(Dpc->DeferredRoutine, DpcHandler);
52     ok_eq_pointer(Dpc->DeferredContext, DeferredContext);
53     ok_eq_pointer(Dpc->SystemArgument1, SystemArgument1);
54     ok_eq_pointer(Dpc->SystemArgument2, SystemArgument2);
55     ok_eq_pointer(Dpc->DpcData, NULL);
56 
57     ok_eq_uint(Prcb->DpcRoutineActive, 1);
58     /* this DPC is not in the list anymore, but it was at the head! */
59     ok_eq_pointer(Prcb->DpcData[DPC_NORMAL].DpcListHead.Flink, Dpc->DpcListEntry.Flink);
60     ok_eq_pointer(Prcb->DpcData[DPC_NORMAL].DpcListHead.Blink, Dpc->DpcListEntry.Blink);
61 }
62 
START_TEST(KeDpc)63 START_TEST(KeDpc)
64 {
65     NTSTATUS Status = STATUS_SUCCESS;
66     KDPC Dpc;
67     KIRQL Irql, Irql2, Irql3;
68     LONG ExpectedDpcCount = 0;
69     BOOLEAN Ret;
70     int i;
71 
72     DpcCount = 0;
73     DpcImportance = MediumImportance;
74 
75 #define ok_dpccount() ok(DpcCount == ExpectedDpcCount, "DpcCount = %ld, expected %ld\n", DpcCount, ExpectedDpcCount);
76     trace("Dpc = %p\n", &Dpc);
77     memset(&Dpc, 0x55, sizeof Dpc);
78     KeInitializeDpc(&Dpc, DpcHandler, &Dpc);
79     /* check the Dpc object's fields */
80     ok_eq_uint(Dpc.Type, DpcObject);
81     ok_eq_uint(Dpc.Importance, DpcImportance);
82     ok_eq_uint(Dpc.Number, 0);
83     ok_eq_pointer(Dpc.DpcListEntry.Flink, (LIST_ENTRY *)0x5555555555555555LL);
84     ok_eq_pointer(Dpc.DpcListEntry.Blink, (LIST_ENTRY *)0x5555555555555555LL);
85     ok_eq_pointer(Dpc.DeferredRoutine, DpcHandler);
86     ok_eq_pointer(Dpc.DeferredContext, &Dpc);
87     ok_eq_pointer(Dpc.SystemArgument1, (PVOID)0x5555555555555555LL);
88     ok_eq_pointer(Dpc.SystemArgument2, (PVOID)0x5555555555555555LL);
89     ok_eq_pointer(Dpc.DpcData, NULL);
90 
91     /* simply run the Dpc a few times */
92     for (i = 0; i < 5; ++i)
93     {
94         ok_dpccount();
95         Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xabc123, (PVOID)0x5678);
96         ok_bool_true(Ret, "KeInsertQueueDpc returned");
97         ++ExpectedDpcCount;
98         ok_dpccount();
99     }
100 
101     /* insert into queue at high irql
102      * -> should only run when lowered to APC_LEVEL,
103      *    inserting a second time should fail
104      */
105     KeRaiseIrql(APC_LEVEL, &Irql);
106     for (i = 0; i < 5; ++i)
107     {
108         KeRaiseIrql(DISPATCH_LEVEL, &Irql2);
109           ok_dpccount();
110           Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xabc123, (PVOID)0x5678);
111           ok_bool_true(Ret, "KeInsertQueueDpc returned");
112           Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xdef, (PVOID)0x123);
113           ok_bool_false(Ret, "KeInsertQueueDpc returned");
114           ok_dpccount();
115           KeRaiseIrql(HIGH_LEVEL, &Irql3);
116             ok_dpccount();
117           KeLowerIrql(Irql3);
118           ok_dpccount();
119         KeLowerIrql(Irql2);
120         ++ExpectedDpcCount;
121         ok_dpccount();
122     }
123     KeLowerIrql(Irql);
124 
125     /* now test removing from the queue */
126     KeRaiseIrql(APC_LEVEL, &Irql);
127     for (i = 0; i < 5; ++i)
128     {
129         KeRaiseIrql(DISPATCH_LEVEL, &Irql2);
130           ok_dpccount();
131           Ret = KeRemoveQueueDpc(&Dpc);
132           ok_bool_false(Ret, "KeRemoveQueueDpc returned");
133           Ret = KeInsertQueueDpc(&Dpc, (PVOID)0xabc123, (PVOID)0x5678);
134           ok_bool_true(Ret, "KeInsertQueueDpc returned");
135           ok_dpccount();
136           KeRaiseIrql(HIGH_LEVEL, &Irql3);
137             ok_dpccount();
138           KeLowerIrql(Irql3);
139           ok_dpccount();
140           Ret = KeRemoveQueueDpc(&Dpc);
141           ok_bool_true(Ret, "KeRemoveQueueDpc returned");
142         KeLowerIrql(Irql2);
143         ok_dpccount();
144     }
145     KeLowerIrql(Irql);
146 
147     /* parameter checks */
148     Status = STATUS_SUCCESS;
149     _SEH2_TRY {
150         KeInitializeDpc(&Dpc, NULL, NULL);
151     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
152         Status = _SEH2_GetExceptionCode();
153     } _SEH2_END;
154     ok_eq_hex(Status, STATUS_SUCCESS);
155 
156     if (!skip(Status == STATUS_SUCCESS, "KeInitializeDpc failed\n"))
157     {
158         KeRaiseIrql(HIGH_LEVEL, &Irql);
159           Ret = KeInsertQueueDpc(&Dpc, NULL, NULL);
160           ok_bool_true(Ret, "KeInsertQueueDpc returned");
161           Ret = KeRemoveQueueDpc(&Dpc);
162           ok_bool_true(Ret, "KeRemoveQueueDpc returned");
163         KeLowerIrql(Irql);
164     }
165 
166     Status = STATUS_SUCCESS;
167     _SEH2_TRY {
168         KeInitializeDpc(NULL, NULL, NULL);
169     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
170         Status = _SEH2_GetExceptionCode();
171     } _SEH2_END;
172     ok_eq_hex(Status, STATUS_ACCESS_VIOLATION);
173 
174     /* These result in IRQL_NOT_LESS_OR_EQUAL on 2k3 -- IRQLs 0x1f and 0xff (?)
175     Ret = KeInsertQueueDpc(NULL, NULL, NULL);
176     Ret = KeRemoveQueueDpc(NULL);*/
177 
178     ok_dpccount();
179     ok_irql(PASSIVE_LEVEL);
180     trace("Final Dpc count: %ld, expected %ld\n", DpcCount, ExpectedDpcCount);
181 }
182