1 #include <debug.h>
2 #include <lwip/sys.h>
3 
4 #include "lwip_glue.h"
5 
6 static LIST_ENTRY ThreadListHead;
7 static KSPIN_LOCK ThreadListLock;
8 
9 KEVENT TerminationEvent;
10 NPAGED_LOOKASIDE_LIST MessageLookasideList;
11 NPAGED_LOOKASIDE_LIST QueueEntryLookasideList;
12 
13 static LARGE_INTEGER StartTime;
14 
15 typedef struct _thread_t
16 {
17     HANDLE Handle;
18     void (* ThreadFunction)(void *arg);
19     void *ThreadContext;
20     LIST_ENTRY ListEntry;
21 } *thread_t;
22 
23 u32_t sys_now(void)
24 {
25     LARGE_INTEGER CurrentTime;
26 
27     KeQuerySystemTime(&CurrentTime);
28 
29     return (CurrentTime.QuadPart - StartTime.QuadPart) / 10000;
30 }
31 
32 void
33 sys_arch_protect(sys_prot_t *lev)
34 {
35     /* Preempt the dispatcher */
36     KeRaiseIrql(DISPATCH_LEVEL, lev);
37 }
38 
39 void
40 sys_arch_unprotect(sys_prot_t lev)
41 {
42     KeLowerIrql(lev);
43 }
44 
45 err_t
46 sys_sem_new(sys_sem_t *sem, u8_t count)
47 {
48     ASSERT(count == 0 || count == 1);
49 
50     /* It seems lwIP uses the semaphore implementation as either a completion event or a lock
51      * so I optimize for this case by using a synchronization event and setting its initial state
52      * to signalled for a lock and non-signalled for a completion event */
53 
54     KeInitializeEvent(&sem->Event, SynchronizationEvent, count);
55 
56     sem->Valid = 1;
57 
58     return ERR_OK;
59 }
60 
61 int sys_sem_valid(sys_sem_t *sem)
62 {
63     return sem->Valid;
64 }
65 
66 void sys_sem_set_invalid(sys_sem_t *sem)
67 {
68     sem->Valid = 0;
69 }
70 
71 void
72 sys_sem_free(sys_sem_t* sem)
73 {
74     /* No op (allocated in stack) */
75 
76     sys_sem_set_invalid(sem);
77 }
78 
79 void
80 sys_sem_signal(sys_sem_t* sem)
81 {
82     KeSetEvent(&sem->Event, IO_NO_INCREMENT, FALSE);
83 }
84 
85 u32_t
86 sys_arch_sem_wait(sys_sem_t* sem, u32_t timeout)
87 {
88     LARGE_INTEGER LargeTimeout, PreWaitTime, PostWaitTime;
89     UINT64 TimeDiff;
90     NTSTATUS Status;
91     PVOID WaitObjects[] = {&sem->Event, &TerminationEvent};
92 
93     LargeTimeout.QuadPart = Int32x32To64(timeout, -10000);
94 
95     KeQuerySystemTime(&PreWaitTime);
96 
97     Status = KeWaitForMultipleObjects(2,
98                                       WaitObjects,
99                                       WaitAny,
100                                       Executive,
101                                       KernelMode,
102                                       FALSE,
103                                       timeout != 0 ? &LargeTimeout : NULL,
104                                       NULL);
105     if (Status == STATUS_WAIT_0)
106     {
107         KeQuerySystemTime(&PostWaitTime);
108         TimeDiff = PostWaitTime.QuadPart - PreWaitTime.QuadPart;
109         TimeDiff /= 10000;
110 
111         return TimeDiff;
112     }
113     else if (Status == STATUS_WAIT_1)
114     {
115         /* DON'T remove ourselves from the thread list! */
116         PsTerminateSystemThread(STATUS_SUCCESS);
117 
118         /* We should never get here! */
119         ASSERT(FALSE);
120 
121         return 0;
122     }
123 
124     return SYS_ARCH_TIMEOUT;
125 }
126 
127 err_t
128 sys_mbox_new(sys_mbox_t *mbox, int size)
129 {
130     KeInitializeSpinLock(&mbox->Lock);
131 
132     InitializeListHead(&mbox->ListHead);
133 
134     KeInitializeEvent(&mbox->Event, NotificationEvent, FALSE);
135 
136     mbox->Valid = 1;
137 
138     return ERR_OK;
139 }
140 
141 int sys_mbox_valid(sys_mbox_t *mbox)
142 {
143     return mbox->Valid;
144 }
145 
146 void sys_mbox_set_invalid(sys_mbox_t *mbox)
147 {
148     mbox->Valid = 0;
149 }
150 
151 void
152 sys_mbox_free(sys_mbox_t *mbox)
153 {
154     ASSERT(IsListEmpty(&mbox->ListHead));
155 
156     sys_mbox_set_invalid(mbox);
157 }
158 
159 void
160 sys_mbox_post(sys_mbox_t *mbox, void *msg)
161 {
162     PLWIP_MESSAGE_CONTAINER Container;
163 
164     Container = ExAllocatePool(NonPagedPool, sizeof(*Container));
165     ASSERT(Container);
166 
167     Container->Message = msg;
168 
169     ExInterlockedInsertTailList(&mbox->ListHead,
170                                 &Container->ListEntry,
171                                 &mbox->Lock);
172 
173     KeSetEvent(&mbox->Event, IO_NO_INCREMENT, FALSE);
174 }
175 
176 u32_t
177 sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
178 {
179     LARGE_INTEGER LargeTimeout, PreWaitTime, PostWaitTime;
180     UINT64 TimeDiff;
181     NTSTATUS Status;
182     PVOID Message;
183     PLWIP_MESSAGE_CONTAINER Container;
184     PLIST_ENTRY Entry;
185     KIRQL OldIrql;
186     PVOID WaitObjects[] = {&mbox->Event, &TerminationEvent};
187 
188     LargeTimeout.QuadPart = Int32x32To64(timeout, -10000);
189 
190     KeQuerySystemTime(&PreWaitTime);
191 
192     Status = KeWaitForMultipleObjects(2,
193                                       WaitObjects,
194                                       WaitAny,
195                                       Executive,
196                                       KernelMode,
197                                       FALSE,
198                                       timeout != 0 ? &LargeTimeout : NULL,
199                                       NULL);
200 
201     if (Status == STATUS_WAIT_0)
202     {
203         KeAcquireSpinLock(&mbox->Lock, &OldIrql);
204         Entry = RemoveHeadList(&mbox->ListHead);
205         ASSERT(Entry);
206         if (IsListEmpty(&mbox->ListHead))
207             KeClearEvent(&mbox->Event);
208         KeReleaseSpinLock(&mbox->Lock, OldIrql);
209 
210         Container = CONTAINING_RECORD(Entry, LWIP_MESSAGE_CONTAINER, ListEntry);
211         Message = Container->Message;
212         ExFreePool(Container);
213 
214         if (msg)
215             *msg = Message;
216 
217         KeQuerySystemTime(&PostWaitTime);
218         TimeDiff = PostWaitTime.QuadPart - PreWaitTime.QuadPart;
219         TimeDiff /= 10000;
220 
221         return TimeDiff;
222     }
223     else if (Status == STATUS_WAIT_1)
224     {
225         /* DON'T remove ourselves from the thread list! */
226         PsTerminateSystemThread(STATUS_SUCCESS);
227 
228         /* We should never get here! */
229         ASSERT(FALSE);
230 
231         return 0;
232     }
233 
234     return SYS_ARCH_TIMEOUT;
235 }
236 
237 u32_t
238 sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
239 {
240     if (sys_arch_mbox_fetch(mbox, msg, 1) != SYS_ARCH_TIMEOUT)
241         return 0;
242     else
243         return SYS_MBOX_EMPTY;
244 }
245 
246 err_t
247 sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
248 {
249     sys_mbox_post(mbox, msg);
250 
251     return ERR_OK;
252 }
253 
254 VOID
255 NTAPI
256 LwipThreadMain(PVOID Context)
257 {
258     thread_t Container = (thread_t)Context;
259     KIRQL OldIrql;
260 
261     ExInterlockedInsertHeadList(&ThreadListHead, &Container->ListEntry, &ThreadListLock);
262 
263     Container->ThreadFunction(Container->ThreadContext);
264 
265     KeAcquireSpinLock(&ThreadListLock, &OldIrql);
266     RemoveEntryList(&Container->ListEntry);
267     KeReleaseSpinLock(&ThreadListLock, OldIrql);
268 
269     ExFreePool(Container);
270 
271     PsTerminateSystemThread(STATUS_SUCCESS);
272 }
273 
274 sys_thread_t
275 sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
276 {
277     thread_t Container;
278     NTSTATUS Status;
279 
280     Container = ExAllocatePool(NonPagedPool, sizeof(*Container));
281     if (!Container)
282         return 0;
283 
284     Container->ThreadFunction = thread;
285     Container->ThreadContext = arg;
286 
287     Status = PsCreateSystemThread(&Container->Handle,
288                                   THREAD_ALL_ACCESS,
289                                   NULL,
290                                   NULL,
291                                   NULL,
292                                   LwipThreadMain,
293                                   Container);
294 
295     if (!NT_SUCCESS(Status))
296     {
297         ExFreePool(Container);
298         return 0;
299     }
300 
301     return 0;
302 }
303 
304 void
305 sys_init(void)
306 {
307     KeInitializeSpinLock(&ThreadListLock);
308     InitializeListHead(&ThreadListHead);
309 
310     KeQuerySystemTime(&StartTime);
311 
312     KeInitializeEvent(&TerminationEvent, NotificationEvent, FALSE);
313 
314     ExInitializeNPagedLookasideList(&MessageLookasideList,
315                                     NULL,
316                                     NULL,
317                                     0,
318                                     sizeof(struct lwip_callback_msg),
319                                     LWIP_MESSAGE_TAG,
320                                     0);
321 
322     ExInitializeNPagedLookasideList(&QueueEntryLookasideList,
323                                     NULL,
324                                     NULL,
325                                     0,
326                                     sizeof(QUEUE_ENTRY),
327                                     LWIP_QUEUE_TAG,
328                                     0);
329 }
330 
331 void
332 sys_shutdown(void)
333 {
334     PLIST_ENTRY CurrentEntry;
335     thread_t Container;
336 
337     /* Set the termination event */
338     KeSetEvent(&TerminationEvent, IO_NO_INCREMENT, FALSE);
339 
340     /* Loop through the thread list and wait for each to die */
341     while ((CurrentEntry = ExInterlockedRemoveHeadList(&ThreadListHead, &ThreadListLock)))
342     {
343         Container = CONTAINING_RECORD(CurrentEntry, struct _thread_t, ListEntry);
344 
345         if (Container->ThreadFunction)
346         {
347             KeWaitForSingleObject(Container->Handle,
348                                   Executive,
349                                   KernelMode,
350                                   FALSE,
351                                   NULL);
352 
353             ZwClose(Container->Handle);
354         }
355     }
356 
357     ExDeleteNPagedLookasideList(&MessageLookasideList);
358     ExDeleteNPagedLookasideList(&QueueEntryLookasideList);
359 }
360