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