[disk,device] Move BootDrive as Boot property
[people/sha0/winvblock.git] / src / aoe / driver.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * AoE specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30
31 #include "winvblock.h"
32 #include "wv_stdlib.h"
33 #include "wv_string.h"
34 #include "portable.h"
35 #include "driver.h"
36 #include "device.h"
37 #include "disk.h"
38 #include "mount.h"
39 #include "bus.h"
40 #include "aoe.h"
41 #include "registry.h"
42 #include "protocol.h"
43 #include "debug.h"
44
45 #define AOEPROTOCOLVER 1
46
47 extern NTSTATUS STDCALL ZwWaitForSingleObject(
48     IN HANDLE Handle,
49     IN winvblock__bool Alertable,
50     IN PLARGE_INTEGER Timeout OPTIONAL
51   );
52
53 /* From aoe/bus.c */
54 extern WV_S_BUS_T AoeBusMain;
55 extern winvblock__bool AoeBusCreate(void);
56 extern void AoeBusFree(void);
57 /* From aoe/registry.c */
58 extern winvblock__bool STDCALL AoeRegSetup(OUT PNTSTATUS);
59
60 /* Forward declarations. */
61 struct AOE_DISK_;
62 static void STDCALL AoeThread_(IN void *);
63 static void AoeProcessAbft_(void);
64 static void STDCALL AoeUnload_(IN PDRIVER_OBJECT);
65 static struct AOE_DISK_ * AoeDiskCreate_(void);
66 static WV_F_DEV_FREE AoeDiskFree_;
67 static WV_F_DISK_IO AoeDiskIo_;
68 static WV_F_DISK_MAX_XFER_LEN AoeDiskMaxXferLen_;
69 static WV_F_DISK_INIT AoeDiskInit_;
70 static WV_F_DISK_CLOSE AoeDiskClose_;
71
72 /** Tag types. */
73 typedef enum AOE_TAG_TYPE_ {
74     AoeTagTypeIo_,
75     AoeTagTypeSearchDrive_,
76     AoeTagTypes_
77   } AOE_E_TAG_TYPE_, * AOE_EP_TAG_TYPE_;
78
79 #ifdef _MSC_VER
80 #  pragma pack(1)
81 #endif
82 /** AoE packet. */
83 struct AOE_PACKET_ {
84     winvblock__uint8 ReservedFlag:2;
85     winvblock__uint8 ErrorFlag:1;
86     winvblock__uint8 ResponseFlag:1;
87     winvblock__uint8 Ver:4;
88     winvblock__uint8 Error;
89     winvblock__uint16 Major;
90     winvblock__uint8 Minor;
91     winvblock__uint8 Command;
92     winvblock__uint32 Tag;
93
94     winvblock__uint8 WriteAFlag:1;
95     winvblock__uint8 AsyncAFlag:1;
96     winvblock__uint8 Reserved1AFlag:2;
97     winvblock__uint8 DeviceHeadAFlag:1;
98     winvblock__uint8 Reserved2AFlag:1;
99     winvblock__uint8 ExtendedAFlag:1;
100     winvblock__uint8 Reserved3AFlag:1;
101     union {
102         winvblock__uint8 Err;
103         winvblock__uint8 Feature;
104       };
105     winvblock__uint8 Count;
106     union {
107         winvblock__uint8 Cmd;
108         winvblock__uint8 Status;
109       };
110
111     winvblock__uint8 Lba0;
112     winvblock__uint8 Lba1;
113     winvblock__uint8 Lba2;
114     winvblock__uint8 Lba3;
115     winvblock__uint8 Lba4;
116     winvblock__uint8 Lba5;
117     winvblock__uint16 Reserved;
118
119     winvblock__uint8 Data[];
120   } __attribute__((__packed__));
121 typedef struct AOE_PACKET_ AOE_S_PACKET_, * AOE_SP_PACKET_;
122 #ifdef _MSC_VER
123 #  pragma pack()
124 #endif
125
126 /** An I/O request. */
127 typedef struct AOE_IO_REQ_ {
128     WV_E_DISK_IO_MODE Mode;
129     winvblock__uint32 SectorCount;
130     winvblock__uint8_ptr Buffer;
131     PIRP Irp;
132     winvblock__uint32 TagCount;
133     winvblock__uint32 TotalTags;
134   } AOE_S_IO_REQ_, * AOE_SP_IO_REQ_;
135
136 /** A work item "tag". */
137 typedef struct AOE_WORK_TAG_ {
138     AOE_E_TAG_TYPE_ type;
139     WV_SP_DEV_T device;
140     AOE_SP_IO_REQ_ request_ptr;
141     winvblock__uint32 Id;
142     AOE_SP_PACKET_ packet_data;
143     winvblock__uint32 PacketSize;
144     LARGE_INTEGER FirstSendTime;
145     LARGE_INTEGER SendTime;
146     winvblock__uint32 BufferOffset;
147     winvblock__uint32 SectorCount;
148     struct AOE_WORK_TAG_ * next;
149     struct AOE_WORK_TAG_ * previous;
150   } AOE_S_WORK_TAG_, * AOE_SP_WORK_TAG_;
151
152 /** A disk search. */
153 typedef struct AOE_DISK_SEARCH_ {
154     WV_SP_DEV_T device;
155     AOE_SP_WORK_TAG_ tag;
156     struct AOE_DISK_SEARCH_ * next;
157   } AOE_S_DISK_SEARCH_, * AOE_SP_DISK_SEARCH_;
158
159 typedef enum AOE_SEARCH_STATE_ {
160     AoeSearchStateSearchNic_,
161     AoeSearchStateGetSize_,
162     AoeSearchStateGettingSize_,
163     AoeSearchStateGetGeometry_,
164     AoeSearchStateGettingGeometry_,
165     AoeSearchStateGetMaxSectsPerPacket_,
166     AoeSearchStateGettingMaxSectsPerPacket_,
167     AoeSearchStateDone_,
168     AoeSearchStates
169   } AOE_E_SEARCH_STATE_, * AOE_EP_SEARCH_STATE_;
170
171 /** The AoE disk type. */
172 typedef struct AOE_DISK_ {
173     WV_SP_DISK_T disk;
174     winvblock__uint32 MTU;
175     winvblock__uint8 ClientMac[6];
176     winvblock__uint8 ServerMac[6];
177     winvblock__uint32 Major;
178     winvblock__uint32 Minor;
179     winvblock__uint32 MaxSectorsPerPacket;
180     winvblock__uint32 Timeout;
181     AOE_E_SEARCH_STATE_ search_state;
182     WV_FP_DEV_FREE prev_free;
183     LIST_ENTRY tracking;
184   } AOE_S_DISK_, * AOE_SP_DISK_;
185
186 typedef struct AOE_TARGET_LIST_ {
187     aoe__mount_target Target;
188     struct AOE_TARGET_LIST_ * next;
189   } AOE_S_TARGET_LIST_, * AOE_SP_TARGET_LIST_;
190
191 /** Private globals. */
192 static AOE_SP_TARGET_LIST_ AoeTargetList_ = NULL;
193 static KSPIN_LOCK AoeTargetListLock_;
194 static winvblock__bool AoeStop_ = FALSE;
195 static KSPIN_LOCK AoeLock_;
196 static KEVENT AoeSignal_;
197 static AOE_SP_WORK_TAG_ AoeTagListFirst_ = NULL;
198 static AOE_SP_WORK_TAG_ AoeTagListLast_ = NULL;
199 static AOE_SP_WORK_TAG_ AoeProbeTag_ = NULL;
200 static AOE_SP_DISK_SEARCH_ AoeDiskSearchList_ = NULL;
201 static LONG AoePendingTags_ = 0;
202 static HANDLE AoeThreadHandle_;
203 static winvblock__bool AoeStarted_ = FALSE;
204 static LIST_ENTRY AoeDiskList_;
205 static KSPIN_LOCK AoeDiskListLock_;
206
207 /* Yield a pointer to the AoE disk. */
208 static AOE_SP_DISK_ AoeDiskFromDev_(WV_SP_DEV_T dev_ptr) {
209     return disk__get_ptr(dev_ptr)->ext;
210   }
211
212 /**
213  * Start AoE operations.
214  *
215  * @ret Status          Return status code.
216  */
217 NTSTATUS STDCALL DriverEntry(
218     IN PDRIVER_OBJECT DriverObject,
219     IN PUNICODE_STRING RegistryPath
220   ) {
221     NTSTATUS Status;
222     OBJECT_ATTRIBUTES ObjectAttributes;
223     void * ThreadObject;
224     WV_SP_BUS_T bus_ptr;
225
226     DBG("Entry\n");
227
228     if (AoeStarted_)
229       return STATUS_SUCCESS;
230     /* Initialize the global list of AoE disks. */
231     InitializeListHead(&AoeDiskList_);
232     KeInitializeSpinLock(&AoeDiskListLock_);
233     /* Setup the Registry. */
234     if (!NT_SUCCESS(AoeRegSetup(&Status))) {
235         DBG("Could not update Registry!\n");
236         return Status;
237       } else {
238         DBG("Registry updated\n");
239       }
240     /* Start up the protocol. */
241     if (!NT_SUCCESS(Status = Protocol_Start())) {
242         DBG("Protocol startup failure!\n");
243         return Status;
244       }
245     /* Allocate and zero-fill the global probe tag. */
246     AoeProbeTag_ = wv_mallocz(sizeof *AoeProbeTag_);
247     if (AoeProbeTag_ == NULL) {
248         DBG("Couldn't allocate probe tag; bye!\n");
249         return STATUS_INSUFFICIENT_RESOURCES;
250       }
251
252     /* Set up the probe tag's AoE packet reference. */
253     AoeProbeTag_->PacketSize = sizeof (AOE_S_PACKET_);
254     /* Allocate and zero-fill the probe tag's packet reference. */
255     AoeProbeTag_->packet_data = wv_mallocz(AoeProbeTag_->PacketSize);
256     if (AoeProbeTag_->packet_data == NULL) {
257         DBG("Couldn't allocate AoeProbeTag_->packet_data\n");
258         wv_free(AoeProbeTag_);
259         return STATUS_INSUFFICIENT_RESOURCES;
260       }
261     AoeProbeTag_->SendTime.QuadPart = 0LL;
262
263     /* Initialize the probe tag's AoE packet. */
264     AoeProbeTag_->packet_data->Ver = AOEPROTOCOLVER;
265     AoeProbeTag_->packet_data->Major =
266       htons((winvblock__uint16) -1);
267     AoeProbeTag_->packet_data->Minor = (winvblock__uint8) -1;
268     AoeProbeTag_->packet_data->Cmd = 0xec;           /* IDENTIFY DEVICE */
269     AoeProbeTag_->packet_data->Count = 1;
270
271     /* Initialize global target-list spinlock. */
272     KeInitializeSpinLock(&AoeTargetListLock_);
273
274     /* Initialize global spin-lock and global thread signal event. */
275     KeInitializeSpinLock(&AoeLock_);
276     KeInitializeEvent(&AoeSignal_, SynchronizationEvent, FALSE);
277
278     /* Initialize object attributes for thread. */
279     InitializeObjectAttributes(
280         &ObjectAttributes,
281         NULL,
282         OBJ_KERNEL_HANDLE,
283         NULL,
284         NULL
285       );
286
287     /* Create global thread. */
288     if (!NT_SUCCESS(Status = PsCreateSystemThread(
289         &AoeThreadHandle_,
290         THREAD_ALL_ACCESS,
291         &ObjectAttributes,
292         NULL,
293         NULL,
294         AoeThread_,
295         NULL
296       )))
297       return Error("PsCreateSystemThread", Status);
298
299     if (!NT_SUCCESS(Status = ObReferenceObjectByHandle(
300         AoeThreadHandle_,
301         THREAD_ALL_ACCESS,
302         NULL,
303         KernelMode,
304         &ThreadObject,
305         NULL
306       ))) {
307         ZwClose(AoeThreadHandle_);
308         Error("ObReferenceObjectByHandle", Status);
309         AoeStop_ = TRUE;
310         KeSetEvent(&AoeSignal_, 0, FALSE);
311       }
312
313     DriverObject->DriverUnload = AoeUnload_;
314     AoeStarted_ = TRUE;
315     if (!AoeBusCreate()) {
316         DBG("Unable to create AoE bus!\n");
317         AoeUnload_(DriverObject);
318         return STATUS_INSUFFICIENT_RESOURCES;
319       }
320     AoeProcessAbft_();
321     DBG("Exit\n");
322     return Status;
323   }
324
325 /**
326  * Stop AoE operations.
327  */
328 static void STDCALL AoeUnload_(IN PDRIVER_OBJECT DriverObject) {
329     NTSTATUS Status;
330     AOE_SP_DISK_SEARCH_ disk_searcher, previous_disk_searcher;
331     AOE_SP_WORK_TAG_ tag;
332     KIRQL Irql, Irql2;
333     AOE_SP_TARGET_LIST_ Walker, Next;
334
335     DBG("Entry\n");
336     /* If we're not already started, there's nothing to do. */
337     if (!AoeStarted_)
338       return;
339     /* Destroy the AoE bus. */
340     AoeBusFree();
341     /* Stop the AoE protocol. */
342     Protocol_Stop();
343     /* If we're not already shutting down, signal the event. */
344     if (!AoeStop_) {
345         AoeStop_ = TRUE;
346         KeSetEvent(&AoeSignal_, 0, FALSE);
347         /* Wait until the event has been signalled. */
348         if (!NT_SUCCESS(Status = ZwWaitForSingleObject(
349             AoeThreadHandle_,
350             FALSE,
351             NULL
352           )))
353           Error("AoE_Stop ZwWaitForSingleObject", Status);
354         ZwClose(AoeThreadHandle_);
355       }
356
357     /* Free the target list. */
358     KeAcquireSpinLock(&AoeTargetListLock_, &Irql2);
359     Walker = AoeTargetList_;
360     while (Walker != NULL) {
361         Next = Walker->next;
362         wv_free(Walker);
363         Walker = Next;
364       }
365     KeReleaseSpinLock(&AoeTargetListLock_, Irql2);
366
367     /* Wait until we have the global spin-lock. */
368     KeAcquireSpinLock(&AoeLock_, &Irql);
369
370     /* Free disk searches in the global disk search list. */
371     disk_searcher = AoeDiskSearchList_;
372     while (disk_searcher != NULL) {
373         KeSetEvent(
374             &(disk__get_ptr(disk_searcher->device)->SearchEvent),
375             0,
376             FALSE
377           );
378         previous_disk_searcher = disk_searcher;
379         disk_searcher = disk_searcher->next;
380         wv_free(previous_disk_searcher);
381       }
382
383     /* Cancel and free all tags in the global tag list. */
384     tag = AoeTagListFirst_;
385     while (tag != NULL) {
386         if (tag->request_ptr != NULL && --tag->request_ptr->TagCount == 0) {
387             tag->request_ptr->Irp->IoStatus.Information = 0;
388             tag->request_ptr->Irp->IoStatus.Status = STATUS_CANCELLED;
389             IoCompleteRequest(tag->request_ptr->Irp, IO_NO_INCREMENT);
390             wv_free(tag->request_ptr);
391           }
392         if (tag->next == NULL) {
393             wv_free(tag->packet_data);
394             wv_free(tag);
395             tag = NULL;
396           } else {
397             tag = tag->next;
398             wv_free(tag->previous->packet_data);
399             wv_free(tag->previous);
400           }
401       }
402     AoeTagListFirst_ = NULL;
403     AoeTagListLast_ = NULL;
404
405     /* Free the global probe tag and its AoE packet. */
406     wv_free(AoeProbeTag_->packet_data);
407     wv_free(AoeProbeTag_);
408
409     /* Release the global spin-lock. */
410     KeReleaseSpinLock(&AoeLock_, Irql);
411     AoeBusFree();
412     AoeStarted_ = FALSE;
413     DBG("Exit\n");
414   }
415
416 /**
417  * Search for disk parameters.
418  *
419  * @v disk_ptr          The disk to initialize (for AoE, match).
420  * @ret BOOLEAN         See below.
421  *
422  * Returns TRUE if the disk could be matched, FALSE otherwise.
423  */
424 static winvblock__bool STDCALL AoeDiskInit_(IN WV_SP_DISK_T disk_ptr) {
425     AOE_SP_DISK_SEARCH_
426       disk_searcher, disk_search_walker, previous_disk_searcher;
427     LARGE_INTEGER Timeout, CurrentTime;
428     AOE_SP_WORK_TAG_ tag, tag_walker;
429     KIRQL Irql, InnerIrql;
430     LARGE_INTEGER MaxSectorsPerPacketSendTime;
431     winvblock__uint32 MTU;
432     AOE_SP_DISK_ aoe_disk_ptr;
433
434     aoe_disk_ptr = AoeDiskFromDev_(disk_ptr->Dev);
435     /*
436      * Allocate our disk search 
437      */
438     if ((disk_searcher = wv_malloc(sizeof *disk_searcher)) == NULL) {
439         DBG ( "Couldn't allocate for disk_searcher; bye!\n" );
440         return FALSE;
441       }
442
443     /*
444      * Initialize the disk search 
445      */
446     disk_searcher->device = disk_ptr->Dev;
447     disk_searcher->next = NULL;
448     aoe_disk_ptr->search_state = AoeSearchStateSearchNic_;
449     KeResetEvent ( &disk_ptr->SearchEvent );
450
451     /*
452      * Wait until we have the global spin-lock 
453      */
454     KeAcquireSpinLock ( &AoeLock_, &Irql );
455
456     /*
457      * Add our disk search to the global list of disk searches 
458      */
459     if ( AoeDiskSearchList_ == NULL )
460       {
461         AoeDiskSearchList_ = disk_searcher;
462       }
463     else
464       {
465         disk_search_walker = AoeDiskSearchList_;
466         while ( disk_search_walker->next )
467     disk_search_walker = disk_search_walker->next;
468         disk_search_walker->next = disk_searcher;
469       }
470
471     /*
472      * Release the global spin-lock 
473      */
474     KeReleaseSpinLock ( &AoeLock_, Irql );
475
476     /*
477      * We go through all the states until the disk is ready for use 
478      */
479     while ( TRUE )
480       {
481         /*
482          * Wait for our device's extension's search to be signalled 
483          */
484         /*
485          * TODO: Make the below value a #defined constant 
486          */
487         /*
488          * 500.000 * 100ns = 50.000.000 ns = 50ms 
489          */
490         Timeout.QuadPart = -500000LL;
491         KeWaitForSingleObject ( &disk_ptr->SearchEvent, Executive, KernelMode,
492               FALSE, &Timeout );
493         if ( AoeStop_ )
494     {
495       DBG ( "AoE is shutting down; bye!\n" );
496       return FALSE;
497     }
498
499         /*
500          * Wait until we have the device extension's spin-lock 
501          */
502         KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
503
504         if (aoe_disk_ptr->search_state == AoeSearchStateSearchNic_)
505     {
506       if ( !Protocol_SearchNIC ( aoe_disk_ptr->ClientMac ) )
507         {
508           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
509           continue;
510         }
511       else
512         {
513           /*
514            * We found the adapter to use, get MTU next 
515            */
516           aoe_disk_ptr->MTU = Protocol_GetMTU ( aoe_disk_ptr->ClientMac );
517           aoe_disk_ptr->search_state = AoeSearchStateGetSize_;
518         }
519     }
520
521         if (aoe_disk_ptr->search_state == AoeSearchStateGettingSize_)
522     {
523       /*
524        * Still getting the disk's size 
525        */
526       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
527       continue;
528     }
529         if (aoe_disk_ptr->search_state == AoeSearchStateGettingGeometry_)
530     {
531       /*
532        * Still getting the disk's geometry 
533        */
534       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
535       continue;
536     }
537         if (aoe_disk_ptr->search_state ==
538           AoeSearchStateGettingMaxSectsPerPacket_)
539     {
540       KeQuerySystemTime ( &CurrentTime );
541       /*
542        * TODO: Make the below value a #defined constant 
543        */
544       /*
545        * 2.500.000 * 100ns = 250.000.000 ns = 250ms 
546        */
547       if ( CurrentTime.QuadPart >
548            MaxSectorsPerPacketSendTime.QuadPart + 2500000LL )
549         {
550           DBG ( "No reply after 250ms for MaxSectorsPerPacket %d, "
551           "giving up\n", aoe_disk_ptr->MaxSectorsPerPacket );
552           aoe_disk_ptr->MaxSectorsPerPacket--;
553           aoe_disk_ptr->search_state = AoeSearchStateDone_;
554         }
555       else
556         {
557           /*
558            * Still getting the maximum sectors per packet count 
559            */
560           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
561           continue;
562         }
563     }
564
565         if (aoe_disk_ptr->search_state == AoeSearchStateDone_)
566     {
567       /*
568        * We've finished the disk search; perform clean-up 
569        */
570       KeAcquireSpinLock ( &AoeLock_, &InnerIrql );
571
572       /*
573        * Tag clean-up: Find out if our tag is in the global tag list 
574        */
575       tag_walker = AoeTagListFirst_;
576       while ( tag_walker != NULL && tag_walker != tag )
577         tag_walker = tag_walker->next;
578       if ( tag_walker != NULL )
579         {
580           /*
581            * We found it.  If it's at the beginning of the list, adjust
582            * the list to point the the next tag
583            */
584           if ( tag->previous == NULL )
585       AoeTagListFirst_ = tag->next;
586           else
587       /*
588        * Remove our tag from the list 
589        */
590       tag->previous->next = tag->next;
591           /*
592            * If we 're at the end of the list, adjust the list's end to
593            * point to the penultimate tag
594            */
595           if ( tag->next == NULL )
596       AoeTagListLast_ = tag->previous;
597           else
598       /*
599        * Remove our tag from the list 
600        */
601       tag->next->previous = tag->previous;
602           AoePendingTags_--;
603           if ( AoePendingTags_ < 0 )
604       DBG ( "AoePendingTags_ < 0!!\n" );
605           /*
606            * Free our tag and its AoE packet 
607            */
608           wv_free(tag->packet_data);
609           wv_free(tag);
610         }
611
612       /*
613        * Disk search clean-up 
614        */
615       if ( AoeDiskSearchList_ == NULL )
616         {
617           DBG ( "AoeDiskSearchList_ == NULL!!\n" );
618         }
619       else
620         {
621           /*
622            * Find our disk search in the global list of disk searches 
623            */
624           disk_search_walker = AoeDiskSearchList_;
625           while (
626               disk_search_walker &&
627               disk_search_walker->device != disk_ptr->Dev
628             ) {
629         previous_disk_searcher = disk_search_walker;
630         disk_search_walker = disk_search_walker->next;
631       }
632           if ( disk_search_walker )
633       {
634         /*
635          * We found our disk search.  If it's the first one in
636          * the list, adjust the list and remove it
637          */
638         if ( disk_search_walker == AoeDiskSearchList_ )
639           AoeDiskSearchList_ = disk_search_walker->next;
640         else
641           /*
642            * Just remove it 
643            */
644           previous_disk_searcher->next = disk_search_walker->next;
645         /*
646          * Free our disk search 
647          */
648         wv_free(disk_search_walker);
649       }
650           else
651       {
652         DBG ( "Disk not found in AoeDiskSearchList_!!\n" );
653       }
654         }
655
656       /*
657        * Release global and device extension spin-locks 
658        */
659       KeReleaseSpinLock ( &AoeLock_, InnerIrql );
660       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
661
662       DBG ( "Disk size: %I64uM cylinders: %I64u heads: %u "
663       "sectors: %u sectors per packet: %u\n",
664       disk_ptr->LBADiskSize / 2048, disk_ptr->Cylinders,
665       disk_ptr->Heads, disk_ptr->Sectors,
666       aoe_disk_ptr->MaxSectorsPerPacket );
667       return TRUE;
668     }
669
670         #if 0
671         if ( aoe_disk_ptr->search_state == AoeSearchStateDone_)
672         #endif
673         /*
674          * Establish our tag 
675          */
676         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
677       DBG ( "Couldn't allocate tag\n" );
678       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
679       /*
680        * Maybe next time around 
681        */
682       continue;
683     }
684         tag->type = AoeTagTypeSearchDrive_;
685         tag->device = disk_ptr->Dev;
686
687         /*
688          * Establish our tag's AoE packet 
689          */
690         tag->PacketSize = sizeof (AOE_S_PACKET_);
691         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
692       DBG ( "Couldn't allocate tag->packet_data\n" );
693       wv_free(tag);
694       tag = NULL;
695       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
696       /*
697        * Maybe next time around 
698        */
699       continue;
700     }
701         tag->packet_data->Ver = AOEPROTOCOLVER;
702         tag->packet_data->Major =
703     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
704         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
705         tag->packet_data->ExtendedAFlag = TRUE;
706
707         /*
708          * Initialize the packet appropriately based on our current phase 
709          */
710         switch ( aoe_disk_ptr->search_state )
711     {
712       case AoeSearchStateGetSize_:
713         /*
714          * TODO: Make the below value into a #defined constant 
715          */
716         tag->packet_data->Cmd = 0xec;  /* IDENTIFY DEVICE */
717         tag->packet_data->Count = 1;
718         aoe_disk_ptr->search_state = AoeSearchStateGettingSize_;
719         break;
720       case AoeSearchStateGetGeometry_:
721         /*
722          * TODO: Make the below value into a #defined constant 
723          */
724         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
725         tag->packet_data->Count = 1;
726         aoe_disk_ptr->search_state = AoeSearchStateGettingGeometry_;
727         break;
728       case AoeSearchStateGetMaxSectsPerPacket_:
729         /*
730          * TODO: Make the below value into a #defined constant 
731          */
732         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
733         tag->packet_data->Count =
734           ( winvblock__uint8 ) ( ++aoe_disk_ptr->MaxSectorsPerPacket );
735         KeQuerySystemTime ( &MaxSectorsPerPacketSendTime );
736         aoe_disk_ptr->search_state =
737           AoeSearchStateGettingMaxSectsPerPacket_;
738         /*
739          * TODO: Make the below value into a #defined constant 
740          */
741         aoe_disk_ptr->Timeout = 200000;
742         break;
743       default:
744         DBG ( "Undefined search_state!!\n" );
745         wv_free(tag->packet_data);
746         wv_free(tag);
747         /*
748          * TODO: Do we need to nullify tag here? 
749          */
750         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
751         continue;
752         break;
753     }
754
755         /*
756          * Enqueue our tag 
757          */
758         tag->next = NULL;
759         KeAcquireSpinLock ( &AoeLock_, &InnerIrql );
760         if ( AoeTagListFirst_ == NULL )
761     {
762       AoeTagListFirst_ = tag;
763       tag->previous = NULL;
764     }
765         else
766     {
767       AoeTagListLast_->next = tag;
768       tag->previous = AoeTagListLast_;
769     }
770         AoeTagListLast_ = tag;
771         KeReleaseSpinLock ( &AoeLock_, InnerIrql );
772         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
773       }
774   }
775
776 static NTSTATUS STDCALL AoeDiskIo_(
777     IN WV_SP_DEV_T dev_ptr,
778     IN WV_E_DISK_IO_MODE mode,
779     IN LONGLONG start_sector,
780     IN winvblock__uint32 sector_count,
781     IN winvblock__uint8_ptr buffer,
782     IN PIRP irp
783   ) {
784     AOE_SP_IO_REQ_ request_ptr;
785     AOE_SP_WORK_TAG_ tag, new_tag_list = NULL, previous_tag = NULL;
786     KIRQL Irql;
787     winvblock__uint32 i;
788     PHYSICAL_ADDRESS PhysicalAddress;
789     winvblock__uint8_ptr PhysicalMemory;
790     WV_SP_DISK_T disk_ptr;
791     AOE_SP_DISK_ aoe_disk_ptr;
792
793     /*
794      * Establish pointers to the disk and AoE disk
795      */
796     disk_ptr = disk__get_ptr ( dev_ptr );
797     aoe_disk_ptr = AoeDiskFromDev_(dev_ptr);
798
799     if ( AoeStop_ )
800       {
801         /*
802          * Shutting down AoE; we can't service this request 
803          */
804         irp->IoStatus.Information = 0;
805         irp->IoStatus.Status = STATUS_CANCELLED;
806         IoCompleteRequest ( irp, IO_NO_INCREMENT );
807         return STATUS_CANCELLED;
808       }
809
810     if ( sector_count < 1 )
811       {
812         /*
813          * A silly request 
814          */
815         DBG ( "sector_count < 1; cancelling\n" );
816         irp->IoStatus.Information = 0;
817         irp->IoStatus.Status = STATUS_CANCELLED;
818         IoCompleteRequest ( irp, IO_NO_INCREMENT );
819         return STATUS_CANCELLED;
820       }
821
822     /*
823      * Allocate and zero-fill our request 
824      */
825     if ((request_ptr = wv_mallocz(sizeof *request_ptr)) == NULL) {
826         DBG ( "Couldn't allocate for reques_ptr; bye!\n" );
827         irp->IoStatus.Information = 0;
828         irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
829         IoCompleteRequest ( irp, IO_NO_INCREMENT );
830         return STATUS_INSUFFICIENT_RESOURCES;
831       }
832
833     /*
834      * Initialize the request 
835      */
836     request_ptr->Mode = mode;
837     request_ptr->SectorCount = sector_count;
838     request_ptr->Buffer = buffer;
839     request_ptr->Irp = irp;
840     request_ptr->TagCount = 0;
841
842     /*
843      * Split the requested sectors into packets in tags
844      */
845     for ( i = 0; i < sector_count; i += aoe_disk_ptr->MaxSectorsPerPacket )
846       {
847         /*
848          * Allocate each tag 
849          */
850         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
851       DBG ( "Couldn't allocate tag; bye!\n" );
852       /*
853        * We failed while allocating tags; free the ones we built 
854        */
855       tag = new_tag_list;
856       while ( tag != NULL )
857         {
858           previous_tag = tag;
859           tag = tag->next;
860           wv_free(previous_tag->packet_data);
861           wv_free(previous_tag);
862         }
863       wv_free(request_ptr);
864       irp->IoStatus.Information = 0;
865       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
866       IoCompleteRequest ( irp, IO_NO_INCREMENT );
867       return STATUS_INSUFFICIENT_RESOURCES;
868     }
869
870         /*
871          * Initialize each tag 
872          */
873         tag->type = AoeTagTypeIo_;
874         tag->request_ptr = request_ptr;
875         tag->device = dev_ptr;
876         request_ptr->TagCount++;
877         tag->Id = 0;
878         tag->BufferOffset = i * disk_ptr->SectorSize;
879         tag->SectorCount =
880     ( ( sector_count - i ) <
881       aoe_disk_ptr->MaxSectorsPerPacket ? sector_count -
882       i : aoe_disk_ptr->MaxSectorsPerPacket );
883
884         /*
885          * Allocate and initialize each tag's AoE packet 
886          */
887         tag->PacketSize = sizeof (AOE_S_PACKET_);
888         if (mode == WvDiskIoModeWrite)
889     tag->PacketSize += tag->SectorCount * disk_ptr->SectorSize;
890         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
891       DBG ( "Couldn't allocate tag->packet_data; bye!\n" );
892       /*
893        * We failed while allocating an AoE packet; free
894        * the tags we built
895        */
896       wv_free(tag);
897       tag = new_tag_list;
898       while ( tag != NULL )
899         {
900           previous_tag = tag;
901           tag = tag->next;
902           wv_free(previous_tag->packet_data);
903           wv_free(previous_tag);
904         }
905       wv_free(request_ptr);
906       irp->IoStatus.Information = 0;
907       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
908       IoCompleteRequest ( irp, IO_NO_INCREMENT );
909       return STATUS_INSUFFICIENT_RESOURCES;
910     }
911         tag->packet_data->Ver = AOEPROTOCOLVER;
912         tag->packet_data->Major =
913     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
914         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
915         tag->packet_data->Tag = 0;
916         tag->packet_data->Command = 0;
917         tag->packet_data->ExtendedAFlag = TRUE;
918         if (mode == WvDiskIoModeRead)
919     {
920       tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
921     }
922         else
923     {
924       tag->packet_data->Cmd = 0x34;  /* WRITE SECTOR */
925       tag->packet_data->WriteAFlag = 1;
926     }
927         tag->packet_data->Count = ( winvblock__uint8 ) tag->SectorCount;
928         tag->packet_data->Lba0 =
929     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 0 ) & 255 );
930         tag->packet_data->Lba1 =
931     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 8 ) & 255 );
932         tag->packet_data->Lba2 =
933     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 16 ) & 255 );
934         tag->packet_data->Lba3 =
935     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 24 ) & 255 );
936         tag->packet_data->Lba4 =
937     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 32 ) & 255 );
938         tag->packet_data->Lba5 =
939     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 40 ) & 255 );
940
941         /* For a write request, copy from the buffer into the AoE packet. */
942         if (mode == WvDiskIoModeWrite)
943     RtlCopyMemory ( tag->packet_data->Data, &buffer[tag->BufferOffset],
944         tag->SectorCount * disk_ptr->SectorSize );
945
946         /*
947          * Add this tag to the request's tag list 
948          */
949         tag->previous = previous_tag;
950         tag->next = NULL;
951         if ( new_tag_list == NULL )
952     {
953       new_tag_list = tag;
954     }
955         else
956     {
957       previous_tag->next = tag;
958     }
959         previous_tag = tag;
960       }
961     /*
962      * Split the requested sectors into packets in tags
963      */
964     request_ptr->TotalTags = request_ptr->TagCount;
965
966     /*
967      * Wait until we have the global spin-lock 
968      */
969     KeAcquireSpinLock ( &AoeLock_, &Irql );
970
971     /*
972      * Enqueue our request's tag list to the global tag list 
973      */
974     if ( AoeTagListLast_ == NULL )
975       {
976         AoeTagListFirst_ = new_tag_list;
977       }
978     else
979       {
980         AoeTagListLast_->next = new_tag_list;
981         new_tag_list->previous = AoeTagListLast_;
982       }
983     /*
984      * Adjust the global list to reflect our last tag 
985      */
986     AoeTagListLast_ = tag;
987
988     irp->IoStatus.Information = 0;
989     irp->IoStatus.Status = STATUS_PENDING;
990     IoMarkIrpPending ( irp );
991
992     KeReleaseSpinLock ( &AoeLock_, Irql );
993     KeSetEvent ( &AoeSignal_, 0, FALSE );
994     return STATUS_PENDING;
995   }
996
997 static void STDCALL add_target(
998     IN winvblock__uint8_ptr ClientMac,
999     IN winvblock__uint8_ptr ServerMac,
1000     winvblock__uint16 Major,
1001     winvblock__uint8 Minor,
1002     LONGLONG LBASize
1003   )
1004   {
1005     AOE_SP_TARGET_LIST_ Walker, Last;
1006     KIRQL Irql;
1007
1008     KeAcquireSpinLock ( &AoeTargetListLock_, &Irql );
1009     Walker = Last = AoeTargetList_;
1010     while ( Walker != NULL )
1011       {
1012         if (wv_memcmpeq(&Walker->Target.ClientMac, ClientMac, 6) &&
1013           wv_memcmpeq(&Walker->Target.ServerMac, ServerMac, 6) &&
1014           Walker->Target.Major == Major
1015           && Walker->Target.Minor == Minor) {
1016       if ( Walker->Target.LBASize != LBASize )
1017         {
1018           DBG ( "LBASize changed for e%d.%d " "(%I64u->%I64u)\n", Major,
1019           Minor, Walker->Target.LBASize, LBASize );
1020           Walker->Target.LBASize = LBASize;
1021         }
1022       KeQuerySystemTime ( &Walker->Target.ProbeTime );
1023       KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1024       return;
1025     }
1026         Last = Walker;
1027         Walker = Walker->next;
1028       }
1029
1030     if ((Walker = wv_malloc(sizeof *Walker)) == NULL) {
1031         DBG("wv_malloc Walker\n");
1032         KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1033         return;
1034       }
1035     Walker->next = NULL;
1036     RtlCopyMemory ( Walker->Target.ClientMac, ClientMac, 6 );
1037     RtlCopyMemory ( Walker->Target.ServerMac, ServerMac, 6 );
1038     Walker->Target.Major = Major;
1039     Walker->Target.Minor = Minor;
1040     Walker->Target.LBASize = LBASize;
1041     KeQuerySystemTime ( &Walker->Target.ProbeTime );
1042
1043     if ( Last == NULL )
1044       {
1045         AoeTargetList_ = Walker;
1046       }
1047     else
1048       {
1049         Last->next = Walker;
1050       }
1051     KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1052   }
1053
1054 /**
1055  * Process an AoE reply.
1056  *
1057  * @v SourceMac         The AoE server's MAC address.
1058  * @v DestinationMac    The AoE client's MAC address.
1059  * @v Data              The AoE packet.
1060  * @v DataSize          The AoE packet's size.
1061  */
1062 NTSTATUS STDCALL aoe__reply(
1063     IN winvblock__uint8_ptr SourceMac,
1064     IN winvblock__uint8_ptr DestinationMac,
1065     IN winvblock__uint8_ptr Data,
1066     IN winvblock__uint32 DataSize
1067   )
1068   {
1069     AOE_SP_PACKET_ reply = (AOE_SP_PACKET_) Data;
1070     LONGLONG LBASize;
1071     AOE_SP_WORK_TAG_ tag;
1072     KIRQL Irql;
1073     winvblock__bool Found = FALSE;
1074     LARGE_INTEGER CurrentTime;
1075     WV_SP_DISK_T disk_ptr;
1076     AOE_SP_DISK_ aoe_disk_ptr;
1077
1078     /*
1079      * Discard non-responses 
1080      */
1081     if ( !reply->ResponseFlag )
1082       return STATUS_SUCCESS;
1083
1084     /*
1085      * If the response matches our probe, add the AoE disk device 
1086      */
1087     if ( AoeProbeTag_->Id == reply->Tag )
1088       {
1089         RtlCopyMemory ( &LBASize, &reply->Data[200], sizeof ( LONGLONG ) );
1090         add_target ( DestinationMac, SourceMac, ntohs ( reply->Major ),
1091          reply->Minor, LBASize );
1092         return STATUS_SUCCESS;
1093       }
1094
1095     /*
1096      * Wait until we have the global spin-lock 
1097      */
1098     KeAcquireSpinLock ( &AoeLock_, &Irql );
1099
1100     /*
1101      * Search for request tag 
1102      */
1103     if ( AoeTagListFirst_ == NULL )
1104       {
1105         KeReleaseSpinLock ( &AoeLock_, Irql );
1106         return STATUS_SUCCESS;
1107       }
1108     tag = AoeTagListFirst_;
1109     while ( tag != NULL )
1110       {
1111         if ( ( tag->Id == reply->Tag )
1112        && ( tag->packet_data->Major == reply->Major )
1113        && ( tag->packet_data->Minor == reply->Minor ) )
1114     {
1115       Found = TRUE;
1116       break;
1117     }
1118         tag = tag->next;
1119       }
1120     if ( !Found )
1121       {
1122         KeReleaseSpinLock ( &AoeLock_, Irql );
1123         return STATUS_SUCCESS;
1124       }
1125     else
1126       {
1127         /*
1128          * Remove the tag from the global tag list 
1129          */
1130         if ( tag->previous == NULL )
1131     AoeTagListFirst_ = tag->next;
1132         else
1133     tag->previous->next = tag->next;
1134         if ( tag->next == NULL )
1135     AoeTagListLast_ = tag->previous;
1136         else
1137     tag->next->previous = tag->previous;
1138         AoePendingTags_--;
1139         if ( AoePendingTags_ < 0 )
1140     DBG ( "AoePendingTags_ < 0!!\n" );
1141         KeSetEvent ( &AoeSignal_, 0, FALSE );
1142       }
1143     KeReleaseSpinLock ( &AoeLock_, Irql );
1144
1145     /*
1146      * Establish pointers to the disk device and AoE disk
1147      */
1148     disk_ptr = disk__get_ptr ( tag->device );
1149     aoe_disk_ptr = AoeDiskFromDev_(tag->device);
1150
1151     /*
1152      * If our tag was a discovery request, note the server 
1153      */
1154     if (wv_memcmpeq(aoe_disk_ptr->ServerMac, "\xff\xff\xff\xff\xff\xff", 6)) {
1155         RtlCopyMemory ( aoe_disk_ptr->ServerMac, SourceMac, 6 );
1156         DBG ( "Major: %d minor: %d found on server "
1157         "%02x:%02x:%02x:%02x:%02x:%02x\n", aoe_disk_ptr->Major,
1158         aoe_disk_ptr->Minor, SourceMac[0], SourceMac[1], SourceMac[2],
1159         SourceMac[3], SourceMac[4], SourceMac[5] );
1160       }
1161
1162     KeQuerySystemTime ( &CurrentTime );
1163     aoe_disk_ptr->Timeout -=
1164       ( winvblock__uint32 ) ( ( aoe_disk_ptr->Timeout -
1165               ( CurrentTime.QuadPart -
1166           tag->FirstSendTime.QuadPart ) ) / 1024 );
1167     /*
1168      * TODO: Replace the values below with #defined constants 
1169      */
1170     if ( aoe_disk_ptr->Timeout > 100000000 )
1171       aoe_disk_ptr->Timeout = 100000000;
1172
1173     switch ( tag->type )
1174       {
1175         case AoeTagTypeSearchDrive_:
1176     KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
1177     switch ( aoe_disk_ptr->search_state )
1178       {
1179         case AoeSearchStateGettingSize_:
1180           /*
1181            * The reply tells us the disk size
1182            */
1183           RtlCopyMemory ( &disk_ptr->LBADiskSize, &reply->Data[200],
1184               sizeof ( LONGLONG ) );
1185           /*
1186            * Next we are concerned with the disk geometry
1187            */
1188           aoe_disk_ptr->search_state = AoeSearchStateGetGeometry_;
1189           break;
1190         case AoeSearchStateGettingGeometry_:
1191           /*
1192            * FIXME: use real values from partition table.
1193            * We used to truncate a fractional end cylinder, but
1194            * now leave it be in the hopes everyone uses LBA
1195            */
1196           disk_ptr->SectorSize = 512;
1197           disk_ptr->Heads = 255;
1198           disk_ptr->Sectors = 63;
1199           disk_ptr->Cylinders =
1200       disk_ptr->LBADiskSize / ( disk_ptr->Heads *
1201               disk_ptr->Sectors );
1202           /*
1203            * Next we are concerned with the maximum sectors per packet
1204            */
1205           aoe_disk_ptr->search_state =
1206             AoeSearchStateGetMaxSectsPerPacket_;
1207           break;
1208         case AoeSearchStateGettingMaxSectsPerPacket_:
1209           DataSize -= sizeof (AOE_S_PACKET_);
1210           if ( DataSize <
1211          ( aoe_disk_ptr->MaxSectorsPerPacket *
1212            disk_ptr->SectorSize ) )
1213       {
1214         DBG ( "Packet size too low while getting "
1215         "MaxSectorsPerPacket (tried %d, got size of %d)\n",
1216         aoe_disk_ptr->MaxSectorsPerPacket, DataSize );
1217         aoe_disk_ptr->MaxSectorsPerPacket--;
1218         aoe_disk_ptr->search_state = AoeSearchStateDone_;
1219       }
1220           else if ( aoe_disk_ptr->MTU <
1221         ( sizeof (AOE_S_PACKET_) +
1222           ( ( aoe_disk_ptr->MaxSectorsPerPacket +
1223               1 ) * disk_ptr->SectorSize ) ) )
1224       {
1225         DBG ( "Got MaxSectorsPerPacket %d at size of %d. "
1226         "MTU of %d reached\n",
1227         aoe_disk_ptr->MaxSectorsPerPacket, DataSize,
1228         aoe_disk_ptr->MTU );
1229         aoe_disk_ptr->search_state = AoeSearchStateDone_;
1230       }
1231           else
1232       {
1233         DBG ( "Got MaxSectorsPerPacket %d at size of %d, "
1234         "trying next...\n", aoe_disk_ptr->MaxSectorsPerPacket,
1235         DataSize );
1236         aoe_disk_ptr->search_state =
1237           AoeSearchStateGetMaxSectsPerPacket_;
1238       }
1239           break;
1240         default:
1241           DBG ( "Undefined search_state!\n" );
1242           break;
1243       }
1244     KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1245     KeSetEvent ( &disk_ptr->SearchEvent, 0, FALSE );
1246     break;
1247         case AoeTagTypeIo_:
1248     /* If the reply is in response to a read request, get our data! */
1249     if (tag->request_ptr->Mode == WvDiskIoModeRead)
1250       RtlCopyMemory ( &tag->request_ptr->Buffer[tag->BufferOffset],
1251           reply->Data,
1252           tag->SectorCount * disk_ptr->SectorSize );
1253     /*
1254      * If this is the last reply expected for the read request,
1255      * complete the IRP and free the request
1256      */
1257     if ( InterlockedDecrement ( &tag->request_ptr->TagCount ) == 0 )
1258       {
1259         tag->request_ptr->Irp->IoStatus.Information =
1260           tag->request_ptr->SectorCount * disk_ptr->SectorSize;
1261         tag->request_ptr->Irp->IoStatus.Status = STATUS_SUCCESS;
1262         WvDriverCompletePendingIrp(tag->request_ptr->Irp);
1263         wv_free(tag->request_ptr);
1264       }
1265     break;
1266         default:
1267     DBG ( "Unknown tag type!!\n" );
1268     break;
1269       }
1270
1271     KeSetEvent ( &AoeSignal_, 0, FALSE );
1272     wv_free(tag->packet_data);
1273     wv_free(tag);
1274     return STATUS_SUCCESS;
1275   }
1276
1277 void aoe__reset_probe(void)
1278   {
1279     AoeProbeTag_->SendTime.QuadPart = 0LL;
1280   }
1281
1282 static void STDCALL AoeThread_(IN void *StartContext)
1283   {
1284     LARGE_INTEGER Timeout, CurrentTime, ProbeTime, ReportTime;
1285     winvblock__uint32 NextTagId = 1;
1286     AOE_SP_WORK_TAG_ tag;
1287     KIRQL Irql;
1288     winvblock__uint32 Sends = 0;
1289     winvblock__uint32 Resends = 0;
1290     winvblock__uint32 ResendFails = 0;
1291     winvblock__uint32 Fails = 0;
1292     winvblock__uint32 RequestTimeout = 0;
1293     WV_SP_DISK_T disk_ptr;
1294     AOE_SP_DISK_ aoe_disk_ptr;
1295
1296     DBG ( "Entry\n" );
1297     ReportTime.QuadPart = 0LL;
1298     ProbeTime.QuadPart = 0LL;
1299
1300     while ( TRUE )
1301       {
1302         /*
1303          * TODO: Make the below value a #defined constant 
1304          */
1305         /*
1306          * 100.000 * 100ns = 10.000.000 ns = 10ms
1307          */
1308         Timeout.QuadPart = -100000LL;
1309         KeWaitForSingleObject ( &AoeSignal_, Executive,
1310               KernelMode, FALSE, &Timeout );
1311         KeResetEvent ( &AoeSignal_ );
1312         if ( AoeStop_ )
1313     {
1314       DBG ( "Stopping...\n" );
1315       PsTerminateSystemThread ( STATUS_SUCCESS );
1316     }
1317
1318         KeQuerySystemTime ( &CurrentTime );
1319         /*
1320          * TODO: Make the below value a #defined constant 
1321          */
1322         if ( CurrentTime.QuadPart > ( ReportTime.QuadPart + 10000000LL ) )
1323     {
1324       DBG ( "Sends: %d  Resends: %d  ResendFails: %d  Fails: %d  "
1325       "AoePendingTags_: %d  RequestTimeout: %d\n", Sends,
1326       Resends, ResendFails, Fails, AoePendingTags_,
1327       RequestTimeout );
1328       Sends = 0;
1329       Resends = 0;
1330       ResendFails = 0;
1331       Fails = 0;
1332       KeQuerySystemTime ( &ReportTime );
1333     }
1334
1335         /*
1336          * TODO: Make the below value a #defined constant 
1337          */
1338         if ( CurrentTime.QuadPart >
1339        ( AoeProbeTag_->SendTime.QuadPart + 100000000LL ) )
1340     {
1341       AoeProbeTag_->Id = NextTagId++;
1342       if ( NextTagId == 0 )
1343         NextTagId++;
1344       AoeProbeTag_->packet_data->Tag = AoeProbeTag_->Id;
1345       Protocol_Send ( "\xff\xff\xff\xff\xff\xff",
1346           "\xff\xff\xff\xff\xff\xff",
1347           ( winvblock__uint8_ptr ) AoeProbeTag_->
1348           packet_data, AoeProbeTag_->PacketSize,
1349           NULL );
1350       KeQuerySystemTime ( &AoeProbeTag_->SendTime );
1351     }
1352
1353         KeAcquireSpinLock ( &AoeLock_, &Irql );
1354         if ( AoeTagListFirst_ == NULL )
1355     {
1356       KeReleaseSpinLock ( &AoeLock_, Irql );
1357       continue;
1358     }
1359         tag = AoeTagListFirst_;
1360         while ( tag != NULL )
1361     {
1362       /*
1363        * Establish pointers to the disk and AoE disk
1364        */
1365       disk_ptr = disk__get_ptr ( tag->device );
1366       aoe_disk_ptr = AoeDiskFromDev_(tag->device);
1367
1368       RequestTimeout = aoe_disk_ptr->Timeout;
1369       if ( tag->Id == 0 )
1370         {
1371           if ( AoePendingTags_ <= 64 )
1372       {
1373         /*
1374          * if ( AoePendingTags_ <= 102400 ) { 
1375          */
1376         if ( AoePendingTags_ < 0 )
1377           DBG ( "AoePendingTags_ < 0!!\n" );
1378         tag->Id = NextTagId++;
1379         if ( NextTagId == 0 )
1380           NextTagId++;
1381         tag->packet_data->Tag = tag->Id;
1382         if ( Protocol_Send
1383              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1384          ( winvblock__uint8_ptr ) tag->packet_data,
1385          tag->PacketSize, tag ) )
1386           {
1387             KeQuerySystemTime ( &tag->FirstSendTime );
1388             KeQuerySystemTime ( &tag->SendTime );
1389             AoePendingTags_++;
1390             Sends++;
1391           }
1392         else
1393           {
1394             Fails++;
1395             tag->Id = 0;
1396             break;
1397           }
1398       }
1399         }
1400       else
1401         {
1402           KeQuerySystemTime ( &CurrentTime );
1403           if ( CurrentTime.QuadPart >
1404          ( tag->SendTime.QuadPart +
1405            ( LONGLONG ) ( aoe_disk_ptr->Timeout * 2 ) ) )
1406       {
1407         if ( Protocol_Send
1408              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1409          ( winvblock__uint8_ptr ) tag->packet_data,
1410          tag->PacketSize, tag ) )
1411           {
1412             KeQuerySystemTime ( &tag->SendTime );
1413             aoe_disk_ptr->Timeout += aoe_disk_ptr->Timeout / 1000;
1414             if ( aoe_disk_ptr->Timeout > 100000000 )
1415         aoe_disk_ptr->Timeout = 100000000;
1416             Resends++;
1417           }
1418         else
1419           {
1420             ResendFails++;
1421             break;
1422           }
1423       }
1424         }
1425       tag = tag->next;
1426       if ( tag == AoeTagListFirst_ )
1427         {
1428           DBG ( "Taglist Cyclic!!\n" );
1429           break;
1430         }
1431     }
1432         KeReleaseSpinLock ( &AoeLock_, Irql );
1433       }
1434     DBG ( "Exit\n" );
1435   }
1436
1437 static winvblock__uint32 AoeDiskMaxXferLen_(IN WV_SP_DISK_T disk_ptr) {
1438     AOE_SP_DISK_ aoe_disk_ptr = AoeDiskFromDev_(disk_ptr->Dev);
1439
1440     return disk_ptr->SectorSize * aoe_disk_ptr->MaxSectorsPerPacket;
1441   }
1442
1443 static winvblock__uint32 STDCALL query_id(
1444     IN WV_SP_DEV_T dev,
1445     IN BUS_QUERY_ID_TYPE query_type,
1446     IN OUT WCHAR (*buf)[512]
1447   ) {
1448     AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1449
1450     switch (query_type) {
1451         case BusQueryDeviceID:
1452           return swprintf(*buf, winvblock__literal_w L"\\AoEHardDisk") + 1;
1453
1454         case BusQueryInstanceID:
1455           return swprintf(
1456               *buf,
1457               L"AoE_at_Shelf_%d.Slot_%d",
1458               aoe_disk->Major,
1459               aoe_disk->Minor
1460             ) + 1;
1461
1462         case BusQueryHardwareIDs: {
1463             winvblock__uint32 tmp;
1464
1465             tmp = swprintf(
1466                 *buf,
1467                 winvblock__literal_w L"\\AoEHardDisk"
1468               ) + 1;
1469             tmp += swprintf(*buf + tmp, L"GenDisk") + 4;
1470             return tmp;
1471           }
1472
1473         case BusQueryCompatibleIDs:
1474           return swprintf(*buf, L"GenDisk") + 4;
1475
1476         default:
1477           return 0;
1478       }
1479   }
1480
1481 #ifdef _MSC_VER
1482 #  pragma pack(1)
1483 #endif
1484 winvblock__def_struct(abft) {
1485     winvblock__uint32 Signature;  /* 0x54464261 (aBFT) */
1486     winvblock__uint32 Length;
1487     winvblock__uint8 Revision;
1488     winvblock__uint8 Checksum;
1489     winvblock__uint8 OEMID[6];
1490     winvblock__uint8 OEMTableID[8];
1491     winvblock__uint8 Reserved1[12];
1492     winvblock__uint16 Major;
1493     winvblock__uint8 Minor;
1494     winvblock__uint8 Reserved2;
1495     winvblock__uint8 ClientMac[6];
1496   } __attribute__((__packed__));
1497 #ifdef _MSC_VER
1498 #  pragma pack()
1499 #endif
1500
1501 static void STDCALL AoeDiskClose_(IN WV_SP_DISK_T disk_ptr) {
1502     return;
1503   }
1504
1505 static void AoeProcessAbft_(void) {
1506     PHYSICAL_ADDRESS PhysicalAddress;
1507     winvblock__uint8_ptr PhysicalMemory;
1508     winvblock__uint32 Offset, Checksum, i;
1509     winvblock__bool FoundAbft = FALSE;
1510     abft AoEBootRecord;
1511     AOE_SP_DISK_ aoe_disk;
1512
1513     /* Find aBFT. */
1514     PhysicalAddress.QuadPart = 0LL;
1515     PhysicalMemory = MmMapIoSpace(PhysicalAddress, 0xa0000, MmNonCached);
1516     if (!PhysicalMemory) {
1517         DBG("Could not map low memory\n");
1518         goto err_map_mem;
1519       }
1520     for (Offset = 0; Offset < 0xa0000; Offset += 0x10) {
1521         if (!(
1522             ((abft_ptr) (PhysicalMemory + Offset))->Signature ==
1523             0x54464261
1524           ))
1525           continue;
1526         Checksum = 0;
1527         for (
1528             i = 0;
1529             i < ((abft_ptr) (PhysicalMemory + Offset))->Length;
1530             i++
1531           )
1532           Checksum += PhysicalMemory[Offset + i];
1533         if (Checksum & 0xff)
1534           continue;
1535         if (((abft_ptr) (PhysicalMemory + Offset))->Revision != 1) {
1536             DBG(
1537                 "Found aBFT with mismatched revision v%d at "
1538                   "segment 0x%4x. want v1.\n",
1539                 ((abft_ptr) (PhysicalMemory + Offset))->Revision,
1540                 (Offset / 0x10)
1541               );
1542             continue;
1543           }
1544         DBG("Found aBFT at segment: 0x%04x\n", (Offset / 0x10));
1545         RtlCopyMemory(
1546             &AoEBootRecord,
1547             PhysicalMemory + Offset,
1548             sizeof (abft)
1549           );
1550         FoundAbft = TRUE;
1551         break;
1552       }
1553     MmUnmapIoSpace(PhysicalMemory, 0xa0000);
1554
1555     #ifdef RIS
1556     FoundAbft = TRUE;
1557     RtlCopyMemory(AoEBootRecord.ClientMac, "\x00\x0c\x29\x34\x69\x34", 6);
1558     AoEBootRecord.Major = 0;
1559     AoEBootRecord.Minor = 10;
1560     #endif
1561
1562     if (!FoundAbft)
1563       goto out_no_abft;
1564     aoe_disk = AoeDiskCreate_();
1565     if(aoe_disk == NULL) {
1566         DBG("Could not create AoE disk from aBFT!\n");
1567         return;
1568       }
1569     DBG(
1570         "Attaching AoE disk from client NIC "
1571           "%02x:%02x:%02x:%02x:%02x:%02x to major: %d minor: %d\n",
1572         AoEBootRecord.ClientMac[0],
1573         AoEBootRecord.ClientMac[1],
1574         AoEBootRecord.ClientMac[2],
1575         AoEBootRecord.ClientMac[3],
1576         AoEBootRecord.ClientMac[4],
1577         AoEBootRecord.ClientMac[5],
1578         AoEBootRecord.Major,
1579         AoEBootRecord.Minor
1580       );
1581     RtlCopyMemory(aoe_disk->ClientMac, AoEBootRecord.ClientMac, 6);
1582     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
1583     aoe_disk->Major = AoEBootRecord.Major;
1584     aoe_disk->Minor = AoEBootRecord.Minor;
1585     aoe_disk->MaxSectorsPerPacket = 1;
1586     aoe_disk->Timeout = 200000;          /* 20 ms. */
1587     aoe_disk->disk->Dev->Boot = TRUE;
1588     aoe_disk->disk->Media = WvDiskMediaTypeHard;
1589     WvDriverBusAddDev(aoe_disk->disk->Dev);
1590     return;
1591
1592     out_no_abft:
1593     DBG("No aBFT found\n");
1594
1595     err_map_mem:
1596
1597     return;
1598   }
1599
1600 NTSTATUS STDCALL aoe__scan(
1601     IN WV_SP_DEV_T dev,
1602     IN PIRP irp
1603   ) {
1604     KIRQL irql;
1605     winvblock__uint32 count;
1606     AOE_SP_TARGET_LIST_ target_walker;
1607     aoe__mount_targets_ptr targets;
1608     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1609
1610     DBG("Got IOCTL_AOE_SCAN...\n");
1611     KeAcquireSpinLock(&AoeTargetListLock_, &irql);
1612
1613     count = 0;
1614     target_walker = AoeTargetList_;
1615     while (target_walker != NULL) {
1616         count++;
1617         target_walker = target_walker->next;
1618       }
1619
1620     targets = wv_malloc(sizeof *targets + (count * sizeof targets->Target[0]));
1621     if (targets == NULL) {
1622         DBG("wv_malloc targets\n");
1623         return driver__complete_irp(
1624             irp,
1625             0,
1626             STATUS_INSUFFICIENT_RESOURCES
1627           );
1628       }
1629     irp->IoStatus.Information =
1630       sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target));
1631     targets->Count = count;
1632
1633     count = 0;
1634     target_walker = AoeTargetList_;
1635     while (target_walker != NULL) {
1636         RtlCopyMemory(
1637             &targets->Target[count],
1638             &target_walker->Target,
1639             sizeof (aoe__mount_target)
1640           );
1641         count++;
1642         target_walker = target_walker->next;
1643       }
1644     RtlCopyMemory(
1645         irp->AssociatedIrp.SystemBuffer,
1646         targets,
1647         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
1648           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target))) ?
1649           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
1650           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target)))
1651         )
1652       );
1653     wv_free(targets);
1654
1655     KeReleaseSpinLock(&AoeTargetListLock_, irql);
1656     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
1657   }
1658
1659 NTSTATUS STDCALL aoe__show(
1660     IN WV_SP_DEV_T dev,
1661     IN PIRP irp
1662   ) {
1663     winvblock__uint32 count;
1664     WV_SP_DEV_T dev_walker;
1665     WV_SP_BUS_T bus;
1666     aoe__mount_disks_ptr disks;
1667     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1668
1669     DBG("Got IOCTL_AOE_SHOW...\n");
1670
1671     bus = &AoeBusMain;
1672     dev_walker = bus->first_child;
1673     count = 0;
1674     while (dev_walker != NULL) {
1675         count++;
1676         dev_walker = dev_walker->next_sibling_ptr;
1677       }
1678
1679     disks = wv_malloc(sizeof *disks + (count * sizeof disks->Disk[0]));
1680     if (disks == NULL) {
1681         DBG("wv_malloc disks\n");
1682         return driver__complete_irp(
1683             irp,
1684             0,
1685             STATUS_INSUFFICIENT_RESOURCES
1686           );
1687       }
1688     irp->IoStatus.Information =
1689       sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk));
1690     disks->Count = count;
1691
1692     count = 0;
1693     dev_walker = bus->first_child;
1694     while (dev_walker != NULL) {
1695         WV_SP_DISK_T disk = disk__get_ptr(dev_walker);
1696         AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev_walker);
1697
1698         disks->Disk[count].Disk = dev_walker->DevNum;
1699         RtlCopyMemory(
1700             &disks->Disk[count].ClientMac,
1701             &aoe_disk->ClientMac,
1702             6
1703           );
1704         RtlCopyMemory(
1705             &disks->Disk[count].ServerMac,
1706             &aoe_disk->ServerMac,
1707             6
1708           );
1709         disks->Disk[count].Major = aoe_disk->Major;
1710         disks->Disk[count].Minor = aoe_disk->Minor;
1711         disks->Disk[count].LBASize = disk->LBADiskSize;
1712         count++;
1713         dev_walker = dev_walker->next_sibling_ptr;
1714       }
1715     RtlCopyMemory(
1716         irp->AssociatedIrp.SystemBuffer,
1717         disks,
1718         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
1719           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk))) ?
1720           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
1721           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk)))
1722         )
1723       );
1724     wv_free(disks);
1725     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
1726   }
1727
1728 NTSTATUS STDCALL aoe__mount(
1729     IN WV_SP_DEV_T dev,
1730     IN PIRP irp
1731   ) {
1732     winvblock__uint8_ptr buffer = irp->AssociatedIrp.SystemBuffer;
1733     AOE_SP_DISK_ aoe_disk;
1734
1735     DBG(
1736         "Got IOCTL_AOE_MOUNT for client: %02x:%02x:%02x:%02x:%02x:%02x "
1737           "Major:%d Minor:%d\n",
1738         buffer[0],
1739         buffer[1],
1740         buffer[2],
1741         buffer[3],
1742         buffer[4],
1743         buffer[5],
1744         *(winvblock__uint16_ptr) (buffer + 6),
1745         (winvblock__uint8) buffer[8]
1746       );
1747     aoe_disk = AoeDiskCreate_();
1748     if (aoe_disk == NULL) {
1749         DBG("Could not create AoE disk!\n");
1750         return driver__complete_irp(
1751             irp,
1752             0,
1753             STATUS_INSUFFICIENT_RESOURCES
1754           );
1755       }
1756     RtlCopyMemory(aoe_disk->ClientMac, buffer, 6);
1757     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
1758     aoe_disk->Major = *(winvblock__uint16_ptr) (buffer + 6);
1759     aoe_disk->Minor = (winvblock__uint8) buffer[8];
1760     aoe_disk->MaxSectorsPerPacket = 1;
1761     aoe_disk->Timeout = 200000;             /* 20 ms. */
1762     aoe_disk->disk->Dev->Boot = FALSE;
1763     aoe_disk->disk->Media = WvDiskMediaTypeHard;
1764     WvDriverBusAddDev(aoe_disk->disk->Dev);
1765
1766     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
1767   }
1768
1769 /**
1770  * Create a new AoE disk.
1771  *
1772  * @ret aoe_disk        The address of a new AoE disk, or NULL for failure.
1773  *
1774  * This function should not be confused with a PDO creation routine, which is
1775  * actually implemented for each device type.  This routine will allocate an
1776  * AOE_S_DISK_, track it in a global list, as well as populate the disk
1777  * with default values.
1778  */
1779 static AOE_SP_DISK_ AoeDiskCreate_(void) {
1780     WV_SP_DISK_T disk;
1781     AOE_SP_DISK_ aoe_disk;
1782
1783     /* Try to create a disk. */
1784     disk = disk__create();
1785     if (disk == NULL)
1786       goto err_nodisk;
1787     /*
1788      * AoE disk devices might be used for booting and should
1789      * not be allocated from a paged memory pool.
1790      */
1791     aoe_disk = wv_mallocz(sizeof *aoe_disk);
1792     if (aoe_disk == NULL)
1793       goto err_noaoedisk;
1794     /* Track the new AoE disk in our global list. */
1795     ExInterlockedInsertTailList(
1796         &AoeDiskList_,
1797         &aoe_disk->tracking,
1798         &AoeDiskListLock_
1799       );
1800     /* Populate non-zero device defaults. */
1801     aoe_disk->disk = disk;
1802     aoe_disk->prev_free = disk->Dev->Ops.Free;
1803     disk->Dev->Ops.Free = AoeDiskFree_;
1804     disk->Dev->Ops.PnpId = query_id;
1805     disk->disk_ops.Io = AoeDiskIo_;
1806     disk->disk_ops.MaxXferLen = AoeDiskMaxXferLen_;
1807     disk->disk_ops.Init = AoeDiskInit_;
1808     disk->disk_ops.Close = AoeDiskClose_;
1809     disk->ext = aoe_disk;
1810
1811     return aoe_disk;
1812
1813     err_noaoedisk:
1814
1815     WvDevFree(disk->Dev);
1816     err_nodisk:
1817
1818     return NULL;
1819   }
1820
1821 /**
1822  * Default AoE disk deletion operation.
1823  *
1824  * @v dev               Points to the AoE disk device to delete.
1825  */
1826 static void STDCALL AoeDiskFree_(IN WV_SP_DEV_T dev) {
1827     AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1828     /* Free the "inherited class". */
1829     aoe_disk->prev_free(dev);
1830     /*
1831      * Track the AoE disk deletion in our global list.  Unfortunately,
1832      * for now we have faith that an AoE disk won't be deleted twice and
1833      * result in a race condition.  Something to keep in mind...
1834      */
1835     ExInterlockedRemoveHeadList(
1836         aoe_disk->tracking.Blink,
1837         &AoeDiskListLock_
1838       );
1839
1840     wv_free(aoe_disk);
1841   }