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