[disk] Rename disk__io_mode to WV_E_DISK_IO_MODE
[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     WV_E_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 == WvDiskIoModeWrite)
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 == WvDiskIoModeRead)
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         /* For a write request, copy from the buffer into the AoE packet. */
1431         if (mode == WvDiskIoModeWrite)
1432     RtlCopyMemory ( tag->packet_data->Data, &buffer[tag->BufferOffset],
1433         tag->SectorCount * disk_ptr->SectorSize );
1434
1435         /*
1436          * Add this tag to the request's tag list 
1437          */
1438         tag->previous = previous_tag;
1439         tag->next = NULL;
1440         if ( new_tag_list == NULL )
1441     {
1442       new_tag_list = tag;
1443     }
1444         else
1445     {
1446       previous_tag->next = tag;
1447     }
1448         previous_tag = tag;
1449       }
1450     /*
1451      * Split the requested sectors into packets in tags
1452      */
1453     request_ptr->TotalTags = request_ptr->TagCount;
1454
1455     /*
1456      * Wait until we have the global spin-lock 
1457      */
1458     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1459
1460     /*
1461      * Enqueue our request's tag list to the global tag list 
1462      */
1463     if ( aoe__tag_list_last_ == NULL )
1464       {
1465         aoe__tag_list_ = new_tag_list;
1466       }
1467     else
1468       {
1469         aoe__tag_list_last_->next = new_tag_list;
1470         new_tag_list->previous = aoe__tag_list_last_;
1471       }
1472     /*
1473      * Adjust the global list to reflect our last tag 
1474      */
1475     aoe__tag_list_last_ = tag;
1476
1477     irp->IoStatus.Information = 0;
1478     irp->IoStatus.Status = STATUS_PENDING;
1479     IoMarkIrpPending ( irp );
1480
1481     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1482     KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1483     return STATUS_PENDING;
1484   }
1485
1486 static void STDCALL add_target(
1487     IN winvblock__uint8_ptr ClientMac,
1488     IN winvblock__uint8_ptr ServerMac,
1489     winvblock__uint16 Major,
1490     winvblock__uint8 Minor,
1491     LONGLONG LBASize
1492   )
1493   {
1494     struct aoe__target_list_ * Walker, * Last;
1495     KIRQL Irql;
1496
1497     KeAcquireSpinLock ( &aoe__target_list_spinlock_, &Irql );
1498     Walker = Last = aoe__target_list_;
1499     while ( Walker != NULL )
1500       {
1501         if (wv_memcmpeq(&Walker->Target.ClientMac, ClientMac, 6) &&
1502           wv_memcmpeq(&Walker->Target.ServerMac, ServerMac, 6) &&
1503           Walker->Target.Major == Major
1504           && Walker->Target.Minor == Minor) {
1505       if ( Walker->Target.LBASize != LBASize )
1506         {
1507           DBG ( "LBASize changed for e%d.%d " "(%I64u->%I64u)\n", Major,
1508           Minor, Walker->Target.LBASize, LBASize );
1509           Walker->Target.LBASize = LBASize;
1510         }
1511       KeQuerySystemTime ( &Walker->Target.ProbeTime );
1512       KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1513       return;
1514     }
1515         Last = Walker;
1516         Walker = Walker->next;
1517       }
1518
1519     if ((Walker = wv_malloc(sizeof *Walker)) == NULL) {
1520         DBG("wv_malloc Walker\n");
1521         KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1522         return;
1523       }
1524     Walker->next = NULL;
1525     RtlCopyMemory ( Walker->Target.ClientMac, ClientMac, 6 );
1526     RtlCopyMemory ( Walker->Target.ServerMac, ServerMac, 6 );
1527     Walker->Target.Major = Major;
1528     Walker->Target.Minor = Minor;
1529     Walker->Target.LBASize = LBASize;
1530     KeQuerySystemTime ( &Walker->Target.ProbeTime );
1531
1532     if ( Last == NULL )
1533       {
1534         aoe__target_list_ = Walker;
1535       }
1536     else
1537       {
1538         Last->next = Walker;
1539       }
1540     KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1541   }
1542
1543 /**
1544  * Process an AoE reply.
1545  *
1546  * @v SourceMac         The AoE server's MAC address.
1547  * @v DestinationMac    The AoE client's MAC address.
1548  * @v Data              The AoE packet.
1549  * @v DataSize          The AoE packet's size.
1550  */
1551 NTSTATUS STDCALL aoe__reply(
1552     IN winvblock__uint8_ptr SourceMac,
1553     IN winvblock__uint8_ptr DestinationMac,
1554     IN winvblock__uint8_ptr Data,
1555     IN winvblock__uint32 DataSize
1556   )
1557   {
1558     struct aoe__packet_ * reply = (struct aoe__packet_ *) Data;
1559     LONGLONG LBASize;
1560     struct aoe__work_tag_ * tag;
1561     KIRQL Irql;
1562     winvblock__bool Found = FALSE;
1563     LARGE_INTEGER CurrentTime;
1564     disk__type_ptr disk_ptr;
1565     struct aoe__disk_type_ * aoe_disk_ptr;
1566
1567     /*
1568      * Discard non-responses 
1569      */
1570     if ( !reply->ResponseFlag )
1571       return STATUS_SUCCESS;
1572
1573     /*
1574      * If the response matches our probe, add the AoE disk device 
1575      */
1576     if ( aoe__probe_tag_->Id == reply->Tag )
1577       {
1578         RtlCopyMemory ( &LBASize, &reply->Data[200], sizeof ( LONGLONG ) );
1579         add_target ( DestinationMac, SourceMac, ntohs ( reply->Major ),
1580          reply->Minor, LBASize );
1581         return STATUS_SUCCESS;
1582       }
1583
1584     /*
1585      * Wait until we have the global spin-lock 
1586      */
1587     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1588
1589     /*
1590      * Search for request tag 
1591      */
1592     if ( aoe__tag_list_ == NULL )
1593       {
1594         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1595         return STATUS_SUCCESS;
1596       }
1597     tag = aoe__tag_list_;
1598     while ( tag != NULL )
1599       {
1600         if ( ( tag->Id == reply->Tag )
1601        && ( tag->packet_data->Major == reply->Major )
1602        && ( tag->packet_data->Minor == reply->Minor ) )
1603     {
1604       Found = TRUE;
1605       break;
1606     }
1607         tag = tag->next;
1608       }
1609     if ( !Found )
1610       {
1611         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1612         return STATUS_SUCCESS;
1613       }
1614     else
1615       {
1616         /*
1617          * Remove the tag from the global tag list 
1618          */
1619         if ( tag->previous == NULL )
1620     aoe__tag_list_ = tag->next;
1621         else
1622     tag->previous->next = tag->next;
1623         if ( tag->next == NULL )
1624     aoe__tag_list_last_ = tag->previous;
1625         else
1626     tag->next->previous = tag->previous;
1627         aoe__outstanding_tags_--;
1628         if ( aoe__outstanding_tags_ < 0 )
1629     DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1630         KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1631       }
1632     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1633
1634     /*
1635      * Establish pointers to the disk device and AoE disk
1636      */
1637     disk_ptr = disk__get_ptr ( tag->device );
1638     aoe_disk_ptr = aoe__get_ ( tag->device );
1639
1640     /*
1641      * If our tag was a discovery request, note the server 
1642      */
1643     if (wv_memcmpeq(aoe_disk_ptr->ServerMac, "\xff\xff\xff\xff\xff\xff", 6)) {
1644         RtlCopyMemory ( aoe_disk_ptr->ServerMac, SourceMac, 6 );
1645         DBG ( "Major: %d minor: %d found on server "
1646         "%02x:%02x:%02x:%02x:%02x:%02x\n", aoe_disk_ptr->Major,
1647         aoe_disk_ptr->Minor, SourceMac[0], SourceMac[1], SourceMac[2],
1648         SourceMac[3], SourceMac[4], SourceMac[5] );
1649       }
1650
1651     KeQuerySystemTime ( &CurrentTime );
1652     aoe_disk_ptr->Timeout -=
1653       ( winvblock__uint32 ) ( ( aoe_disk_ptr->Timeout -
1654               ( CurrentTime.QuadPart -
1655           tag->FirstSendTime.QuadPart ) ) / 1024 );
1656     /*
1657      * TODO: Replace the values below with #defined constants 
1658      */
1659     if ( aoe_disk_ptr->Timeout > 100000000 )
1660       aoe_disk_ptr->Timeout = 100000000;
1661
1662     switch ( tag->type )
1663       {
1664         case aoe__tag_type_search_drive_:
1665     KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
1666     switch ( aoe_disk_ptr->search_state )
1667       {
1668         case aoe__search_state_getting_size_:
1669           /*
1670            * The reply tells us the disk size
1671            */
1672           RtlCopyMemory ( &disk_ptr->LBADiskSize, &reply->Data[200],
1673               sizeof ( LONGLONG ) );
1674           /*
1675            * Next we are concerned with the disk geometry
1676            */
1677           aoe_disk_ptr->search_state = aoe__search_state_get_geometry_;
1678           break;
1679         case aoe__search_state_getting_geometry_:
1680           /*
1681            * FIXME: use real values from partition table.
1682            * We used to truncate a fractional end cylinder, but
1683            * now leave it be in the hopes everyone uses LBA
1684            */
1685           disk_ptr->SectorSize = 512;
1686           disk_ptr->Heads = 255;
1687           disk_ptr->Sectors = 63;
1688           disk_ptr->Cylinders =
1689       disk_ptr->LBADiskSize / ( disk_ptr->Heads *
1690               disk_ptr->Sectors );
1691           /*
1692            * Next we are concerned with the maximum sectors per packet
1693            */
1694           aoe_disk_ptr->search_state =
1695             aoe__search_state_get_max_sectors_per_packet_;
1696           break;
1697         case aoe__search_state_getting_max_sectors_per_packet_:
1698           DataSize -= sizeof (struct aoe__packet_);
1699           if ( DataSize <
1700          ( aoe_disk_ptr->MaxSectorsPerPacket *
1701            disk_ptr->SectorSize ) )
1702       {
1703         DBG ( "Packet size too low while getting "
1704         "MaxSectorsPerPacket (tried %d, got size of %d)\n",
1705         aoe_disk_ptr->MaxSectorsPerPacket, DataSize );
1706         aoe_disk_ptr->MaxSectorsPerPacket--;
1707         aoe_disk_ptr->search_state = aoe__search_state_done_;
1708       }
1709           else if ( aoe_disk_ptr->MTU <
1710         ( sizeof (struct aoe__packet_) +
1711           ( ( aoe_disk_ptr->MaxSectorsPerPacket +
1712               1 ) * disk_ptr->SectorSize ) ) )
1713       {
1714         DBG ( "Got MaxSectorsPerPacket %d at size of %d. "
1715         "MTU of %d reached\n",
1716         aoe_disk_ptr->MaxSectorsPerPacket, DataSize,
1717         aoe_disk_ptr->MTU );
1718         aoe_disk_ptr->search_state = aoe__search_state_done_;
1719       }
1720           else
1721       {
1722         DBG ( "Got MaxSectorsPerPacket %d at size of %d, "
1723         "trying next...\n", aoe_disk_ptr->MaxSectorsPerPacket,
1724         DataSize );
1725         aoe_disk_ptr->search_state =
1726           aoe__search_state_get_max_sectors_per_packet_;
1727       }
1728           break;
1729         default:
1730           DBG ( "Undefined search_state!\n" );
1731           break;
1732       }
1733     KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1734     KeSetEvent ( &disk_ptr->SearchEvent, 0, FALSE );
1735     break;
1736         case aoe__tag_type_io_:
1737     /* If the reply is in response to a read request, get our data! */
1738     if (tag->request_ptr->Mode == WvDiskIoModeRead)
1739       RtlCopyMemory ( &tag->request_ptr->Buffer[tag->BufferOffset],
1740           reply->Data,
1741           tag->SectorCount * disk_ptr->SectorSize );
1742     /*
1743      * If this is the last reply expected for the read request,
1744      * complete the IRP and free the request
1745      */
1746     if ( InterlockedDecrement ( &tag->request_ptr->TagCount ) == 0 )
1747       {
1748         tag->request_ptr->Irp->IoStatus.Information =
1749           tag->request_ptr->SectorCount * disk_ptr->SectorSize;
1750         tag->request_ptr->Irp->IoStatus.Status = STATUS_SUCCESS;
1751         Driver_CompletePendingIrp ( tag->request_ptr->Irp );
1752         wv_free(tag->request_ptr);
1753       }
1754     break;
1755         default:
1756     DBG ( "Unknown tag type!!\n" );
1757     break;
1758       }
1759
1760     KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1761     wv_free(tag->packet_data);
1762     wv_free(tag);
1763     return STATUS_SUCCESS;
1764   }
1765
1766 void aoe__reset_probe(void)
1767   {
1768     aoe__probe_tag_->SendTime.QuadPart = 0LL;
1769   }
1770
1771 static void STDCALL aoe__thread_(IN void *StartContext)
1772   {
1773     LARGE_INTEGER Timeout, CurrentTime, ProbeTime, ReportTime;
1774     winvblock__uint32 NextTagId = 1;
1775     struct aoe__work_tag_ * tag;
1776     KIRQL Irql;
1777     winvblock__uint32 Sends = 0;
1778     winvblock__uint32 Resends = 0;
1779     winvblock__uint32 ResendFails = 0;
1780     winvblock__uint32 Fails = 0;
1781     winvblock__uint32 RequestTimeout = 0;
1782     disk__type_ptr disk_ptr;
1783     struct aoe__disk_type_ * aoe_disk_ptr;
1784
1785     DBG ( "Entry\n" );
1786     ReportTime.QuadPart = 0LL;
1787     ProbeTime.QuadPart = 0LL;
1788
1789     while ( TRUE )
1790       {
1791         /*
1792          * TODO: Make the below value a #defined constant 
1793          */
1794         /*
1795          * 100.000 * 100ns = 10.000.000 ns = 10ms
1796          */
1797         Timeout.QuadPart = -100000LL;
1798         KeWaitForSingleObject ( &aoe__thread_sig_evt_, Executive,
1799               KernelMode, FALSE, &Timeout );
1800         KeResetEvent ( &aoe__thread_sig_evt_ );
1801         if ( aoe__stop_ )
1802     {
1803       DBG ( "Stopping...\n" );
1804       PsTerminateSystemThread ( STATUS_SUCCESS );
1805     }
1806
1807         KeQuerySystemTime ( &CurrentTime );
1808         /*
1809          * TODO: Make the below value a #defined constant 
1810          */
1811         if ( CurrentTime.QuadPart > ( ReportTime.QuadPart + 10000000LL ) )
1812     {
1813       DBG ( "Sends: %d  Resends: %d  ResendFails: %d  Fails: %d  "
1814       "aoe__outstanding_tags_: %d  RequestTimeout: %d\n", Sends,
1815       Resends, ResendFails, Fails, aoe__outstanding_tags_,
1816       RequestTimeout );
1817       Sends = 0;
1818       Resends = 0;
1819       ResendFails = 0;
1820       Fails = 0;
1821       KeQuerySystemTime ( &ReportTime );
1822     }
1823
1824         /*
1825          * TODO: Make the below value a #defined constant 
1826          */
1827         if ( CurrentTime.QuadPart >
1828        ( aoe__probe_tag_->SendTime.QuadPart + 100000000LL ) )
1829     {
1830       aoe__probe_tag_->Id = NextTagId++;
1831       if ( NextTagId == 0 )
1832         NextTagId++;
1833       aoe__probe_tag_->packet_data->Tag = aoe__probe_tag_->Id;
1834       Protocol_Send ( "\xff\xff\xff\xff\xff\xff",
1835           "\xff\xff\xff\xff\xff\xff",
1836           ( winvblock__uint8_ptr ) aoe__probe_tag_->
1837           packet_data, aoe__probe_tag_->PacketSize,
1838           NULL );
1839       KeQuerySystemTime ( &aoe__probe_tag_->SendTime );
1840     }
1841
1842         KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1843         if ( aoe__tag_list_ == NULL )
1844     {
1845       KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1846       continue;
1847     }
1848         tag = aoe__tag_list_;
1849         while ( tag != NULL )
1850     {
1851       /*
1852        * Establish pointers to the disk and AoE disk
1853        */
1854       disk_ptr = disk__get_ptr ( tag->device );
1855       aoe_disk_ptr = aoe__get_ ( tag->device );
1856
1857       RequestTimeout = aoe_disk_ptr->Timeout;
1858       if ( tag->Id == 0 )
1859         {
1860           if ( aoe__outstanding_tags_ <= 64 )
1861       {
1862         /*
1863          * if ( aoe__outstanding_tags_ <= 102400 ) { 
1864          */
1865         if ( aoe__outstanding_tags_ < 0 )
1866           DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1867         tag->Id = NextTagId++;
1868         if ( NextTagId == 0 )
1869           NextTagId++;
1870         tag->packet_data->Tag = tag->Id;
1871         if ( Protocol_Send
1872              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1873          ( winvblock__uint8_ptr ) tag->packet_data,
1874          tag->PacketSize, tag ) )
1875           {
1876             KeQuerySystemTime ( &tag->FirstSendTime );
1877             KeQuerySystemTime ( &tag->SendTime );
1878             aoe__outstanding_tags_++;
1879             Sends++;
1880           }
1881         else
1882           {
1883             Fails++;
1884             tag->Id = 0;
1885             break;
1886           }
1887       }
1888         }
1889       else
1890         {
1891           KeQuerySystemTime ( &CurrentTime );
1892           if ( CurrentTime.QuadPart >
1893          ( tag->SendTime.QuadPart +
1894            ( LONGLONG ) ( aoe_disk_ptr->Timeout * 2 ) ) )
1895       {
1896         if ( Protocol_Send
1897              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1898          ( winvblock__uint8_ptr ) tag->packet_data,
1899          tag->PacketSize, tag ) )
1900           {
1901             KeQuerySystemTime ( &tag->SendTime );
1902             aoe_disk_ptr->Timeout += aoe_disk_ptr->Timeout / 1000;
1903             if ( aoe_disk_ptr->Timeout > 100000000 )
1904         aoe_disk_ptr->Timeout = 100000000;
1905             Resends++;
1906           }
1907         else
1908           {
1909             ResendFails++;
1910             break;
1911           }
1912       }
1913         }
1914       tag = tag->next;
1915       if ( tag == aoe__tag_list_ )
1916         {
1917           DBG ( "Taglist Cyclic!!\n" );
1918           break;
1919         }
1920     }
1921         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1922       }
1923     DBG ( "Exit\n" );
1924   }
1925
1926 static disk__max_xfer_len_decl(max_xfer_len)
1927   {
1928     struct aoe__disk_type_ * aoe_disk_ptr = aoe__get_(disk_ptr->device);
1929
1930     return disk_ptr->SectorSize * aoe_disk_ptr->MaxSectorsPerPacket;
1931   }
1932
1933 static winvblock__uint32 STDCALL query_id(
1934     IN WV_SP_DEV_T dev,
1935     IN BUS_QUERY_ID_TYPE query_type,
1936     IN OUT WCHAR (*buf)[512]
1937   ) {
1938     struct aoe__disk_type_ * aoe_disk = aoe__get_(dev);
1939
1940     switch (query_type) {
1941         case BusQueryDeviceID:
1942           return swprintf(*buf, winvblock__literal_w L"\\AoEHardDisk") + 1;
1943
1944         case BusQueryInstanceID:
1945           return swprintf(
1946               *buf,
1947               L"AoE_at_Shelf_%d.Slot_%d",
1948               aoe_disk->Major,
1949               aoe_disk->Minor
1950             ) + 1;
1951
1952         case BusQueryHardwareIDs: {
1953             winvblock__uint32 tmp;
1954
1955             tmp = swprintf(
1956                 *buf,
1957                 winvblock__literal_w L"\\AoEHardDisk"
1958               ) + 1;
1959             tmp += swprintf(*buf + tmp, L"GenDisk") + 4;
1960             return tmp;
1961           }
1962
1963         case BusQueryCompatibleIDs:
1964           return swprintf(*buf, L"GenDisk") + 4;
1965
1966         default:
1967           return 0;
1968       }
1969   }
1970
1971 #ifdef _MSC_VER
1972 #  pragma pack(1)
1973 #endif
1974 winvblock__def_struct(abft) {
1975     winvblock__uint32 Signature;  /* 0x54464261 (aBFT) */
1976     winvblock__uint32 Length;
1977     winvblock__uint8 Revision;
1978     winvblock__uint8 Checksum;
1979     winvblock__uint8 OEMID[6];
1980     winvblock__uint8 OEMTableID[8];
1981     winvblock__uint8 Reserved1[12];
1982     winvblock__uint16 Major;
1983     winvblock__uint8 Minor;
1984     winvblock__uint8 Reserved2;
1985     winvblock__uint8 ClientMac[6];
1986   } __attribute__((__packed__));
1987 #ifdef _MSC_VER
1988 #  pragma pack()
1989 #endif
1990
1991 disk__close_decl(close) {
1992     return;
1993   }
1994
1995 static void aoe__process_abft_(void) {
1996     PHYSICAL_ADDRESS PhysicalAddress;
1997     winvblock__uint8_ptr PhysicalMemory;
1998     winvblock__uint32 Offset, Checksum, i;
1999     winvblock__bool FoundAbft = FALSE;
2000     abft AoEBootRecord;
2001     struct aoe__disk_type_ * aoe_disk;
2002
2003     /* Find aBFT. */
2004     PhysicalAddress.QuadPart = 0LL;
2005     PhysicalMemory = MmMapIoSpace(PhysicalAddress, 0xa0000, MmNonCached);
2006     if (!PhysicalMemory) {
2007         DBG("Could not map low memory\n");
2008         goto err_map_mem;
2009       }
2010     for (Offset = 0; Offset < 0xa0000; Offset += 0x10) {
2011         if (!(
2012             ((abft_ptr) (PhysicalMemory + Offset))->Signature ==
2013             0x54464261
2014           ))
2015           continue;
2016         Checksum = 0;
2017         for (
2018             i = 0;
2019             i < ((abft_ptr) (PhysicalMemory + Offset))->Length;
2020             i++
2021           )
2022           Checksum += PhysicalMemory[Offset + i];
2023         if (Checksum & 0xff)
2024           continue;
2025         if (((abft_ptr) (PhysicalMemory + Offset))->Revision != 1) {
2026             DBG(
2027                 "Found aBFT with mismatched revision v%d at "
2028                   "segment 0x%4x. want v1.\n",
2029                 ((abft_ptr) (PhysicalMemory + Offset))->Revision,
2030                 (Offset / 0x10)
2031               );
2032             continue;
2033           }
2034         DBG("Found aBFT at segment: 0x%04x\n", (Offset / 0x10));
2035         RtlCopyMemory(
2036             &AoEBootRecord,
2037             PhysicalMemory + Offset,
2038             sizeof (abft)
2039           );
2040         FoundAbft = TRUE;
2041         break;
2042       }
2043     MmUnmapIoSpace(PhysicalMemory, 0xa0000);
2044
2045     #ifdef RIS
2046     FoundAbft = TRUE;
2047     RtlCopyMemory(AoEBootRecord.ClientMac, "\x00\x0c\x29\x34\x69\x34", 6);
2048     AoEBootRecord.Major = 0;
2049     AoEBootRecord.Minor = 10;
2050     #endif
2051
2052     if (!FoundAbft)
2053       goto out_no_abft;
2054     aoe_disk = aoe__create_disk_();
2055     if(aoe_disk == NULL) {
2056         DBG("Could not create AoE disk from aBFT!\n");
2057         return;
2058       }
2059     DBG(
2060         "Attaching AoE disk from client NIC "
2061           "%02x:%02x:%02x:%02x:%02x:%02x to major: %d minor: %d\n",
2062         AoEBootRecord.ClientMac[0],
2063         AoEBootRecord.ClientMac[1],
2064         AoEBootRecord.ClientMac[2],
2065         AoEBootRecord.ClientMac[3],
2066         AoEBootRecord.ClientMac[4],
2067         AoEBootRecord.ClientMac[5],
2068         AoEBootRecord.Major,
2069         AoEBootRecord.Minor
2070       );
2071     RtlCopyMemory(aoe_disk->ClientMac, AoEBootRecord.ClientMac, 6);
2072     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
2073     aoe_disk->Major = AoEBootRecord.Major;
2074     aoe_disk->Minor = AoEBootRecord.Minor;
2075     aoe_disk->MaxSectorsPerPacket = 1;
2076     aoe_disk->Timeout = 200000;          /* 20 ms. */
2077     aoe_disk->disk->BootDrive = TRUE;
2078     aoe_disk->disk->Media = WvDiskMediaTypeHard;
2079     WvBusAddChild(driver__bus(), aoe_disk->disk->device);
2080     return;
2081
2082     out_no_abft:
2083     DBG("No aBFT found\n");
2084
2085     err_map_mem:
2086
2087     return;
2088   }
2089
2090 NTSTATUS STDCALL aoe__scan(
2091     IN WV_SP_DEV_T dev,
2092     IN PIRP irp
2093   ) {
2094     KIRQL irql;
2095     winvblock__uint32 count;
2096     struct aoe__target_list_ * target_walker;
2097     aoe__mount_targets_ptr targets;
2098     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2099
2100     DBG("Got IOCTL_AOE_SCAN...\n");
2101     KeAcquireSpinLock(&aoe__target_list_spinlock_, &irql);
2102
2103     count = 0;
2104     target_walker = aoe__target_list_;
2105     while (target_walker != NULL) {
2106         count++;
2107         target_walker = target_walker->next;
2108       }
2109
2110     targets = wv_malloc(sizeof *targets + (count * sizeof targets->Target[0]));
2111     if (targets == NULL) {
2112         DBG("wv_malloc targets\n");
2113         return driver__complete_irp(
2114             irp,
2115             0,
2116             STATUS_INSUFFICIENT_RESOURCES
2117           );
2118       }
2119     irp->IoStatus.Information =
2120       sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target));
2121     targets->Count = count;
2122
2123     count = 0;
2124     target_walker = aoe__target_list_;
2125     while (target_walker != NULL) {
2126         RtlCopyMemory(
2127             &targets->Target[count],
2128             &target_walker->Target,
2129             sizeof (aoe__mount_target)
2130           );
2131         count++;
2132         target_walker = target_walker->next;
2133       }
2134     RtlCopyMemory(
2135         irp->AssociatedIrp.SystemBuffer,
2136         targets,
2137         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
2138           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target))) ?
2139           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
2140           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target)))
2141         )
2142       );
2143     wv_free(targets);
2144
2145     KeReleaseSpinLock(&aoe__target_list_spinlock_, irql);
2146     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
2147   }
2148
2149 NTSTATUS STDCALL aoe__show(
2150     IN WV_SP_DEV_T dev,
2151     IN PIRP irp
2152   ) {
2153     winvblock__uint32 count;
2154     WV_SP_DEV_T dev_walker;
2155     WV_SP_BUS_T bus;
2156     aoe__mount_disks_ptr disks;
2157     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2158
2159     DBG("Got IOCTL_AOE_SHOW...\n");
2160
2161     bus = WvBusFromDev(dev);
2162     dev_walker = bus->first_child;
2163     count = 0;
2164     while (dev_walker != NULL) {
2165         count++;
2166         dev_walker = dev_walker->next_sibling_ptr;
2167       }
2168
2169     disks = wv_malloc(sizeof *disks + (count * sizeof disks->Disk[0]));
2170     if (disks == NULL) {
2171         DBG("wv_malloc disks\n");
2172         return driver__complete_irp(
2173             irp,
2174             0,
2175             STATUS_INSUFFICIENT_RESOURCES
2176           );
2177       }
2178     irp->IoStatus.Information =
2179       sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk));
2180     disks->Count = count;
2181
2182     count = 0;
2183     dev_walker = bus->first_child;
2184     while (dev_walker != NULL) {
2185         disk__type_ptr disk = disk__get_ptr(dev_walker);
2186         struct aoe__disk_type_ * aoe_disk = aoe__get_(dev_walker);
2187
2188         disks->Disk[count].Disk = dev_walker->DevNum;
2189         RtlCopyMemory(
2190             &disks->Disk[count].ClientMac,
2191             &aoe_disk->ClientMac,
2192             6
2193           );
2194         RtlCopyMemory(
2195             &disks->Disk[count].ServerMac,
2196             &aoe_disk->ServerMac,
2197             6
2198           );
2199         disks->Disk[count].Major = aoe_disk->Major;
2200         disks->Disk[count].Minor = aoe_disk->Minor;
2201         disks->Disk[count].LBASize = disk->LBADiskSize;
2202         count++;
2203         dev_walker = dev_walker->next_sibling_ptr;
2204       }
2205     RtlCopyMemory(
2206         irp->AssociatedIrp.SystemBuffer,
2207         disks,
2208         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
2209           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk))) ?
2210           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
2211           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk)))
2212         )
2213       );
2214     wv_free(disks);
2215     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
2216   }
2217
2218 NTSTATUS STDCALL aoe__mount(
2219     IN WV_SP_DEV_T dev,
2220     IN PIRP irp
2221   ) {
2222     winvblock__uint8_ptr buffer = irp->AssociatedIrp.SystemBuffer;
2223     struct aoe__disk_type_ * aoe_disk;
2224
2225     DBG(
2226         "Got IOCTL_AOE_MOUNT for client: %02x:%02x:%02x:%02x:%02x:%02x "
2227           "Major:%d Minor:%d\n",
2228         buffer[0],
2229         buffer[1],
2230         buffer[2],
2231         buffer[3],
2232         buffer[4],
2233         buffer[5],
2234         *(winvblock__uint16_ptr) (buffer + 6),
2235         (winvblock__uint8) buffer[8]
2236       );
2237     aoe_disk = aoe__create_disk_();
2238     if (aoe_disk == NULL) {
2239         DBG("Could not create AoE disk!\n");
2240         return driver__complete_irp(
2241             irp,
2242             0,
2243             STATUS_INSUFFICIENT_RESOURCES
2244           );
2245       }
2246     RtlCopyMemory(aoe_disk->ClientMac, buffer, 6);
2247     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
2248     aoe_disk->Major = *(winvblock__uint16_ptr) (buffer + 6);
2249     aoe_disk->Minor = (winvblock__uint8) buffer[8];
2250     aoe_disk->MaxSectorsPerPacket = 1;
2251     aoe_disk->Timeout = 200000;             /* 20 ms. */
2252     aoe_disk->disk->BootDrive = FALSE;
2253     aoe_disk->disk->Media = WvDiskMediaTypeHard;
2254     WvBusAddChild(driver__bus(), aoe_disk->disk->device);
2255
2256     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
2257   }
2258
2259 /**
2260  * Create a new AoE disk.
2261  *
2262  * @ret aoe_disk *      The address of a new AoE disk, or NULL for failure.
2263  *
2264  * This function should not be confused with a PDO creation routine, which is
2265  * actually implemented for each device type.  This routine will allocate a
2266  * aoe__disk_type_, track it in a global list, as well as populate the disk
2267  * with default values.
2268  */
2269 static struct aoe__disk_type_ * aoe__create_disk_(void) {
2270     disk__type_ptr disk;
2271     struct aoe__disk_type_ * aoe_disk;
2272
2273     /* Try to create a disk. */
2274     disk = disk__create();
2275     if (disk == NULL)
2276       goto err_nodisk;
2277     /*
2278      * AoE disk devices might be used for booting and should
2279      * not be allocated from a paged memory pool.
2280      */
2281     aoe_disk = wv_mallocz(sizeof *aoe_disk);
2282     if (aoe_disk == NULL)
2283       goto err_noaoedisk;
2284     /* Track the new AoE disk in our global list. */
2285     ExInterlockedInsertTailList(
2286         &aoe__disk_list_,
2287         &aoe_disk->tracking,
2288         &aoe__disk_list_lock_
2289       );
2290     /* Populate non-zero device defaults. */
2291     aoe_disk->disk = disk;
2292     aoe_disk->prev_free = disk->device->Ops.Free;
2293     disk->device->Ops.Free = aoe__free_disk_;
2294     disk->device->Ops.PnpId = query_id;
2295     disk->disk_ops.io = io;
2296     disk->disk_ops.max_xfer_len = max_xfer_len;
2297     disk->disk_ops.init = init;
2298     disk->disk_ops.close = close;
2299     disk->ext = aoe_disk;
2300
2301     return aoe_disk;
2302
2303     err_noaoedisk:
2304
2305     WvDevFree(disk->device);
2306     err_nodisk:
2307
2308     return NULL;
2309   }
2310
2311 /**
2312  * Default AoE disk deletion operation.
2313  *
2314  * @v dev               Points to the AoE disk device to delete.
2315  */
2316 static void STDCALL aoe__free_disk_(IN WV_SP_DEV_T dev) {
2317     struct aoe__disk_type_ * aoe_disk = aoe__get_(dev);
2318     /* Free the "inherited class". */
2319     aoe_disk->prev_free(dev);
2320     /*
2321      * Track the AoE disk deletion in our global list.  Unfortunately,
2322      * for now we have faith that an AoE disk won't be deleted twice and
2323      * result in a race condition.  Something to keep in mind...
2324      */
2325     ExInterlockedRemoveHeadList(
2326         aoe_disk->tracking.Blink,
2327         &aoe__disk_list_lock_
2328       );
2329
2330     wv_free(aoe_disk);
2331   }