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
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 
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