7979517babe2ee97eb60f2c7b0b16620b0c99a7f
[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_SP_BUS_T aoe_bus;
55 extern winvblock__bool aoe_bus__create(void);
56 extern void aoe_bus__free(void);
57
58 /* Forward declarations. */
59 struct aoe__disk_type_;
60 static void STDCALL aoe__thread_(IN void *);
61 static void aoe__process_abft_(void);
62 static void STDCALL aoe__unload_(IN PDRIVER_OBJECT);
63 static struct aoe__disk_type_ * aoe__create_disk_(void);
64 static WV_F_DEV_FREE aoe__free_disk_;
65
66 /** Tag types. */
67 enum aoe__tag_type_
68   {
69     aoe__tag_type_io_,
70     aoe__tag_type_search_drive_
71   };
72
73 #ifdef _MSC_VER
74 #  pragma pack(1)
75 #endif
76
77 /** AoE packet. */
78 struct aoe__packet_
79   {
80     winvblock__uint8 ReservedFlag:2;
81     winvblock__uint8 ErrorFlag:1;
82     winvblock__uint8 ResponseFlag:1;
83     winvblock__uint8 Ver:4;
84     winvblock__uint8 Error;
85     winvblock__uint16 Major;
86     winvblock__uint8 Minor;
87     winvblock__uint8 Command;
88     winvblock__uint32 Tag;
89
90     winvblock__uint8 WriteAFlag:1;
91     winvblock__uint8 AsyncAFlag:1;
92     winvblock__uint8 Reserved1AFlag:2;
93     winvblock__uint8 DeviceHeadAFlag:1;
94     winvblock__uint8 Reserved2AFlag:1;
95     winvblock__uint8 ExtendedAFlag:1;
96     winvblock__uint8 Reserved3AFlag:1;
97     union
98       {
99         winvblock__uint8 Err;
100         winvblock__uint8 Feature;
101       };
102     winvblock__uint8 Count;
103     union
104       {
105         winvblock__uint8 Cmd;
106         winvblock__uint8 Status;
107       };
108
109     winvblock__uint8 Lba0;
110     winvblock__uint8 Lba1;
111     winvblock__uint8 Lba2;
112     winvblock__uint8 Lba3;
113     winvblock__uint8 Lba4;
114     winvblock__uint8 Lba5;
115     winvblock__uint16 Reserved;
116
117     winvblock__uint8 Data[];
118   } __attribute__((__packed__));
119
120 #ifdef _MSC_VER
121 #  pragma pack()
122 #endif
123
124 /** An I/O request. */
125 struct aoe__io_req_
126   {
127     disk__io_mode Mode;
128     winvblock__uint32 SectorCount;
129     winvblock__uint8_ptr Buffer;
130     PIRP Irp;
131     winvblock__uint32 TagCount;
132     winvblock__uint32 TotalTags;
133   };
134
135 /** A work item "tag". */
136 struct aoe__work_tag_
137   {
138     enum aoe__tag_type_ type;
139     WV_SP_DEV_T device;
140     struct aoe__io_req_ * request_ptr;
141     winvblock__uint32 Id;
142     struct aoe__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   };
151
152 /** A disk search. */
153 struct aoe__disk_search_
154   {
155     WV_SP_DEV_T device;
156     struct aoe__work_tag_ * tag;
157     struct aoe__disk_search_ * next;
158   };
159
160 enum aoe__search_state_
161   {
162     aoe__search_state_search_nic_,
163     aoe__search_state_get_size_,
164     aoe__search_state_getting_size_,
165     aoe__search_state_get_geometry_,
166     aoe__search_state_getting_geometry_,
167     aoe__search_state_get_max_sectors_per_packet_,
168     aoe__search_state_getting_max_sectors_per_packet_,
169     aoe__search_state_done_
170   };
171
172 /** The AoE disk type. */
173 struct aoe__disk_type_
174   {
175     disk__type_ptr disk;
176     winvblock__uint32 MTU;
177     winvblock__uint8 ClientMac[6];
178     winvblock__uint8 ServerMac[6];
179     winvblock__uint32 Major;
180     winvblock__uint32 Minor;
181     winvblock__uint32 MaxSectorsPerPacket;
182     winvblock__uint32 Timeout;
183     enum aoe__search_state_ search_state;
184     WV_FP_DEV_FREE prev_free;
185     LIST_ENTRY tracking;
186   };
187
188 struct aoe__target_list_
189   {
190     aoe__mount_target Target;
191     struct aoe__target_list_ * next;
192   };
193
194 /** Private globals. */
195 static struct aoe__target_list_ * aoe__target_list_ = NULL;
196 static KSPIN_LOCK aoe__target_list_spinlock_;
197 static winvblock__bool aoe__stop_ = FALSE;
198 static KSPIN_LOCK aoe__spinlock_;
199 static KEVENT aoe__thread_sig_evt_;
200 static struct aoe__work_tag_ * aoe__tag_list_ = NULL;
201 static struct aoe__work_tag_ * aoe__tag_list_last_ = NULL;
202 static struct aoe__work_tag_ * aoe__probe_tag_ = NULL;
203 static struct aoe__disk_search_ * aoe__disk_search_list_ = NULL;
204 static LONG aoe__outstanding_tags_ = 0;
205 static HANDLE aoe__thread_handle_;
206 static winvblock__bool aoe__started_ = FALSE;
207 static LIST_ENTRY aoe__disk_list_;
208 static KSPIN_LOCK aoe__disk_list_lock_;
209
210 /* Yield a pointer to the AoE disk. */
211 static struct aoe__disk_type_ * aoe__get_(WV_SP_DEV_T dev_ptr)
212   {
213     return disk__get_ptr(dev_ptr)->ext;
214   }
215
216 static winvblock__bool STDCALL setup_reg(OUT PNTSTATUS status_out)
217   {
218     NTSTATUS status;
219     winvblock__bool Updated = FALSE;
220     WCHAR InterfacesPath[] = L"\\Ndi\\Interfaces\\";
221     WCHAR LinkagePath[] = L"\\Linkage\\";
222     WCHAR NdiPath[] = L"\\Ndi\\";
223     WCHAR DriverServiceNamePath[] =
224       L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
225     OBJECT_ATTRIBUTES SubKeyObject;
226     HANDLE
227       ControlKeyHandle, NetworkClassKeyHandle, SubKeyHandle;
228     winvblock__uint32
229       i, SubkeyIndex, ResultLength, InterfacesKeyStringLength,
230       LinkageKeyStringLength, NdiKeyStringLength, NewValueLength;
231     PWCHAR
232       InterfacesKeyString, LinkageKeyString, NdiKeyString,
233       DriverServiceNameString, NewValue;
234     UNICODE_STRING
235       InterfacesKey, LinkageKey, NdiKey, LowerRange, UpperBind, Service,
236       DriverServiceName;
237     PKEY_BASIC_INFORMATION KeyInformation;
238     PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
239     winvblock__bool Update, Found;
240
241     DBG("Entry\n");
242
243     RtlInitUnicodeString(&LowerRange, L"LowerRange");
244     RtlInitUnicodeString(&UpperBind, L"UpperBind");
245     RtlInitUnicodeString(&Service, L"Service");
246
247     /* Open the network adapter class key. */
248     status = registry__open_key(
249         (L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Class\\"
250           L"{4D36E972-E325-11CE-BFC1-08002BE10318}\\"),
251         &NetworkClassKeyHandle
252       );
253     if (!NT_SUCCESS(status))
254       goto err_keyopennetworkclass;
255
256     /* Enumerate through subkeys. */
257     SubkeyIndex = 0;
258     while ((status = ZwEnumerateKey(
259         NetworkClassKeyHandle,
260         SubkeyIndex,
261         KeyBasicInformation,
262         NULL,
263         0,
264         &ResultLength
265       )) != STATUS_NO_MORE_ENTRIES)
266       {
267         if ((status != STATUS_SUCCESS) &&
268             (status != STATUS_BUFFER_OVERFLOW) &&
269             (status != STATUS_BUFFER_TOO_SMALL)
270           )
271           {
272             DBG("ZwEnumerateKey 1 failed (%lx)\n", status);
273             goto e0_1;
274           }
275         if ((KeyInformation = wv_malloc(ResultLength)) == NULL)
276           {
277             DBG("wv_malloc KeyData failed\n");
278             goto e0_1;
279             registry__close_key(NetworkClassKeyHandle);
280           }
281         if (!(NT_SUCCESS(
282             ZwEnumerateKey(
283                 NetworkClassKeyHandle,
284                 SubkeyIndex,
285                 KeyBasicInformation,
286                 KeyInformation,
287                 ResultLength,
288                 &ResultLength
289           ))))
290           {
291             DBG ("ZwEnumerateKey 2 failed\n");
292             goto e0_2;
293           }
294
295         InterfacesKeyStringLength =
296           KeyInformation->NameLength + sizeof InterfacesPath;
297         InterfacesKeyString = wv_malloc(InterfacesKeyStringLength);
298         if (InterfacesKeyString == NULL)
299           {
300             DBG("wv_malloc InterfacesKeyString failed\n");
301             goto e0_2;
302           }
303
304         RtlCopyMemory(
305             InterfacesKeyString,
306             KeyInformation->Name,
307             KeyInformation->NameLength
308           );
309         RtlCopyMemory(
310             InterfacesKeyString +
311               (KeyInformation->NameLength / sizeof (WCHAR)),
312             InterfacesPath,
313             sizeof InterfacesPath
314           );
315         RtlInitUnicodeString(&InterfacesKey, InterfacesKeyString);
316
317         Update = FALSE;
318         InitializeObjectAttributes(
319             &SubKeyObject,
320             &InterfacesKey,
321             OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
322             NetworkClassKeyHandle,
323             NULL
324           );
325         if (NT_SUCCESS(ZwOpenKey(&SubKeyHandle, KEY_ALL_ACCESS, &SubKeyObject)))
326           {
327             if ((status = ZwQueryValueKey(
328                 SubKeyHandle,
329                 &LowerRange,
330                 KeyValuePartialInformation,
331                 NULL,
332                 0,
333                 &ResultLength
334               )) != STATUS_OBJECT_NAME_NOT_FOUND )
335               {
336                 if ((status != STATUS_SUCCESS) &&
337                     (status != STATUS_BUFFER_OVERFLOW) &&
338                     (status != STATUS_BUFFER_TOO_SMALL))
339                   {
340                     DBG(
341                         "ZwQueryValueKey InterfacesKey 1 failed (%lx)\n",
342                         status
343                       );
344                     goto e1_1;
345                   }
346                 if ((KeyValueInformation = wv_malloc(ResultLength)) == NULL)
347                   {
348                     DBG("wv_malloc InterfacesKey KeyValueData failed\n");
349                     goto e1_1;
350                   }
351                 if (!(NT_SUCCESS(ZwQueryValueKey(
352                     SubKeyHandle,
353                     &LowerRange,
354                     KeyValuePartialInformation,
355                     KeyValueInformation,
356                     ResultLength,
357                     &ResultLength
358                   ))))
359                   {
360                     DBG("ZwQueryValueKey InterfacesKey 2 failed\n");
361                     goto e1_2;
362                   }
363                 if (wv_memcmpeq(
364                     L"ethernet",
365                     KeyValueInformation->Data,
366                     sizeof L"ethernet"
367                   ))
368                   Update = TRUE;
369                 wv_free(KeyValueInformation);
370                 if (!NT_SUCCESS(ZwClose(SubKeyHandle)))
371                   {
372                     DBG("ZwClose InterfacesKey SubKeyHandle failed\n");
373                     goto e1_0;
374                   }
375                }
376           }
377         wv_free(InterfacesKeyString);
378
379         if (Update)
380           {
381             LinkageKeyStringLength =
382               KeyInformation->NameLength + sizeof LinkagePath;
383             if ((LinkageKeyString = wv_malloc(LinkageKeyStringLength)) == NULL)
384               {
385                 DBG("wv_malloc LinkageKeyString failed\n");
386                 goto e0_2;
387               }
388             RtlCopyMemory(
389                 LinkageKeyString,
390                 KeyInformation->Name,
391                 KeyInformation->NameLength
392               );
393             RtlCopyMemory(
394                 LinkageKeyString +
395                 (KeyInformation->NameLength / sizeof (WCHAR)),
396                 LinkagePath,
397                 sizeof LinkagePath
398               );
399             RtlInitUnicodeString(&LinkageKey, LinkageKeyString);
400
401             InitializeObjectAttributes(
402                 &SubKeyObject,
403                 &LinkageKey,
404                 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
405                 NetworkClassKeyHandle,
406                 NULL
407               );
408             if (!NT_SUCCESS(ZwCreateKey(
409                 &SubKeyHandle,
410                 KEY_ALL_ACCESS,
411                 &SubKeyObject,
412                 0,
413                 NULL,
414                  REG_OPTION_NON_VOLATILE,
415                 NULL
416               )))
417               {
418                 DBG("ZwCreateKey failed (%lx)\n");
419                 goto e2_0;
420               }
421             if ((status = ZwQueryValueKey(
422                 SubKeyHandle,
423                 &UpperBind,
424                 KeyValuePartialInformation,
425                 NULL,
426                 0,
427                 &ResultLength
428               )) != STATUS_OBJECT_NAME_NOT_FOUND)
429               {
430                 if ((status != STATUS_SUCCESS) &&
431                     (status != STATUS_BUFFER_OVERFLOW) &&
432                     (status != STATUS_BUFFER_TOO_SMALL))
433                   {
434                     DBG("ZwQueryValueKey LinkageKey 1 failed (%lx)\n", status);
435                     goto e2_1;
436                   }
437                 if ((KeyValueInformation = wv_malloc(ResultLength)) == NULL)
438                   {
439                     DBG("wv_malloc LinkageKey KeyValueData failed\n");
440                     goto e2_1;
441                   }
442                 if (!(NT_SUCCESS(ZwQueryValueKey(
443                     SubKeyHandle,
444                     &UpperBind,
445                     KeyValuePartialInformation,
446                     KeyValueInformation,
447                     ResultLength,
448                     &ResultLength
449                   ))))
450                    {
451                     DBG("ZwQueryValueKey LinkageKey 2 failed\n");
452                     goto e2_2;
453                   }
454
455                 Found = FALSE;
456                 for (i = 0;
457                     i < (KeyValueInformation->DataLength -
458                       sizeof winvblock__literal_w / sizeof (WCHAR));
459                     i++)
460                   {
461                     if (wv_memcmpeq(
462                         winvblock__literal_w,
463                         ((PWCHAR)KeyValueInformation->Data) + i,
464                         sizeof winvblock__literal_w
465                       ))
466                       {
467                         Found = TRUE;
468                         break;
469                       } /* if wv_memcmpeq */
470                   } /* for */
471
472                 if (Found)
473                   {
474                     NewValueLength = KeyValueInformation->DataLength;
475                     if ((NewValue = wv_malloc(NewValueLength)) == NULL)
476                       {
477                         DBG("wv_malloc NewValue 1 failed\n");
478                         goto e2_2;
479                       }
480                     RtlCopyMemory(
481                         NewValue,
482                         KeyValueInformation->Data,
483                         KeyValueInformation->DataLength
484                       );
485                   } /* if Found */
486                   else
487                   {
488                     Updated = TRUE;
489                     NewValueLength = KeyValueInformation->DataLength +
490                       sizeof winvblock__literal_w;
491                     if ((NewValue = wv_malloc(NewValueLength)) == NULL)
492                       {
493                         DBG("wv_malloc NewValue 2 failed\n");
494                         goto e2_2;
495                       }
496                     RtlCopyMemory(
497                         NewValue,
498                         winvblock__literal_w,
499                         sizeof winvblock__literal_w
500                       );
501                     RtlCopyMemory(
502                         NewValue +
503                           (sizeof winvblock__literal_w / sizeof (WCHAR)),
504                         KeyValueInformation->Data,
505                         KeyValueInformation->DataLength
506                       );
507                      } /* else !Found */
508                 wv_free(KeyValueInformation);
509               }
510               else
511               {
512                 Updated = TRUE;
513                 NewValueLength = sizeof winvblock__literal_w + sizeof (WCHAR);
514                 if ((NewValue = wv_mallocz(NewValueLength)) == NULL)
515                   {
516                     DBG("wv_mallocz NewValue 3 failed\n");
517                     goto e2_1;
518                   }
519                 RtlCopyMemory(
520                     NewValue,
521                     winvblock__literal_w,
522                     sizeof winvblock__literal_w
523                   );
524               }
525             if (!NT_SUCCESS(ZwSetValueKey(
526                 SubKeyHandle,
527                 &UpperBind,
528                 0,
529                 REG_MULTI_SZ,
530                 NewValue,
531                 NewValueLength
532               )))
533               {
534                 DBG("ZwSetValueKey failed\n");
535                 wv_free(NewValue);
536                 goto e2_1;
537               }
538             wv_free(NewValue);
539             if (!NT_SUCCESS(ZwClose(SubKeyHandle)))
540               {
541                 DBG("ZwClose LinkageKey SubKeyHandle failed\n");
542                 goto e2_0;
543               }
544             wv_free(LinkageKeyString);
545
546             /* Not sure where this comes from. */
547             #if 0
548             start nic (
549             #endif
550             NdiKeyStringLength = KeyInformation->NameLength + sizeof NdiPath;
551             if ((NdiKeyString = wv_malloc(NdiKeyStringLength)) == NULL)
552               {
553                 DBG("wv_malloc NdiKeyString failed\n");
554                 goto e0_2;
555               }
556             RtlCopyMemory(
557                 NdiKeyString,
558                 KeyInformation->Name,
559                 KeyInformation->NameLength
560               );
561             RtlCopyMemory(
562                 NdiKeyString + (KeyInformation->NameLength / sizeof (WCHAR)),
563                 NdiPath,
564                 sizeof NdiPath
565               );
566             RtlInitUnicodeString(&NdiKey, NdiKeyString);
567
568             InitializeObjectAttributes(
569                 &SubKeyObject,
570                 &NdiKey,
571                 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
572                 NetworkClassKeyHandle,
573                 NULL
574               );
575             if (NT_SUCCESS(ZwOpenKey(
576                 &SubKeyHandle,
577                 KEY_ALL_ACCESS,
578                 &SubKeyObject
579               )))
580               {
581                 if ((status = ZwQueryValueKey(
582                     SubKeyHandle,
583                     &Service,
584                     KeyValuePartialInformation,
585                     NULL,
586                     0,
587                     &ResultLength
588                   )) != STATUS_OBJECT_NAME_NOT_FOUND)
589                   {
590                   if ((status != STATUS_SUCCESS) &&
591                       (status != STATUS_BUFFER_OVERFLOW) &&
592                       (status != STATUS_BUFFER_TOO_SMALL))
593                     {
594                       DBG("ZwQueryValueKey NdiKey 1 failed (%lx)\n", status);
595                       goto e3_1;
596                     }
597                   if ((KeyValueInformation = wv_malloc(ResultLength)) == NULL)
598                     {
599                       DBG("wv_malloc NdiKey KeyValueData failed\n");
600                       goto e3_1;
601                     }
602                   if (!(NT_SUCCESS(ZwQueryValueKey(
603                       SubKeyHandle,
604                       &Service,
605                       KeyValuePartialInformation,
606                       KeyValueInformation,
607                       ResultLength,
608                       &ResultLength
609                     ))))
610                     {
611                       DBG("ZwQueryValueKey NdiKey 2 failed\n");
612                       wv_free(KeyValueInformation);
613                       goto e3_1;
614                     }
615                   if (!NT_SUCCESS(ZwClose(SubKeyHandle)))
616                     {
617                       DBG("ZwClose NdiKey SubKeyHandle failed\n");
618                       goto e3_0;
619                     }
620                   DriverServiceNameString = wv_malloc(
621                       sizeof DriverServiceNamePath +
622                         KeyValueInformation->DataLength -
623                         sizeof *DriverServiceNamePath
624                     );
625                   if (DriverServiceNameString == NULL)
626                     {
627                       DBG("wv_malloc DriverServiceNameString failed\n");
628                       goto e3_0;
629                     }
630
631                   RtlCopyMemory(
632                       DriverServiceNameString,
633                       DriverServiceNamePath,
634                       sizeof DriverServiceNamePath
635                     );
636                   RtlCopyMemory(
637                       DriverServiceNameString +
638                         (sizeof DriverServiceNamePath / sizeof (WCHAR)) - 1,
639                       KeyValueInformation->Data,
640                       KeyValueInformation->DataLength
641                     );
642                   RtlInitUnicodeString(
643                       &DriverServiceName,
644                       DriverServiceNameString
645                     );
646                   #if 0
647                   DBG(
648                       "Starting driver %S -> %08x\n",
649                       KeyValueInformation->Data,
650                       ZwLoadDriver(&DriverServiceName)
651                     );
652                   #endif
653                     wv_free(DriverServiceNameString);
654                     wv_free(KeyValueInformation);
655                   }
656               }
657             wv_free(NdiKeyString);
658           }
659         wv_free(KeyInformation);
660         SubkeyIndex++;
661       } /* while */
662     registry__close_key ( NetworkClassKeyHandle );
663     *status_out = STATUS_SUCCESS;
664     return Updated;
665
666     e3_1:
667
668     if (!NT_SUCCESS(ZwClose(SubKeyHandle)))
669       DBG("ZwClose SubKeyHandle failed\n");
670     e3_0:
671
672     wv_free(NdiKeyString);
673     goto e0_2;
674     e2_2:
675
676     wv_free(KeyValueInformation);
677     e2_1:
678
679     if (!NT_SUCCESS(ZwClose(SubKeyHandle)))
680       DBG("ZwClose SubKeyHandle failed\n");
681     e2_0:
682
683     wv_free(LinkageKeyString);
684     goto e0_2;
685     e1_2:
686
687     wv_free(KeyValueInformation);
688     e1_1:
689
690     if (!NT_SUCCESS(ZwClose(SubKeyHandle)))
691       DBG("ZwClose SubKeyHandle failed\n");
692     e1_0:
693
694     wv_free(InterfacesKeyString);
695     goto e0_2;
696     e0_2:
697
698     wv_free(KeyInformation);
699     e0_1:
700
701     registry__close_key(NetworkClassKeyHandle);
702     err_keyopennetworkclass:
703
704     *status_out = STATUS_UNSUCCESSFUL;
705     return FALSE;
706   }
707
708 /**
709  * Start AoE operations.
710  *
711  * @ret Status          Return status code.
712  */
713 NTSTATUS STDCALL DriverEntry(
714     IN PDRIVER_OBJECT DriverObject,
715     IN PUNICODE_STRING RegistryPath
716   ) {
717     NTSTATUS Status;
718     OBJECT_ATTRIBUTES ObjectAttributes;
719     void * ThreadObject;
720     WV_SP_BUS_T bus_ptr;
721
722     DBG("Entry\n");
723
724     if (aoe__started_)
725       return STATUS_SUCCESS;
726     /* Initialize the global list of AoE disks. */
727     InitializeListHead(&aoe__disk_list_);
728     KeInitializeSpinLock(&aoe__disk_list_lock_);
729     /* Setup the Registry. */
730     if (!NT_SUCCESS(setup_reg(&Status))) {
731         DBG("Could not update Registry!\n");
732         return Status;
733       } else {
734         DBG("Registry updated\n");
735       }
736     /* Start up the protocol. */
737     if (!NT_SUCCESS(Status = Protocol_Start())) {
738         DBG("Protocol startup failure!\n");
739         return Status;
740       }
741     /* Allocate and zero-fill the global probe tag. */
742     aoe__probe_tag_ = wv_mallocz(sizeof *aoe__probe_tag_);
743     if (aoe__probe_tag_ == NULL) {
744         DBG("Couldn't allocate probe tag; bye!\n");
745         return STATUS_INSUFFICIENT_RESOURCES;
746       }
747
748     /* Set up the probe tag's AoE packet reference. */
749     aoe__probe_tag_->PacketSize = sizeof (struct aoe__packet_);
750     /* Allocate and zero-fill the probe tag's packet reference. */
751     aoe__probe_tag_->packet_data = wv_mallocz(aoe__probe_tag_->PacketSize);
752     if (aoe__probe_tag_->packet_data == NULL) {
753         DBG("Couldn't allocate aoe__probe_tag_->packet_data\n");
754         wv_free(aoe__probe_tag_);
755         return STATUS_INSUFFICIENT_RESOURCES;
756       }
757     aoe__probe_tag_->SendTime.QuadPart = 0LL;
758
759     /* Initialize the probe tag's AoE packet. */
760     aoe__probe_tag_->packet_data->Ver = AOEPROTOCOLVER;
761     aoe__probe_tag_->packet_data->Major =
762       htons((winvblock__uint16) -1);
763     aoe__probe_tag_->packet_data->Minor = (winvblock__uint8) -1;
764     aoe__probe_tag_->packet_data->Cmd = 0xec;           /* IDENTIFY DEVICE */
765     aoe__probe_tag_->packet_data->Count = 1;
766
767     /* Initialize global target-list spinlock. */
768     KeInitializeSpinLock(&aoe__target_list_spinlock_);
769
770     /* Initialize global spin-lock and global thread signal event. */
771     KeInitializeSpinLock(&aoe__spinlock_);
772     KeInitializeEvent(&aoe__thread_sig_evt_, SynchronizationEvent, FALSE);
773
774     /* Initialize object attributes for thread. */
775     InitializeObjectAttributes(
776         &ObjectAttributes,
777         NULL,
778         OBJ_KERNEL_HANDLE,
779         NULL,
780         NULL
781       );
782
783     /* Create global thread. */
784     if (!NT_SUCCESS(Status = PsCreateSystemThread(
785         &aoe__thread_handle_,
786         THREAD_ALL_ACCESS,
787         &ObjectAttributes,
788         NULL,
789         NULL,
790         aoe__thread_,
791         NULL
792       )))
793       return Error("PsCreateSystemThread", Status);
794
795     if (!NT_SUCCESS(Status = ObReferenceObjectByHandle(
796         aoe__thread_handle_,
797         THREAD_ALL_ACCESS,
798         NULL,
799         KernelMode,
800         &ThreadObject,
801         NULL
802       ))) {
803         ZwClose(aoe__thread_handle_);
804         Error("ObReferenceObjectByHandle", Status);
805         aoe__stop_ = TRUE;
806         KeSetEvent(&aoe__thread_sig_evt_, 0, FALSE);
807       }
808
809     DriverObject->DriverUnload = aoe__unload_;
810     aoe__started_ = TRUE;
811     if (!aoe_bus__create()) {
812         DBG("Unable to create AoE bus!\n");
813         aoe__unload_(DriverObject);
814         return STATUS_INSUFFICIENT_RESOURCES;
815       }
816     aoe__process_abft_();
817     DBG("Exit\n");
818     return Status;
819   }
820
821 /**
822  * Stop AoE operations.
823  */
824 static void STDCALL aoe__unload_(IN PDRIVER_OBJECT DriverObject) {
825     NTSTATUS Status;
826     struct aoe__disk_search_ * disk_searcher, * previous_disk_searcher;
827     struct aoe__work_tag_ * tag;
828     KIRQL Irql, Irql2;
829     struct aoe__target_list_ * Walker, * Next;
830
831     DBG("Entry\n");
832     /* If we're not already started, there's nothing to do. */
833     if (!aoe__started_)
834       return;
835     /* Destroy the AoE bus. */
836     aoe_bus__free();
837     /* Stop the AoE protocol. */
838     Protocol_Stop();
839     /* If we're not already shutting down, signal the event. */
840     if (!aoe__stop_) {
841         aoe__stop_ = TRUE;
842         KeSetEvent(&aoe__thread_sig_evt_, 0, FALSE);
843         /* Wait until the event has been signalled. */
844         if (!NT_SUCCESS(Status = ZwWaitForSingleObject(
845             aoe__thread_handle_,
846             FALSE,
847             NULL
848           )))
849           Error("AoE_Stop ZwWaitForSingleObject", Status);
850         ZwClose(aoe__thread_handle_);
851       }
852
853     /* Free the target list. */
854     KeAcquireSpinLock(&aoe__target_list_spinlock_, &Irql2);
855     Walker = aoe__target_list_;
856     while (Walker != NULL) {
857         Next = Walker->next;
858         wv_free(Walker);
859         Walker = Next;
860       }
861     KeReleaseSpinLock(&aoe__target_list_spinlock_, Irql2);
862
863     /* Wait until we have the global spin-lock. */
864     KeAcquireSpinLock(&aoe__spinlock_, &Irql);
865
866     /* Free disk searches in the global disk search list. */
867     disk_searcher = aoe__disk_search_list_;
868     while (disk_searcher != NULL) {
869         KeSetEvent(
870             &(disk__get_ptr(disk_searcher->device)->SearchEvent),
871             0,
872             FALSE
873           );
874         previous_disk_searcher = disk_searcher;
875         disk_searcher = disk_searcher->next;
876         wv_free(previous_disk_searcher);
877       }
878
879     /* Cancel and free all tags in the global tag list. */
880     tag = aoe__tag_list_;
881     while (tag != NULL) {
882         if (tag->request_ptr != NULL && --tag->request_ptr->TagCount == 0) {
883             tag->request_ptr->Irp->IoStatus.Information = 0;
884             tag->request_ptr->Irp->IoStatus.Status = STATUS_CANCELLED;
885             IoCompleteRequest(tag->request_ptr->Irp, IO_NO_INCREMENT);
886             wv_free(tag->request_ptr);
887           }
888         if (tag->next == NULL) {
889             wv_free(tag->packet_data);
890             wv_free(tag);
891             tag = NULL;
892           } else {
893             tag = tag->next;
894             wv_free(tag->previous->packet_data);
895             wv_free(tag->previous);
896           }
897       }
898     aoe__tag_list_ = NULL;
899     aoe__tag_list_last_ = NULL;
900
901     /* Free the global probe tag and its AoE packet. */
902     wv_free(aoe__probe_tag_->packet_data);
903     wv_free(aoe__probe_tag_);
904
905     /* Release the global spin-lock. */
906     KeReleaseSpinLock(&aoe__spinlock_, Irql);
907     aoe_bus__free();
908     aoe__started_ = FALSE;
909     DBG("Exit\n");
910   }
911
912 /**
913  * Search for disk parameters.
914  *
915  * @v dev_ptr           The device extension for the disk.
916  *
917  * Returns TRUE if the disk could be matched, FALSE otherwise.
918  */
919 static disk__init_decl(init)
920   {
921     struct aoe__disk_search_
922       * disk_searcher, * disk_search_walker, * previous_disk_searcher;
923     LARGE_INTEGER Timeout, CurrentTime;
924     struct aoe__work_tag_ * tag, * tag_walker;
925     KIRQL Irql, InnerIrql;
926     LARGE_INTEGER MaxSectorsPerPacketSendTime;
927     winvblock__uint32 MTU;
928     struct aoe__disk_type_ * aoe_disk_ptr;
929
930     aoe_disk_ptr = aoe__get_ ( disk_ptr->device );
931     /*
932      * Allocate our disk search 
933      */
934     if ((disk_searcher = wv_malloc(sizeof *disk_searcher)) == NULL) {
935         DBG ( "Couldn't allocate for disk_searcher; bye!\n" );
936         return FALSE;
937       }
938
939     /*
940      * Initialize the disk search 
941      */
942     disk_searcher->device = disk_ptr->device;
943     disk_searcher->next = NULL;
944     aoe_disk_ptr->search_state = aoe__search_state_search_nic_;
945     KeResetEvent ( &disk_ptr->SearchEvent );
946
947     /*
948      * Wait until we have the global spin-lock 
949      */
950     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
951
952     /*
953      * Add our disk search to the global list of disk searches 
954      */
955     if ( aoe__disk_search_list_ == NULL )
956       {
957         aoe__disk_search_list_ = disk_searcher;
958       }
959     else
960       {
961         disk_search_walker = aoe__disk_search_list_;
962         while ( disk_search_walker->next )
963     disk_search_walker = disk_search_walker->next;
964         disk_search_walker->next = disk_searcher;
965       }
966
967     /*
968      * Release the global spin-lock 
969      */
970     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
971
972     /*
973      * We go through all the states until the disk is ready for use 
974      */
975     while ( TRUE )
976       {
977         /*
978          * Wait for our device's extension's search to be signalled 
979          */
980         /*
981          * TODO: Make the below value a #defined constant 
982          */
983         /*
984          * 500.000 * 100ns = 50.000.000 ns = 50ms 
985          */
986         Timeout.QuadPart = -500000LL;
987         KeWaitForSingleObject ( &disk_ptr->SearchEvent, Executive, KernelMode,
988               FALSE, &Timeout );
989         if ( aoe__stop_ )
990     {
991       DBG ( "AoE is shutting down; bye!\n" );
992       return FALSE;
993     }
994
995         /*
996          * Wait until we have the device extension's spin-lock 
997          */
998         KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
999
1000         if (aoe_disk_ptr->search_state == aoe__search_state_search_nic_)
1001     {
1002       if ( !Protocol_SearchNIC ( aoe_disk_ptr->ClientMac ) )
1003         {
1004           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1005           continue;
1006         }
1007       else
1008         {
1009           /*
1010            * We found the adapter to use, get MTU next 
1011            */
1012           aoe_disk_ptr->MTU = Protocol_GetMTU ( aoe_disk_ptr->ClientMac );
1013           aoe_disk_ptr->search_state = aoe__search_state_get_size_;
1014         }
1015     }
1016
1017         if (aoe_disk_ptr->search_state == aoe__search_state_getting_size_)
1018     {
1019       /*
1020        * Still getting the disk's size 
1021        */
1022       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1023       continue;
1024     }
1025         if (aoe_disk_ptr->search_state == aoe__search_state_getting_geometry_)
1026     {
1027       /*
1028        * Still getting the disk's geometry 
1029        */
1030       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1031       continue;
1032     }
1033         if (aoe_disk_ptr->search_state ==
1034           aoe__search_state_getting_max_sectors_per_packet_)
1035     {
1036       KeQuerySystemTime ( &CurrentTime );
1037       /*
1038        * TODO: Make the below value a #defined constant 
1039        */
1040       /*
1041        * 2.500.000 * 100ns = 250.000.000 ns = 250ms 
1042        */
1043       if ( CurrentTime.QuadPart >
1044            MaxSectorsPerPacketSendTime.QuadPart + 2500000LL )
1045         {
1046           DBG ( "No reply after 250ms for MaxSectorsPerPacket %d, "
1047           "giving up\n", aoe_disk_ptr->MaxSectorsPerPacket );
1048           aoe_disk_ptr->MaxSectorsPerPacket--;
1049           aoe_disk_ptr->search_state = aoe__search_state_done_;
1050         }
1051       else
1052         {
1053           /*
1054            * Still getting the maximum sectors per packet count 
1055            */
1056           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1057           continue;
1058         }
1059     }
1060
1061         if (aoe_disk_ptr->search_state == aoe__search_state_done_)
1062     {
1063       /*
1064        * We've finished the disk search; perform clean-up 
1065        */
1066       KeAcquireSpinLock ( &aoe__spinlock_, &InnerIrql );
1067
1068       /*
1069        * Tag clean-up: Find out if our tag is in the global tag list 
1070        */
1071       tag_walker = aoe__tag_list_;
1072       while ( tag_walker != NULL && tag_walker != tag )
1073         tag_walker = tag_walker->next;
1074       if ( tag_walker != NULL )
1075         {
1076           /*
1077            * We found it.  If it's at the beginning of the list, adjust
1078            * the list to point the the next tag
1079            */
1080           if ( tag->previous == NULL )
1081       aoe__tag_list_ = tag->next;
1082           else
1083       /*
1084        * Remove our tag from the list 
1085        */
1086       tag->previous->next = tag->next;
1087           /*
1088            * If we 're at the end of the list, adjust the list's end to
1089            * point to the penultimate tag
1090            */
1091           if ( tag->next == NULL )
1092       aoe__tag_list_last_ = tag->previous;
1093           else
1094       /*
1095        * Remove our tag from the list 
1096        */
1097       tag->next->previous = tag->previous;
1098           aoe__outstanding_tags_--;
1099           if ( aoe__outstanding_tags_ < 0 )
1100       DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1101           /*
1102            * Free our tag and its AoE packet 
1103            */
1104           wv_free(tag->packet_data);
1105           wv_free(tag);
1106         }
1107
1108       /*
1109        * Disk search clean-up 
1110        */
1111       if ( aoe__disk_search_list_ == NULL )
1112         {
1113           DBG ( "aoe__disk_search_list_ == NULL!!\n" );
1114         }
1115       else
1116         {
1117           /*
1118            * Find our disk search in the global list of disk searches 
1119            */
1120           disk_search_walker = aoe__disk_search_list_;
1121           while ( disk_search_walker
1122             && disk_search_walker->device != disk_ptr->device )
1123       {
1124         previous_disk_searcher = disk_search_walker;
1125         disk_search_walker = disk_search_walker->next;
1126       }
1127           if ( disk_search_walker )
1128       {
1129         /*
1130          * We found our disk search.  If it's the first one in
1131          * the list, adjust the list and remove it
1132          */
1133         if ( disk_search_walker == aoe__disk_search_list_ )
1134           aoe__disk_search_list_ = disk_search_walker->next;
1135         else
1136           /*
1137            * Just remove it 
1138            */
1139           previous_disk_searcher->next = disk_search_walker->next;
1140         /*
1141          * Free our disk search 
1142          */
1143         wv_free(disk_search_walker);
1144       }
1145           else
1146       {
1147         DBG ( "Disk not found in aoe__disk_search_list_!!\n" );
1148       }
1149         }
1150
1151       /*
1152        * Release global and device extension spin-locks 
1153        */
1154       KeReleaseSpinLock ( &aoe__spinlock_, InnerIrql );
1155       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1156
1157       DBG ( "Disk size: %I64uM cylinders: %I64u heads: %u "
1158       "sectors: %u sectors per packet: %u\n",
1159       disk_ptr->LBADiskSize / 2048, disk_ptr->Cylinders,
1160       disk_ptr->Heads, disk_ptr->Sectors,
1161       aoe_disk_ptr->MaxSectorsPerPacket );
1162       return TRUE;
1163     }
1164
1165         #if 0
1166         if ( aoe_disk_ptr->search_state == aoe__search_state_done_)
1167         #endif
1168         /*
1169          * Establish our tag 
1170          */
1171         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
1172       DBG ( "Couldn't allocate tag\n" );
1173       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1174       /*
1175        * Maybe next time around 
1176        */
1177       continue;
1178     }
1179         tag->type = aoe__tag_type_search_drive_;
1180         tag->device = disk_ptr->device;
1181
1182         /*
1183          * Establish our tag's AoE packet 
1184          */
1185         tag->PacketSize = sizeof (struct aoe__packet_);
1186         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
1187       DBG ( "Couldn't allocate tag->packet_data\n" );
1188       wv_free(tag);
1189       tag = NULL;
1190       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1191       /*
1192        * Maybe next time around 
1193        */
1194       continue;
1195     }
1196         tag->packet_data->Ver = AOEPROTOCOLVER;
1197         tag->packet_data->Major =
1198     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
1199         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
1200         tag->packet_data->ExtendedAFlag = TRUE;
1201
1202         /*
1203          * Initialize the packet appropriately based on our current phase 
1204          */
1205         switch ( aoe_disk_ptr->search_state )
1206     {
1207       case aoe__search_state_get_size_:
1208         /*
1209          * TODO: Make the below value into a #defined constant 
1210          */
1211         tag->packet_data->Cmd = 0xec;  /* IDENTIFY DEVICE */
1212         tag->packet_data->Count = 1;
1213         aoe_disk_ptr->search_state = aoe__search_state_getting_size_;
1214         break;
1215       case aoe__search_state_get_geometry_:
1216         /*
1217          * TODO: Make the below value into a #defined constant 
1218          */
1219         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
1220         tag->packet_data->Count = 1;
1221         aoe_disk_ptr->search_state = aoe__search_state_getting_geometry_;
1222         break;
1223       case aoe__search_state_get_max_sectors_per_packet_:
1224         /*
1225          * TODO: Make the below value into a #defined constant 
1226          */
1227         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
1228         tag->packet_data->Count =
1229           ( winvblock__uint8 ) ( ++aoe_disk_ptr->MaxSectorsPerPacket );
1230         KeQuerySystemTime ( &MaxSectorsPerPacketSendTime );
1231         aoe_disk_ptr->search_state =
1232           aoe__search_state_getting_max_sectors_per_packet_;
1233         /*
1234          * TODO: Make the below value into a #defined constant 
1235          */
1236         aoe_disk_ptr->Timeout = 200000;
1237         break;
1238       default:
1239         DBG ( "Undefined search_state!!\n" );
1240         wv_free(tag->packet_data);
1241         wv_free(tag);
1242         /*
1243          * TODO: Do we need to nullify tag here? 
1244          */
1245         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1246         continue;
1247         break;
1248     }
1249
1250         /*
1251          * Enqueue our tag 
1252          */
1253         tag->next = NULL;
1254         KeAcquireSpinLock ( &aoe__spinlock_, &InnerIrql );
1255         if ( aoe__tag_list_ == NULL )
1256     {
1257       aoe__tag_list_ = tag;
1258       tag->previous = NULL;
1259     }
1260         else
1261     {
1262       aoe__tag_list_last_->next = tag;
1263       tag->previous = aoe__tag_list_last_;
1264     }
1265         aoe__tag_list_last_ = tag;
1266         KeReleaseSpinLock ( &aoe__spinlock_, InnerIrql );
1267         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1268       }
1269   }
1270
1271 static disk__io_decl(io)
1272   {
1273     struct aoe__io_req_ * request_ptr;
1274     struct aoe__work_tag_ * tag, * new_tag_list = NULL, * previous_tag = NULL;
1275     KIRQL Irql;
1276     winvblock__uint32 i;
1277     PHYSICAL_ADDRESS PhysicalAddress;
1278     winvblock__uint8_ptr PhysicalMemory;
1279     disk__type_ptr disk_ptr;
1280     struct aoe__disk_type_ * aoe_disk_ptr;
1281
1282     /*
1283      * Establish pointers to the disk and AoE disk
1284      */
1285     disk_ptr = disk__get_ptr ( dev_ptr );
1286     aoe_disk_ptr = aoe__get_ ( dev_ptr );
1287
1288     if ( aoe__stop_ )
1289       {
1290         /*
1291          * Shutting down AoE; we can't service this request 
1292          */
1293         irp->IoStatus.Information = 0;
1294         irp->IoStatus.Status = STATUS_CANCELLED;
1295         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1296         return STATUS_CANCELLED;
1297       }
1298
1299     if ( sector_count < 1 )
1300       {
1301         /*
1302          * A silly request 
1303          */
1304         DBG ( "sector_count < 1; cancelling\n" );
1305         irp->IoStatus.Information = 0;
1306         irp->IoStatus.Status = STATUS_CANCELLED;
1307         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1308         return STATUS_CANCELLED;
1309       }
1310
1311     /*
1312      * Allocate and zero-fill our request 
1313      */
1314     if ((request_ptr = wv_mallocz(sizeof *request_ptr)) == NULL) {
1315         DBG ( "Couldn't allocate for reques_ptr; bye!\n" );
1316         irp->IoStatus.Information = 0;
1317         irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1318         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1319         return STATUS_INSUFFICIENT_RESOURCES;
1320       }
1321
1322     /*
1323      * Initialize the request 
1324      */
1325     request_ptr->Mode = mode;
1326     request_ptr->SectorCount = sector_count;
1327     request_ptr->Buffer = buffer;
1328     request_ptr->Irp = irp;
1329     request_ptr->TagCount = 0;
1330
1331     /*
1332      * Split the requested sectors into packets in tags
1333      */
1334     for ( i = 0; i < sector_count; i += aoe_disk_ptr->MaxSectorsPerPacket )
1335       {
1336         /*
1337          * Allocate each tag 
1338          */
1339         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
1340       DBG ( "Couldn't allocate tag; bye!\n" );
1341       /*
1342        * We failed while allocating tags; free the ones we built 
1343        */
1344       tag = new_tag_list;
1345       while ( tag != NULL )
1346         {
1347           previous_tag = tag;
1348           tag = tag->next;
1349           wv_free(previous_tag->packet_data);
1350           wv_free(previous_tag);
1351         }
1352       wv_free(request_ptr);
1353       irp->IoStatus.Information = 0;
1354       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1355       IoCompleteRequest ( irp, IO_NO_INCREMENT );
1356       return STATUS_INSUFFICIENT_RESOURCES;
1357     }
1358
1359         /*
1360          * Initialize each tag 
1361          */
1362         tag->type = aoe__tag_type_io_;
1363         tag->request_ptr = request_ptr;
1364         tag->device = dev_ptr;
1365         request_ptr->TagCount++;
1366         tag->Id = 0;
1367         tag->BufferOffset = i * disk_ptr->SectorSize;
1368         tag->SectorCount =
1369     ( ( sector_count - i ) <
1370       aoe_disk_ptr->MaxSectorsPerPacket ? sector_count -
1371       i : aoe_disk_ptr->MaxSectorsPerPacket );
1372
1373         /*
1374          * Allocate and initialize each tag's AoE packet 
1375          */
1376         tag->PacketSize = sizeof (struct aoe__packet_);
1377         if ( mode == disk__io_mode_write )
1378     tag->PacketSize += tag->SectorCount * disk_ptr->SectorSize;
1379         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
1380       DBG ( "Couldn't allocate tag->packet_data; bye!\n" );
1381       /*
1382        * We failed while allocating an AoE packet; free
1383        * the tags we built
1384        */
1385       wv_free(tag);
1386       tag = new_tag_list;
1387       while ( tag != NULL )
1388         {
1389           previous_tag = tag;
1390           tag = tag->next;
1391           wv_free(previous_tag->packet_data);
1392           wv_free(previous_tag);
1393         }
1394       wv_free(request_ptr);
1395       irp->IoStatus.Information = 0;
1396       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1397       IoCompleteRequest ( irp, IO_NO_INCREMENT );
1398       return STATUS_INSUFFICIENT_RESOURCES;
1399     }
1400         tag->packet_data->Ver = AOEPROTOCOLVER;
1401         tag->packet_data->Major =
1402     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
1403         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
1404         tag->packet_data->Tag = 0;
1405         tag->packet_data->Command = 0;
1406         tag->packet_data->ExtendedAFlag = TRUE;
1407         if ( mode == disk__io_mode_read )
1408     {
1409       tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
1410     }
1411         else
1412     {
1413       tag->packet_data->Cmd = 0x34;  /* WRITE SECTOR */
1414       tag->packet_data->WriteAFlag = 1;
1415     }
1416         tag->packet_data->Count = ( winvblock__uint8 ) tag->SectorCount;
1417         tag->packet_data->Lba0 =
1418     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 0 ) & 255 );
1419         tag->packet_data->Lba1 =
1420     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 8 ) & 255 );
1421         tag->packet_data->Lba2 =
1422     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 16 ) & 255 );
1423         tag->packet_data->Lba3 =
1424     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 24 ) & 255 );
1425         tag->packet_data->Lba4 =
1426     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 32 ) & 255 );
1427         tag->packet_data->Lba5 =
1428     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 40 ) & 255 );
1429
1430         /*
1431          * For a write request, copy from the buffer into the AoE packet 
1432          */
1433         if ( mode == disk__io_mode_write )
1434     RtlCopyMemory ( tag->packet_data->Data, &buffer[tag->BufferOffset],
1435         tag->SectorCount * disk_ptr->SectorSize );
1436
1437         /*
1438          * Add this tag to the request's tag list 
1439          */
1440         tag->previous = previous_tag;
1441         tag->next = NULL;
1442         if ( new_tag_list == NULL )
1443     {
1444       new_tag_list = tag;
1445     }
1446         else
1447     {
1448       previous_tag->next = tag;
1449     }
1450         previous_tag = tag;
1451       }
1452     /*
1453      * Split the requested sectors into packets in tags
1454      */
1455     request_ptr->TotalTags = request_ptr->TagCount;
1456
1457     /*
1458      * Wait until we have the global spin-lock 
1459      */
1460     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1461
1462     /*
1463      * Enqueue our request's tag list to the global tag list 
1464      */
1465     if ( aoe__tag_list_last_ == NULL )
1466       {
1467         aoe__tag_list_ = new_tag_list;
1468       }
1469     else
1470       {
1471         aoe__tag_list_last_->next = new_tag_list;
1472         new_tag_list->previous = aoe__tag_list_last_;
1473       }
1474     /*
1475      * Adjust the global list to reflect our last tag 
1476      */
1477     aoe__tag_list_last_ = tag;
1478
1479     irp->IoStatus.Information = 0;
1480     irp->IoStatus.Status = STATUS_PENDING;
1481     IoMarkIrpPending ( irp );
1482
1483     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1484     KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1485     return STATUS_PENDING;
1486   }
1487
1488 static void STDCALL add_target(
1489     IN winvblock__uint8_ptr ClientMac,
1490     IN winvblock__uint8_ptr ServerMac,
1491     winvblock__uint16 Major,
1492     winvblock__uint8 Minor,
1493     LONGLONG LBASize
1494   )
1495   {
1496     struct aoe__target_list_ * Walker, * Last;
1497     KIRQL Irql;
1498
1499     KeAcquireSpinLock ( &aoe__target_list_spinlock_, &Irql );
1500     Walker = Last = aoe__target_list_;
1501     while ( Walker != NULL )
1502       {
1503         if (wv_memcmpeq(&Walker->Target.ClientMac, ClientMac, 6) &&
1504           wv_memcmpeq(&Walker->Target.ServerMac, ServerMac, 6) &&
1505           Walker->Target.Major == Major
1506           && Walker->Target.Minor == Minor) {
1507       if ( Walker->Target.LBASize != LBASize )
1508         {
1509           DBG ( "LBASize changed for e%d.%d " "(%I64u->%I64u)\n", Major,
1510           Minor, Walker->Target.LBASize, LBASize );
1511           Walker->Target.LBASize = LBASize;
1512         }
1513       KeQuerySystemTime ( &Walker->Target.ProbeTime );
1514       KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1515       return;
1516     }
1517         Last = Walker;
1518         Walker = Walker->next;
1519       }
1520
1521     if ((Walker = wv_malloc(sizeof *Walker)) == NULL) {
1522         DBG("wv_malloc Walker\n");
1523         KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1524         return;
1525       }
1526     Walker->next = NULL;
1527     RtlCopyMemory ( Walker->Target.ClientMac, ClientMac, 6 );
1528     RtlCopyMemory ( Walker->Target.ServerMac, ServerMac, 6 );
1529     Walker->Target.Major = Major;
1530     Walker->Target.Minor = Minor;
1531     Walker->Target.LBASize = LBASize;
1532     KeQuerySystemTime ( &Walker->Target.ProbeTime );
1533
1534     if ( Last == NULL )
1535       {
1536         aoe__target_list_ = Walker;
1537       }
1538     else
1539       {
1540         Last->next = Walker;
1541       }
1542     KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1543   }
1544
1545 /**
1546  * Process an AoE reply.
1547  *
1548  * @v SourceMac         The AoE server's MAC address.
1549  * @v DestinationMac    The AoE client's MAC address.
1550  * @v Data              The AoE packet.
1551  * @v DataSize          The AoE packet's size.
1552  */
1553 NTSTATUS STDCALL aoe__reply(
1554     IN winvblock__uint8_ptr SourceMac,
1555     IN winvblock__uint8_ptr DestinationMac,
1556     IN winvblock__uint8_ptr Data,
1557     IN winvblock__uint32 DataSize
1558   )
1559   {
1560     struct aoe__packet_ * reply = (struct aoe__packet_ *) Data;
1561     LONGLONG LBASize;
1562     struct aoe__work_tag_ * tag;
1563     KIRQL Irql;
1564     winvblock__bool Found = FALSE;
1565     LARGE_INTEGER CurrentTime;
1566     disk__type_ptr disk_ptr;
1567     struct aoe__disk_type_ * aoe_disk_ptr;
1568
1569     /*
1570      * Discard non-responses 
1571      */
1572     if ( !reply->ResponseFlag )
1573       return STATUS_SUCCESS;
1574
1575     /*
1576      * If the response matches our probe, add the AoE disk device 
1577      */
1578     if ( aoe__probe_tag_->Id == reply->Tag )
1579       {
1580         RtlCopyMemory ( &LBASize, &reply->Data[200], sizeof ( LONGLONG ) );
1581         add_target ( DestinationMac, SourceMac, ntohs ( reply->Major ),
1582          reply->Minor, LBASize );
1583         return STATUS_SUCCESS;
1584       }
1585
1586     /*
1587      * Wait until we have the global spin-lock 
1588      */
1589     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1590
1591     /*
1592      * Search for request tag 
1593      */
1594     if ( aoe__tag_list_ == NULL )
1595       {
1596         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1597         return STATUS_SUCCESS;
1598       }
1599     tag = aoe__tag_list_;
1600     while ( tag != NULL )
1601       {
1602         if ( ( tag->Id == reply->Tag )
1603        && ( tag->packet_data->Major == reply->Major )
1604        && ( tag->packet_data->Minor == reply->Minor ) )
1605     {
1606       Found = TRUE;
1607       break;
1608     }
1609         tag = tag->next;
1610       }
1611     if ( !Found )
1612       {
1613         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1614         return STATUS_SUCCESS;
1615       }
1616     else
1617       {
1618         /*
1619          * Remove the tag from the global tag list 
1620          */
1621         if ( tag->previous == NULL )
1622     aoe__tag_list_ = tag->next;
1623         else
1624     tag->previous->next = tag->next;
1625         if ( tag->next == NULL )
1626     aoe__tag_list_last_ = tag->previous;
1627         else
1628     tag->next->previous = tag->previous;
1629         aoe__outstanding_tags_--;
1630         if ( aoe__outstanding_tags_ < 0 )
1631     DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1632         KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1633       }
1634     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1635
1636     /*
1637      * Establish pointers to the disk device and AoE disk
1638      */
1639     disk_ptr = disk__get_ptr ( tag->device );
1640     aoe_disk_ptr = aoe__get_ ( tag->device );
1641
1642     /*
1643      * If our tag was a discovery request, note the server 
1644      */
1645     if (wv_memcmpeq(aoe_disk_ptr->ServerMac, "\xff\xff\xff\xff\xff\xff", 6)) {
1646         RtlCopyMemory ( aoe_disk_ptr->ServerMac, SourceMac, 6 );
1647         DBG ( "Major: %d minor: %d found on server "
1648         "%02x:%02x:%02x:%02x:%02x:%02x\n", aoe_disk_ptr->Major,
1649         aoe_disk_ptr->Minor, SourceMac[0], SourceMac[1], SourceMac[2],
1650         SourceMac[3], SourceMac[4], SourceMac[5] );
1651       }
1652
1653     KeQuerySystemTime ( &CurrentTime );
1654     aoe_disk_ptr->Timeout -=
1655       ( winvblock__uint32 ) ( ( aoe_disk_ptr->Timeout -
1656               ( CurrentTime.QuadPart -
1657           tag->FirstSendTime.QuadPart ) ) / 1024 );
1658     /*
1659      * TODO: Replace the values below with #defined constants 
1660      */
1661     if ( aoe_disk_ptr->Timeout > 100000000 )
1662       aoe_disk_ptr->Timeout = 100000000;
1663
1664     switch ( tag->type )
1665       {
1666         case aoe__tag_type_search_drive_:
1667     KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
1668     switch ( aoe_disk_ptr->search_state )
1669       {
1670         case aoe__search_state_getting_size_:
1671           /*
1672            * The reply tells us the disk size
1673            */
1674           RtlCopyMemory ( &disk_ptr->LBADiskSize, &reply->Data[200],
1675               sizeof ( LONGLONG ) );
1676           /*
1677            * Next we are concerned with the disk geometry
1678            */
1679           aoe_disk_ptr->search_state = aoe__search_state_get_geometry_;
1680           break;
1681         case aoe__search_state_getting_geometry_:
1682           /*
1683            * FIXME: use real values from partition table.
1684            * We used to truncate a fractional end cylinder, but
1685            * now leave it be in the hopes everyone uses LBA
1686            */
1687           disk_ptr->SectorSize = 512;
1688           disk_ptr->Heads = 255;
1689           disk_ptr->Sectors = 63;
1690           disk_ptr->Cylinders =
1691       disk_ptr->LBADiskSize / ( disk_ptr->Heads *
1692               disk_ptr->Sectors );
1693           /*
1694            * Next we are concerned with the maximum sectors per packet
1695            */
1696           aoe_disk_ptr->search_state =
1697             aoe__search_state_get_max_sectors_per_packet_;
1698           break;
1699         case aoe__search_state_getting_max_sectors_per_packet_:
1700           DataSize -= sizeof (struct aoe__packet_);
1701           if ( DataSize <
1702          ( aoe_disk_ptr->MaxSectorsPerPacket *
1703            disk_ptr->SectorSize ) )
1704       {
1705         DBG ( "Packet size too low while getting "
1706         "MaxSectorsPerPacket (tried %d, got size of %d)\n",
1707         aoe_disk_ptr->MaxSectorsPerPacket, DataSize );
1708         aoe_disk_ptr->MaxSectorsPerPacket--;
1709         aoe_disk_ptr->search_state = aoe__search_state_done_;
1710       }
1711           else if ( aoe_disk_ptr->MTU <
1712         ( sizeof (struct aoe__packet_) +
1713           ( ( aoe_disk_ptr->MaxSectorsPerPacket +
1714               1 ) * disk_ptr->SectorSize ) ) )
1715       {
1716         DBG ( "Got MaxSectorsPerPacket %d at size of %d. "
1717         "MTU of %d reached\n",
1718         aoe_disk_ptr->MaxSectorsPerPacket, DataSize,
1719         aoe_disk_ptr->MTU );
1720         aoe_disk_ptr->search_state = aoe__search_state_done_;
1721       }
1722           else
1723       {
1724         DBG ( "Got MaxSectorsPerPacket %d at size of %d, "
1725         "trying next...\n", aoe_disk_ptr->MaxSectorsPerPacket,
1726         DataSize );
1727         aoe_disk_ptr->search_state =
1728           aoe__search_state_get_max_sectors_per_packet_;
1729       }
1730           break;
1731         default:
1732           DBG ( "Undefined search_state!\n" );
1733           break;
1734       }
1735     KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1736     KeSetEvent ( &disk_ptr->SearchEvent, 0, FALSE );
1737     break;
1738         case aoe__tag_type_io_:
1739     /*
1740      * If the reply is in response to a read request, get our data! 
1741      */
1742     if ( tag->request_ptr->Mode == disk__io_mode_read )
1743       RtlCopyMemory ( &tag->request_ptr->Buffer[tag->BufferOffset],
1744           reply->Data,
1745           tag->SectorCount * disk_ptr->SectorSize );
1746     /*
1747      * If this is the last reply expected for the read request,
1748      * complete the IRP and free the request
1749      */
1750     if ( InterlockedDecrement ( &tag->request_ptr->TagCount ) == 0 )
1751       {
1752         tag->request_ptr->Irp->IoStatus.Information =
1753           tag->request_ptr->SectorCount * disk_ptr->SectorSize;
1754         tag->request_ptr->Irp->IoStatus.Status = STATUS_SUCCESS;
1755         Driver_CompletePendingIrp ( tag->request_ptr->Irp );
1756         wv_free(tag->request_ptr);
1757       }
1758     break;
1759         default:
1760     DBG ( "Unknown tag type!!\n" );
1761     break;
1762       }
1763
1764     KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1765     wv_free(tag->packet_data);
1766     wv_free(tag);
1767     return STATUS_SUCCESS;
1768   }
1769
1770 void aoe__reset_probe(void)
1771   {
1772     aoe__probe_tag_->SendTime.QuadPart = 0LL;
1773   }
1774
1775 static void STDCALL aoe__thread_(IN void *StartContext)
1776   {
1777     LARGE_INTEGER Timeout, CurrentTime, ProbeTime, ReportTime;
1778     winvblock__uint32 NextTagId = 1;
1779     struct aoe__work_tag_ * tag;
1780     KIRQL Irql;
1781     winvblock__uint32 Sends = 0;
1782     winvblock__uint32 Resends = 0;
1783     winvblock__uint32 ResendFails = 0;
1784     winvblock__uint32 Fails = 0;
1785     winvblock__uint32 RequestTimeout = 0;
1786     disk__type_ptr disk_ptr;
1787     struct aoe__disk_type_ * aoe_disk_ptr;
1788
1789     DBG ( "Entry\n" );
1790     ReportTime.QuadPart = 0LL;
1791     ProbeTime.QuadPart = 0LL;
1792
1793     while ( TRUE )
1794       {
1795         /*
1796          * TODO: Make the below value a #defined constant 
1797          */
1798         /*
1799          * 100.000 * 100ns = 10.000.000 ns = 10ms
1800          */
1801         Timeout.QuadPart = -100000LL;
1802         KeWaitForSingleObject ( &aoe__thread_sig_evt_, Executive,
1803               KernelMode, FALSE, &Timeout );
1804         KeResetEvent ( &aoe__thread_sig_evt_ );
1805         if ( aoe__stop_ )
1806     {
1807       DBG ( "Stopping...\n" );
1808       PsTerminateSystemThread ( STATUS_SUCCESS );
1809     }
1810
1811         KeQuerySystemTime ( &CurrentTime );
1812         /*
1813          * TODO: Make the below value a #defined constant 
1814          */
1815         if ( CurrentTime.QuadPart > ( ReportTime.QuadPart + 10000000LL ) )
1816     {
1817       DBG ( "Sends: %d  Resends: %d  ResendFails: %d  Fails: %d  "
1818       "aoe__outstanding_tags_: %d  RequestTimeout: %d\n", Sends,
1819       Resends, ResendFails, Fails, aoe__outstanding_tags_,
1820       RequestTimeout );
1821       Sends = 0;
1822       Resends = 0;
1823       ResendFails = 0;
1824       Fails = 0;
1825       KeQuerySystemTime ( &ReportTime );
1826     }
1827
1828         /*
1829          * TODO: Make the below value a #defined constant 
1830          */
1831         if ( CurrentTime.QuadPart >
1832        ( aoe__probe_tag_->SendTime.QuadPart + 100000000LL ) )
1833     {
1834       aoe__probe_tag_->Id = NextTagId++;
1835       if ( NextTagId == 0 )
1836         NextTagId++;
1837       aoe__probe_tag_->packet_data->Tag = aoe__probe_tag_->Id;
1838       Protocol_Send ( "\xff\xff\xff\xff\xff\xff",
1839           "\xff\xff\xff\xff\xff\xff",
1840           ( winvblock__uint8_ptr ) aoe__probe_tag_->
1841           packet_data, aoe__probe_tag_->PacketSize,
1842           NULL );
1843       KeQuerySystemTime ( &aoe__probe_tag_->SendTime );
1844     }
1845
1846         KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1847         if ( aoe__tag_list_ == NULL )
1848     {
1849       KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1850       continue;
1851     }
1852         tag = aoe__tag_list_;
1853         while ( tag != NULL )
1854     {
1855       /*
1856        * Establish pointers to the disk and AoE disk
1857        */
1858       disk_ptr = disk__get_ptr ( tag->device );
1859       aoe_disk_ptr = aoe__get_ ( tag->device );
1860
1861       RequestTimeout = aoe_disk_ptr->Timeout;
1862       if ( tag->Id == 0 )
1863         {
1864           if ( aoe__outstanding_tags_ <= 64 )
1865       {
1866         /*
1867          * if ( aoe__outstanding_tags_ <= 102400 ) { 
1868          */
1869         if ( aoe__outstanding_tags_ < 0 )
1870           DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1871         tag->Id = NextTagId++;
1872         if ( NextTagId == 0 )
1873           NextTagId++;
1874         tag->packet_data->Tag = tag->Id;
1875         if ( Protocol_Send
1876              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1877          ( winvblock__uint8_ptr ) tag->packet_data,
1878          tag->PacketSize, tag ) )
1879           {
1880             KeQuerySystemTime ( &tag->FirstSendTime );
1881             KeQuerySystemTime ( &tag->SendTime );
1882             aoe__outstanding_tags_++;
1883             Sends++;
1884           }
1885         else
1886           {
1887             Fails++;
1888             tag->Id = 0;
1889             break;
1890           }
1891       }
1892         }
1893       else
1894         {
1895           KeQuerySystemTime ( &CurrentTime );
1896           if ( CurrentTime.QuadPart >
1897          ( tag->SendTime.QuadPart +
1898            ( LONGLONG ) ( aoe_disk_ptr->Timeout * 2 ) ) )
1899       {
1900         if ( Protocol_Send
1901              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1902          ( winvblock__uint8_ptr ) tag->packet_data,
1903          tag->PacketSize, tag ) )
1904           {
1905             KeQuerySystemTime ( &tag->SendTime );
1906             aoe_disk_ptr->Timeout += aoe_disk_ptr->Timeout / 1000;
1907             if ( aoe_disk_ptr->Timeout > 100000000 )
1908         aoe_disk_ptr->Timeout = 100000000;
1909             Resends++;
1910           }
1911         else
1912           {
1913             ResendFails++;
1914             break;
1915           }
1916       }
1917         }
1918       tag = tag->next;
1919       if ( tag == aoe__tag_list_ )
1920         {
1921           DBG ( "Taglist Cyclic!!\n" );
1922           break;
1923         }
1924     }
1925         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1926       }
1927     DBG ( "Exit\n" );
1928   }
1929
1930 static disk__max_xfer_len_decl(max_xfer_len)
1931   {
1932     struct aoe__disk_type_ * aoe_disk_ptr = aoe__get_(disk_ptr->device);
1933
1934     return disk_ptr->SectorSize * aoe_disk_ptr->MaxSectorsPerPacket;
1935   }
1936
1937 static winvblock__uint32 STDCALL query_id(
1938     IN WV_SP_DEV_T dev,
1939     IN BUS_QUERY_ID_TYPE query_type,
1940     IN OUT WCHAR (*buf)[512]
1941   ) {
1942     struct aoe__disk_type_ * aoe_disk = aoe__get_(dev);
1943
1944     switch (query_type) {
1945         case BusQueryDeviceID:
1946           return swprintf(*buf, winvblock__literal_w L"\\AoEHardDisk") + 1;
1947
1948         case BusQueryInstanceID:
1949           return swprintf(
1950               *buf,
1951               L"AoE_at_Shelf_%d.Slot_%d",
1952               aoe_disk->Major,
1953               aoe_disk->Minor
1954             ) + 1;
1955
1956         case BusQueryHardwareIDs: {
1957             winvblock__uint32 tmp;
1958
1959             tmp = swprintf(
1960                 *buf,
1961                 winvblock__literal_w L"\\AoEHardDisk"
1962               ) + 1;
1963             tmp += swprintf(*buf + tmp, L"GenDisk") + 4;
1964             return tmp;
1965           }
1966
1967         case BusQueryCompatibleIDs:
1968           return swprintf(*buf, L"GenDisk") + 4;
1969
1970         default:
1971           return 0;
1972       }
1973   }
1974
1975 #ifdef _MSC_VER
1976 #  pragma pack(1)
1977 #endif
1978 winvblock__def_struct(abft) {
1979     winvblock__uint32 Signature;  /* 0x54464261 (aBFT) */
1980     winvblock__uint32 Length;
1981     winvblock__uint8 Revision;
1982     winvblock__uint8 Checksum;
1983     winvblock__uint8 OEMID[6];
1984     winvblock__uint8 OEMTableID[8];
1985     winvblock__uint8 Reserved1[12];
1986     winvblock__uint16 Major;
1987     winvblock__uint8 Minor;
1988     winvblock__uint8 Reserved2;
1989     winvblock__uint8 ClientMac[6];
1990   } __attribute__((__packed__));
1991 #ifdef _MSC_VER
1992 #  pragma pack()
1993 #endif
1994
1995 disk__close_decl(close) {
1996     return;
1997   }
1998
1999 static void aoe__process_abft_(void) {
2000     PHYSICAL_ADDRESS PhysicalAddress;
2001     winvblock__uint8_ptr PhysicalMemory;
2002     winvblock__uint32 Offset, Checksum, i;
2003     winvblock__bool FoundAbft = FALSE;
2004     abft AoEBootRecord;
2005     struct aoe__disk_type_ * aoe_disk;
2006
2007     /* Find aBFT. */
2008     PhysicalAddress.QuadPart = 0LL;
2009     PhysicalMemory = MmMapIoSpace(PhysicalAddress, 0xa0000, MmNonCached);
2010     if (!PhysicalMemory) {
2011         DBG("Could not map low memory\n");
2012         goto err_map_mem;
2013       }
2014     for (Offset = 0; Offset < 0xa0000; Offset += 0x10) {
2015         if (!(
2016             ((abft_ptr) (PhysicalMemory + Offset))->Signature ==
2017             0x54464261
2018           ))
2019           continue;
2020         Checksum = 0;
2021         for (
2022             i = 0;
2023             i < ((abft_ptr) (PhysicalMemory + Offset))->Length;
2024             i++
2025           )
2026           Checksum += PhysicalMemory[Offset + i];
2027         if (Checksum & 0xff)
2028           continue;
2029         if (((abft_ptr) (PhysicalMemory + Offset))->Revision != 1) {
2030             DBG(
2031                 "Found aBFT with mismatched revision v%d at "
2032                   "segment 0x%4x. want v1.\n",
2033                 ((abft_ptr) (PhysicalMemory + Offset))->Revision,
2034                 (Offset / 0x10)
2035               );
2036             continue;
2037           }
2038         DBG("Found aBFT at segment: 0x%04x\n", (Offset / 0x10));
2039         RtlCopyMemory(
2040             &AoEBootRecord,
2041             PhysicalMemory + Offset,
2042             sizeof (abft)
2043           );
2044         FoundAbft = TRUE;
2045         break;
2046       }
2047     MmUnmapIoSpace(PhysicalMemory, 0xa0000);
2048
2049     #ifdef RIS
2050     FoundAbft = TRUE;
2051     RtlCopyMemory(AoEBootRecord.ClientMac, "\x00\x0c\x29\x34\x69\x34", 6);
2052     AoEBootRecord.Major = 0;
2053     AoEBootRecord.Minor = 10;
2054     #endif
2055
2056     if (!FoundAbft)
2057       goto out_no_abft;
2058     aoe_disk = aoe__create_disk_();
2059     if(aoe_disk == NULL) {
2060         DBG("Could not create AoE disk from aBFT!\n");
2061         return;
2062       }
2063     DBG(
2064         "Attaching AoE disk from client NIC "
2065           "%02x:%02x:%02x:%02x:%02x:%02x to major: %d minor: %d\n",
2066         AoEBootRecord.ClientMac[0],
2067         AoEBootRecord.ClientMac[1],
2068         AoEBootRecord.ClientMac[2],
2069         AoEBootRecord.ClientMac[3],
2070         AoEBootRecord.ClientMac[4],
2071         AoEBootRecord.ClientMac[5],
2072         AoEBootRecord.Major,
2073         AoEBootRecord.Minor
2074       );
2075     RtlCopyMemory(aoe_disk->ClientMac, AoEBootRecord.ClientMac, 6);
2076     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
2077     aoe_disk->Major = AoEBootRecord.Major;
2078     aoe_disk->Minor = AoEBootRecord.Minor;
2079     aoe_disk->MaxSectorsPerPacket = 1;
2080     aoe_disk->Timeout = 200000;          /* 20 ms. */
2081     aoe_disk->disk->BootDrive = TRUE;
2082     aoe_disk->disk->Media = WvDiskMediaTypeHard;
2083     WvBusAddChild(driver__bus(), aoe_disk->disk->device);
2084     return;
2085
2086     out_no_abft:
2087     DBG("No aBFT found\n");
2088
2089     err_map_mem:
2090
2091     return;
2092   }
2093
2094 NTSTATUS STDCALL aoe__scan(
2095     IN WV_SP_DEV_T dev,
2096     IN PIRP irp
2097   ) {
2098     KIRQL irql;
2099     winvblock__uint32 count;
2100     struct aoe__target_list_ * target_walker;
2101     aoe__mount_targets_ptr targets;
2102     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2103
2104     DBG("Got IOCTL_AOE_SCAN...\n");
2105     KeAcquireSpinLock(&aoe__target_list_spinlock_, &irql);
2106
2107     count = 0;
2108     target_walker = aoe__target_list_;
2109     while (target_walker != NULL) {
2110         count++;
2111         target_walker = target_walker->next;
2112       }
2113
2114     targets = wv_malloc(sizeof *targets + (count * sizeof targets->Target[0]));
2115     if (targets == NULL) {
2116         DBG("wv_malloc targets\n");
2117         return driver__complete_irp(
2118             irp,
2119             0,
2120             STATUS_INSUFFICIENT_RESOURCES
2121           );
2122       }
2123     irp->IoStatus.Information =
2124       sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target));
2125     targets->Count = count;
2126
2127     count = 0;
2128     target_walker = aoe__target_list_;
2129     while (target_walker != NULL) {
2130         RtlCopyMemory(
2131             &targets->Target[count],
2132             &target_walker->Target,
2133             sizeof (aoe__mount_target)
2134           );
2135         count++;
2136         target_walker = target_walker->next;
2137       }
2138     RtlCopyMemory(
2139         irp->AssociatedIrp.SystemBuffer,
2140         targets,
2141         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
2142           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target))) ?
2143           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
2144           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target)))
2145         )
2146       );
2147     wv_free(targets);
2148
2149     KeReleaseSpinLock(&aoe__target_list_spinlock_, irql);
2150     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
2151   }
2152
2153 NTSTATUS STDCALL aoe__show(
2154     IN WV_SP_DEV_T dev,
2155     IN PIRP irp
2156   ) {
2157     winvblock__uint32 count;
2158     WV_SP_DEV_T dev_walker;
2159     WV_SP_BUS_T bus;
2160     aoe__mount_disks_ptr disks;
2161     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2162
2163     DBG("Got IOCTL_AOE_SHOW...\n");
2164
2165     bus = WvBusFromDev(dev);
2166     dev_walker = bus->first_child;
2167     count = 0;
2168     while (dev_walker != NULL) {
2169         count++;
2170         dev_walker = dev_walker->next_sibling_ptr;
2171       }
2172
2173     disks = wv_malloc(sizeof *disks + (count * sizeof disks->Disk[0]));
2174     if (disks == NULL) {
2175         DBG("wv_malloc disks\n");
2176         return driver__complete_irp(
2177             irp,
2178             0,
2179             STATUS_INSUFFICIENT_RESOURCES
2180           );
2181       }
2182     irp->IoStatus.Information =
2183       sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk));
2184     disks->Count = count;
2185
2186     count = 0;
2187     dev_walker = bus->first_child;
2188     while (dev_walker != NULL) {
2189         disk__type_ptr disk = disk__get_ptr(dev_walker);
2190         struct aoe__disk_type_ * aoe_disk = aoe__get_(dev_walker);
2191
2192         disks->Disk[count].Disk = dev_walker->DevNum;
2193         RtlCopyMemory(
2194             &disks->Disk[count].ClientMac,
2195             &aoe_disk->ClientMac,
2196             6
2197           );
2198         RtlCopyMemory(
2199             &disks->Disk[count].ServerMac,
2200             &aoe_disk->ServerMac,
2201             6
2202           );
2203         disks->Disk[count].Major = aoe_disk->Major;
2204         disks->Disk[count].Minor = aoe_disk->Minor;
2205         disks->Disk[count].LBASize = disk->LBADiskSize;
2206         count++;
2207         dev_walker = dev_walker->next_sibling_ptr;
2208       }
2209     RtlCopyMemory(
2210         irp->AssociatedIrp.SystemBuffer,
2211         disks,
2212         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
2213           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk))) ?
2214           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
2215           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk)))
2216         )
2217       );
2218     wv_free(disks);
2219     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
2220   }
2221
2222 NTSTATUS STDCALL aoe__mount(
2223     IN WV_SP_DEV_T dev,
2224     IN PIRP irp
2225   ) {
2226     winvblock__uint8_ptr buffer = irp->AssociatedIrp.SystemBuffer;
2227     struct aoe__disk_type_ * aoe_disk;
2228
2229     DBG(
2230         "Got IOCTL_AOE_MOUNT for client: %02x:%02x:%02x:%02x:%02x:%02x "
2231           "Major:%d Minor:%d\n",
2232         buffer[0],
2233         buffer[1],
2234         buffer[2],
2235         buffer[3],
2236         buffer[4],
2237         buffer[5],
2238         *(winvblock__uint16_ptr) (buffer + 6),
2239         (winvblock__uint8) buffer[8]
2240       );
2241     aoe_disk = aoe__create_disk_();
2242     if (aoe_disk == NULL) {
2243         DBG("Could not create AoE disk!\n");
2244         return driver__complete_irp(
2245             irp,
2246             0,
2247             STATUS_INSUFFICIENT_RESOURCES
2248           );
2249       }
2250     RtlCopyMemory(aoe_disk->ClientMac, buffer, 6);
2251     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
2252     aoe_disk->Major = *(winvblock__uint16_ptr) (buffer + 6);
2253     aoe_disk->Minor = (winvblock__uint8) buffer[8];
2254     aoe_disk->MaxSectorsPerPacket = 1;
2255     aoe_disk->Timeout = 200000;             /* 20 ms. */
2256     aoe_disk->disk->BootDrive = FALSE;
2257     aoe_disk->disk->Media = WvDiskMediaTypeHard;
2258     WvBusAddChild(driver__bus(), aoe_disk->disk->device);
2259
2260     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
2261   }
2262
2263 /**
2264  * Create a new AoE disk.
2265  *
2266  * @ret aoe_disk *      The address of a new AoE disk, or NULL for failure.
2267  *
2268  * This function should not be confused with a PDO creation routine, which is
2269  * actually implemented for each device type.  This routine will allocate a
2270  * aoe__disk_type_, track it in a global list, as well as populate the disk
2271  * with default values.
2272  */
2273 static struct aoe__disk_type_ * aoe__create_disk_(void) {
2274     disk__type_ptr disk;
2275     struct aoe__disk_type_ * aoe_disk;
2276
2277     /* Try to create a disk. */
2278     disk = disk__create();
2279     if (disk == NULL)
2280       goto err_nodisk;
2281     /*
2282      * AoE disk devices might be used for booting and should
2283      * not be allocated from a paged memory pool.
2284      */
2285     aoe_disk = wv_mallocz(sizeof *aoe_disk);
2286     if (aoe_disk == NULL)
2287       goto err_noaoedisk;
2288     /* Track the new AoE disk in our global list. */
2289     ExInterlockedInsertTailList(
2290         &aoe__disk_list_,
2291         &aoe_disk->tracking,
2292         &aoe__disk_list_lock_
2293       );
2294     /* Populate non-zero device defaults. */
2295     aoe_disk->disk = disk;
2296     aoe_disk->prev_free = disk->device->Ops.Free;
2297     disk->device->Ops.Free = aoe__free_disk_;
2298     disk->device->Ops.PnpId = query_id;
2299     disk->disk_ops.io = io;
2300     disk->disk_ops.max_xfer_len = max_xfer_len;
2301     disk->disk_ops.init = init;
2302     disk->disk_ops.close = close;
2303     disk->ext = aoe_disk;
2304
2305     return aoe_disk;
2306
2307     err_noaoedisk:
2308
2309     WvDevFree(disk->device);
2310     err_nodisk:
2311
2312     return NULL;
2313   }
2314
2315 /**
2316  * Default AoE disk deletion operation.
2317  *
2318  * @v dev               Points to the AoE disk device to delete.
2319  */
2320 static void STDCALL aoe__free_disk_(IN WV_SP_DEV_T dev) {
2321     struct aoe__disk_type_ * aoe_disk = aoe__get_(dev);
2322     /* Free the "inherited class". */
2323     aoe_disk->prev_free(dev);
2324     /*
2325      * Track the AoE disk deletion in our global list.  Unfortunately,
2326      * for now we have faith that an AoE disk won't be deleted twice and
2327      * result in a race condition.  Something to keep in mind...
2328      */
2329     ExInterlockedRemoveHeadList(
2330         aoe_disk->tracking.Blink,
2331         &aoe__disk_list_lock_
2332       );
2333
2334     wv_free(aoe_disk);
2335   }