ce018866fbe659e6de50eacc61c2402bd475b884
[people/sha0/winvblock.git] / src / aoe / driver.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * AoE specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30
31 #include "winvblock.h"
32 #include "wv_stdlib.h"
33 #include "wv_string.h"
34 #include "portable.h"
35 #include "driver.h"
36 #include "device.h"
37 #include "disk.h"
38 #include "mount.h"
39 #include "bus.h"
40 #include "aoe.h"
41 #include "registry.h"
42 #include "protocol.h"
43 #include "debug.h"
44
45 #define AOEPROTOCOLVER 1
46
47 extern NTSTATUS STDCALL ZwWaitForSingleObject(
48     IN HANDLE Handle,
49     IN winvblock__bool Alertable,
50     IN PLARGE_INTEGER Timeout OPTIONAL
51   );
52
53 /* From aoe/bus.c */
54 extern WV_SP_BUS_T aoe_bus;
55 extern winvblock__bool aoe_bus__create(void);
56 extern void aoe_bus__free(void);
57
58 /* Forward declarations. */
59 struct aoe__disk_type_;
60 static void STDCALL aoe__thread_(IN void *);
61 static void aoe__process_abft_(void);
62 static void STDCALL aoe__unload_(IN PDRIVER_OBJECT);
63 static struct aoe__disk_type_ * aoe__create_disk_(void);
64 static WV_F_DEV_FREE aoe__free_disk_;
65 static WV_F_DISK_IO io;
66 static WV_F_DISK_MAX_XFER_LEN max_xfer_len;
67 static WV_F_DISK_INIT init;
68 static WV_F_DISK_CLOSE close;
69
70 /** Tag types. */
71 enum aoe__tag_type_
72   {
73     aoe__tag_type_io_,
74     aoe__tag_type_search_drive_
75   };
76
77 #ifdef _MSC_VER
78 #  pragma pack(1)
79 #endif
80
81 /** AoE packet. */
82 struct aoe__packet_
83   {
84     winvblock__uint8 ReservedFlag:2;
85     winvblock__uint8 ErrorFlag:1;
86     winvblock__uint8 ResponseFlag:1;
87     winvblock__uint8 Ver:4;
88     winvblock__uint8 Error;
89     winvblock__uint16 Major;
90     winvblock__uint8 Minor;
91     winvblock__uint8 Command;
92     winvblock__uint32 Tag;
93
94     winvblock__uint8 WriteAFlag:1;
95     winvblock__uint8 AsyncAFlag:1;
96     winvblock__uint8 Reserved1AFlag:2;
97     winvblock__uint8 DeviceHeadAFlag:1;
98     winvblock__uint8 Reserved2AFlag:1;
99     winvblock__uint8 ExtendedAFlag:1;
100     winvblock__uint8 Reserved3AFlag:1;
101     union
102       {
103         winvblock__uint8 Err;
104         winvblock__uint8 Feature;
105       };
106     winvblock__uint8 Count;
107     union
108       {
109         winvblock__uint8 Cmd;
110         winvblock__uint8 Status;
111       };
112
113     winvblock__uint8 Lba0;
114     winvblock__uint8 Lba1;
115     winvblock__uint8 Lba2;
116     winvblock__uint8 Lba3;
117     winvblock__uint8 Lba4;
118     winvblock__uint8 Lba5;
119     winvblock__uint16 Reserved;
120
121     winvblock__uint8 Data[];
122   } __attribute__((__packed__));
123
124 #ifdef _MSC_VER
125 #  pragma pack()
126 #endif
127
128 /** An I/O request. */
129 struct aoe__io_req_
130   {
131     WV_E_DISK_IO_MODE Mode;
132     winvblock__uint32 SectorCount;
133     winvblock__uint8_ptr Buffer;
134     PIRP Irp;
135     winvblock__uint32 TagCount;
136     winvblock__uint32 TotalTags;
137   };
138
139 /** A work item "tag". */
140 struct aoe__work_tag_
141   {
142     enum aoe__tag_type_ type;
143     WV_SP_DEV_T device;
144     struct aoe__io_req_ * request_ptr;
145     winvblock__uint32 Id;
146     struct aoe__packet_ * packet_data;
147     winvblock__uint32 PacketSize;
148     LARGE_INTEGER FirstSendTime;
149     LARGE_INTEGER SendTime;
150     winvblock__uint32 BufferOffset;
151     winvblock__uint32 SectorCount;
152     struct aoe__work_tag_ * next;
153     struct aoe__work_tag_ * previous;
154   };
155
156 /** A disk search. */
157 struct aoe__disk_search_
158   {
159     WV_SP_DEV_T device;
160     struct aoe__work_tag_ * tag;
161     struct aoe__disk_search_ * next;
162   };
163
164 enum aoe__search_state_
165   {
166     aoe__search_state_search_nic_,
167     aoe__search_state_get_size_,
168     aoe__search_state_getting_size_,
169     aoe__search_state_get_geometry_,
170     aoe__search_state_getting_geometry_,
171     aoe__search_state_get_max_sectors_per_packet_,
172     aoe__search_state_getting_max_sectors_per_packet_,
173     aoe__search_state_done_
174   };
175
176 /** The AoE disk type. */
177 struct aoe__disk_type_
178   {
179     WV_SP_DISK_T disk;
180     winvblock__uint32 MTU;
181     winvblock__uint8 ClientMac[6];
182     winvblock__uint8 ServerMac[6];
183     winvblock__uint32 Major;
184     winvblock__uint32 Minor;
185     winvblock__uint32 MaxSectorsPerPacket;
186     winvblock__uint32 Timeout;
187     enum aoe__search_state_ search_state;
188     WV_FP_DEV_FREE prev_free;
189     LIST_ENTRY tracking;
190   };
191
192 struct aoe__target_list_
193   {
194     aoe__mount_target Target;
195     struct aoe__target_list_ * next;
196   };
197
198 /** Private globals. */
199 static struct aoe__target_list_ * aoe__target_list_ = NULL;
200 static KSPIN_LOCK aoe__target_list_spinlock_;
201 static winvblock__bool aoe__stop_ = FALSE;
202 static KSPIN_LOCK aoe__spinlock_;
203 static KEVENT aoe__thread_sig_evt_;
204 static struct aoe__work_tag_ * aoe__tag_list_ = NULL;
205 static struct aoe__work_tag_ * aoe__tag_list_last_ = NULL;
206 static struct aoe__work_tag_ * aoe__probe_tag_ = NULL;
207 static struct aoe__disk_search_ * aoe__disk_search_list_ = NULL;
208 static LONG aoe__outstanding_tags_ = 0;
209 static HANDLE aoe__thread_handle_;
210 static winvblock__bool aoe__started_ = FALSE;
211 static LIST_ENTRY aoe__disk_list_;
212 static KSPIN_LOCK aoe__disk_list_lock_;
213
214 /* Yield a pointer to the AoE disk. */
215 static struct aoe__disk_type_ * aoe__get_(WV_SP_DEV_T 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     WV_SP_BUS_T 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     aoe_bus__free();
912     aoe__started_ = FALSE;
913     DBG("Exit\n");
914   }
915
916 /**
917  * Search for disk parameters.
918  *
919  * @v disk_ptr          The disk to initialize (for AoE, match).
920  * @ret BOOLEAN         See below.
921  *
922  * Returns TRUE if the disk could be matched, FALSE otherwise.
923  */
924 static winvblock__bool STDCALL init(IN WV_SP_DISK_T disk_ptr) {
925     struct aoe__disk_search_
926       * disk_searcher, * disk_search_walker, * previous_disk_searcher;
927     LARGE_INTEGER Timeout, CurrentTime;
928     struct aoe__work_tag_ * tag, * tag_walker;
929     KIRQL Irql, InnerIrql;
930     LARGE_INTEGER MaxSectorsPerPacketSendTime;
931     winvblock__uint32 MTU;
932     struct aoe__disk_type_ * aoe_disk_ptr;
933
934     aoe_disk_ptr = aoe__get_(disk_ptr->Dev);
935     /*
936      * Allocate our disk search 
937      */
938     if ((disk_searcher = wv_malloc(sizeof *disk_searcher)) == NULL) {
939         DBG ( "Couldn't allocate for disk_searcher; bye!\n" );
940         return FALSE;
941       }
942
943     /*
944      * Initialize the disk search 
945      */
946     disk_searcher->device = disk_ptr->Dev;
947     disk_searcher->next = NULL;
948     aoe_disk_ptr->search_state = aoe__search_state_search_nic_;
949     KeResetEvent ( &disk_ptr->SearchEvent );
950
951     /*
952      * Wait until we have the global spin-lock 
953      */
954     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
955
956     /*
957      * Add our disk search to the global list of disk searches 
958      */
959     if ( aoe__disk_search_list_ == NULL )
960       {
961         aoe__disk_search_list_ = disk_searcher;
962       }
963     else
964       {
965         disk_search_walker = aoe__disk_search_list_;
966         while ( disk_search_walker->next )
967     disk_search_walker = disk_search_walker->next;
968         disk_search_walker->next = disk_searcher;
969       }
970
971     /*
972      * Release the global spin-lock 
973      */
974     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
975
976     /*
977      * We go through all the states until the disk is ready for use 
978      */
979     while ( TRUE )
980       {
981         /*
982          * Wait for our device's extension's search to be signalled 
983          */
984         /*
985          * TODO: Make the below value a #defined constant 
986          */
987         /*
988          * 500.000 * 100ns = 50.000.000 ns = 50ms 
989          */
990         Timeout.QuadPart = -500000LL;
991         KeWaitForSingleObject ( &disk_ptr->SearchEvent, Executive, KernelMode,
992               FALSE, &Timeout );
993         if ( aoe__stop_ )
994     {
995       DBG ( "AoE is shutting down; bye!\n" );
996       return FALSE;
997     }
998
999         /*
1000          * Wait until we have the device extension's spin-lock 
1001          */
1002         KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
1003
1004         if (aoe_disk_ptr->search_state == aoe__search_state_search_nic_)
1005     {
1006       if ( !Protocol_SearchNIC ( aoe_disk_ptr->ClientMac ) )
1007         {
1008           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1009           continue;
1010         }
1011       else
1012         {
1013           /*
1014            * We found the adapter to use, get MTU next 
1015            */
1016           aoe_disk_ptr->MTU = Protocol_GetMTU ( aoe_disk_ptr->ClientMac );
1017           aoe_disk_ptr->search_state = aoe__search_state_get_size_;
1018         }
1019     }
1020
1021         if (aoe_disk_ptr->search_state == aoe__search_state_getting_size_)
1022     {
1023       /*
1024        * Still getting the disk's size 
1025        */
1026       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1027       continue;
1028     }
1029         if (aoe_disk_ptr->search_state == aoe__search_state_getting_geometry_)
1030     {
1031       /*
1032        * Still getting the disk's geometry 
1033        */
1034       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1035       continue;
1036     }
1037         if (aoe_disk_ptr->search_state ==
1038           aoe__search_state_getting_max_sectors_per_packet_)
1039     {
1040       KeQuerySystemTime ( &CurrentTime );
1041       /*
1042        * TODO: Make the below value a #defined constant 
1043        */
1044       /*
1045        * 2.500.000 * 100ns = 250.000.000 ns = 250ms 
1046        */
1047       if ( CurrentTime.QuadPart >
1048            MaxSectorsPerPacketSendTime.QuadPart + 2500000LL )
1049         {
1050           DBG ( "No reply after 250ms for MaxSectorsPerPacket %d, "
1051           "giving up\n", aoe_disk_ptr->MaxSectorsPerPacket );
1052           aoe_disk_ptr->MaxSectorsPerPacket--;
1053           aoe_disk_ptr->search_state = aoe__search_state_done_;
1054         }
1055       else
1056         {
1057           /*
1058            * Still getting the maximum sectors per packet count 
1059            */
1060           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1061           continue;
1062         }
1063     }
1064
1065         if (aoe_disk_ptr->search_state == aoe__search_state_done_)
1066     {
1067       /*
1068        * We've finished the disk search; perform clean-up 
1069        */
1070       KeAcquireSpinLock ( &aoe__spinlock_, &InnerIrql );
1071
1072       /*
1073        * Tag clean-up: Find out if our tag is in the global tag list 
1074        */
1075       tag_walker = aoe__tag_list_;
1076       while ( tag_walker != NULL && tag_walker != tag )
1077         tag_walker = tag_walker->next;
1078       if ( tag_walker != NULL )
1079         {
1080           /*
1081            * We found it.  If it's at the beginning of the list, adjust
1082            * the list to point the the next tag
1083            */
1084           if ( tag->previous == NULL )
1085       aoe__tag_list_ = tag->next;
1086           else
1087       /*
1088        * Remove our tag from the list 
1089        */
1090       tag->previous->next = tag->next;
1091           /*
1092            * If we 're at the end of the list, adjust the list's end to
1093            * point to the penultimate tag
1094            */
1095           if ( tag->next == NULL )
1096       aoe__tag_list_last_ = tag->previous;
1097           else
1098       /*
1099        * Remove our tag from the list 
1100        */
1101       tag->next->previous = tag->previous;
1102           aoe__outstanding_tags_--;
1103           if ( aoe__outstanding_tags_ < 0 )
1104       DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1105           /*
1106            * Free our tag and its AoE packet 
1107            */
1108           wv_free(tag->packet_data);
1109           wv_free(tag);
1110         }
1111
1112       /*
1113        * Disk search clean-up 
1114        */
1115       if ( aoe__disk_search_list_ == NULL )
1116         {
1117           DBG ( "aoe__disk_search_list_ == NULL!!\n" );
1118         }
1119       else
1120         {
1121           /*
1122            * Find our disk search in the global list of disk searches 
1123            */
1124           disk_search_walker = aoe__disk_search_list_;
1125           while (
1126               disk_search_walker &&
1127               disk_search_walker->device != disk_ptr->Dev
1128             ) {
1129         previous_disk_searcher = disk_search_walker;
1130         disk_search_walker = disk_search_walker->next;
1131       }
1132           if ( disk_search_walker )
1133       {
1134         /*
1135          * We found our disk search.  If it's the first one in
1136          * the list, adjust the list and remove it
1137          */
1138         if ( disk_search_walker == aoe__disk_search_list_ )
1139           aoe__disk_search_list_ = disk_search_walker->next;
1140         else
1141           /*
1142            * Just remove it 
1143            */
1144           previous_disk_searcher->next = disk_search_walker->next;
1145         /*
1146          * Free our disk search 
1147          */
1148         wv_free(disk_search_walker);
1149       }
1150           else
1151       {
1152         DBG ( "Disk not found in aoe__disk_search_list_!!\n" );
1153       }
1154         }
1155
1156       /*
1157        * Release global and device extension spin-locks 
1158        */
1159       KeReleaseSpinLock ( &aoe__spinlock_, InnerIrql );
1160       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1161
1162       DBG ( "Disk size: %I64uM cylinders: %I64u heads: %u "
1163       "sectors: %u sectors per packet: %u\n",
1164       disk_ptr->LBADiskSize / 2048, disk_ptr->Cylinders,
1165       disk_ptr->Heads, disk_ptr->Sectors,
1166       aoe_disk_ptr->MaxSectorsPerPacket );
1167       return TRUE;
1168     }
1169
1170         #if 0
1171         if ( aoe_disk_ptr->search_state == aoe__search_state_done_)
1172         #endif
1173         /*
1174          * Establish our tag 
1175          */
1176         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
1177       DBG ( "Couldn't allocate tag\n" );
1178       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1179       /*
1180        * Maybe next time around 
1181        */
1182       continue;
1183     }
1184         tag->type = aoe__tag_type_search_drive_;
1185         tag->device = disk_ptr->Dev;
1186
1187         /*
1188          * Establish our tag's AoE packet 
1189          */
1190         tag->PacketSize = sizeof (struct aoe__packet_);
1191         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
1192       DBG ( "Couldn't allocate tag->packet_data\n" );
1193       wv_free(tag);
1194       tag = NULL;
1195       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1196       /*
1197        * Maybe next time around 
1198        */
1199       continue;
1200     }
1201         tag->packet_data->Ver = AOEPROTOCOLVER;
1202         tag->packet_data->Major =
1203     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
1204         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
1205         tag->packet_data->ExtendedAFlag = TRUE;
1206
1207         /*
1208          * Initialize the packet appropriately based on our current phase 
1209          */
1210         switch ( aoe_disk_ptr->search_state )
1211     {
1212       case aoe__search_state_get_size_:
1213         /*
1214          * TODO: Make the below value into a #defined constant 
1215          */
1216         tag->packet_data->Cmd = 0xec;  /* IDENTIFY DEVICE */
1217         tag->packet_data->Count = 1;
1218         aoe_disk_ptr->search_state = aoe__search_state_getting_size_;
1219         break;
1220       case aoe__search_state_get_geometry_:
1221         /*
1222          * TODO: Make the below value into a #defined constant 
1223          */
1224         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
1225         tag->packet_data->Count = 1;
1226         aoe_disk_ptr->search_state = aoe__search_state_getting_geometry_;
1227         break;
1228       case aoe__search_state_get_max_sectors_per_packet_:
1229         /*
1230          * TODO: Make the below value into a #defined constant 
1231          */
1232         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
1233         tag->packet_data->Count =
1234           ( winvblock__uint8 ) ( ++aoe_disk_ptr->MaxSectorsPerPacket );
1235         KeQuerySystemTime ( &MaxSectorsPerPacketSendTime );
1236         aoe_disk_ptr->search_state =
1237           aoe__search_state_getting_max_sectors_per_packet_;
1238         /*
1239          * TODO: Make the below value into a #defined constant 
1240          */
1241         aoe_disk_ptr->Timeout = 200000;
1242         break;
1243       default:
1244         DBG ( "Undefined search_state!!\n" );
1245         wv_free(tag->packet_data);
1246         wv_free(tag);
1247         /*
1248          * TODO: Do we need to nullify tag here? 
1249          */
1250         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1251         continue;
1252         break;
1253     }
1254
1255         /*
1256          * Enqueue our tag 
1257          */
1258         tag->next = NULL;
1259         KeAcquireSpinLock ( &aoe__spinlock_, &InnerIrql );
1260         if ( aoe__tag_list_ == NULL )
1261     {
1262       aoe__tag_list_ = tag;
1263       tag->previous = NULL;
1264     }
1265         else
1266     {
1267       aoe__tag_list_last_->next = tag;
1268       tag->previous = aoe__tag_list_last_;
1269     }
1270         aoe__tag_list_last_ = tag;
1271         KeReleaseSpinLock ( &aoe__spinlock_, InnerIrql );
1272         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1273       }
1274   }
1275
1276 static NTSTATUS STDCALL io(
1277     IN WV_SP_DEV_T dev_ptr,
1278     IN WV_E_DISK_IO_MODE mode,
1279     IN LONGLONG start_sector,
1280     IN winvblock__uint32 sector_count,
1281     IN winvblock__uint8_ptr buffer,
1282     IN PIRP irp
1283   ) {
1284     struct aoe__io_req_ * request_ptr;
1285     struct aoe__work_tag_ * tag, * new_tag_list = NULL, * previous_tag = NULL;
1286     KIRQL Irql;
1287     winvblock__uint32 i;
1288     PHYSICAL_ADDRESS PhysicalAddress;
1289     winvblock__uint8_ptr PhysicalMemory;
1290     WV_SP_DISK_T disk_ptr;
1291     struct aoe__disk_type_ * aoe_disk_ptr;
1292
1293     /*
1294      * Establish pointers to the disk and AoE disk
1295      */
1296     disk_ptr = disk__get_ptr ( dev_ptr );
1297     aoe_disk_ptr = aoe__get_ ( dev_ptr );
1298
1299     if ( aoe__stop_ )
1300       {
1301         /*
1302          * Shutting down AoE; we can't service this request 
1303          */
1304         irp->IoStatus.Information = 0;
1305         irp->IoStatus.Status = STATUS_CANCELLED;
1306         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1307         return STATUS_CANCELLED;
1308       }
1309
1310     if ( sector_count < 1 )
1311       {
1312         /*
1313          * A silly request 
1314          */
1315         DBG ( "sector_count < 1; cancelling\n" );
1316         irp->IoStatus.Information = 0;
1317         irp->IoStatus.Status = STATUS_CANCELLED;
1318         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1319         return STATUS_CANCELLED;
1320       }
1321
1322     /*
1323      * Allocate and zero-fill our request 
1324      */
1325     if ((request_ptr = wv_mallocz(sizeof *request_ptr)) == NULL) {
1326         DBG ( "Couldn't allocate for reques_ptr; bye!\n" );
1327         irp->IoStatus.Information = 0;
1328         irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1329         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1330         return STATUS_INSUFFICIENT_RESOURCES;
1331       }
1332
1333     /*
1334      * Initialize the request 
1335      */
1336     request_ptr->Mode = mode;
1337     request_ptr->SectorCount = sector_count;
1338     request_ptr->Buffer = buffer;
1339     request_ptr->Irp = irp;
1340     request_ptr->TagCount = 0;
1341
1342     /*
1343      * Split the requested sectors into packets in tags
1344      */
1345     for ( i = 0; i < sector_count; i += aoe_disk_ptr->MaxSectorsPerPacket )
1346       {
1347         /*
1348          * Allocate each tag 
1349          */
1350         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
1351       DBG ( "Couldn't allocate tag; bye!\n" );
1352       /*
1353        * We failed while allocating tags; free the ones we built 
1354        */
1355       tag = new_tag_list;
1356       while ( tag != NULL )
1357         {
1358           previous_tag = tag;
1359           tag = tag->next;
1360           wv_free(previous_tag->packet_data);
1361           wv_free(previous_tag);
1362         }
1363       wv_free(request_ptr);
1364       irp->IoStatus.Information = 0;
1365       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1366       IoCompleteRequest ( irp, IO_NO_INCREMENT );
1367       return STATUS_INSUFFICIENT_RESOURCES;
1368     }
1369
1370         /*
1371          * Initialize each tag 
1372          */
1373         tag->type = aoe__tag_type_io_;
1374         tag->request_ptr = request_ptr;
1375         tag->device = dev_ptr;
1376         request_ptr->TagCount++;
1377         tag->Id = 0;
1378         tag->BufferOffset = i * disk_ptr->SectorSize;
1379         tag->SectorCount =
1380     ( ( sector_count - i ) <
1381       aoe_disk_ptr->MaxSectorsPerPacket ? sector_count -
1382       i : aoe_disk_ptr->MaxSectorsPerPacket );
1383
1384         /*
1385          * Allocate and initialize each tag's AoE packet 
1386          */
1387         tag->PacketSize = sizeof (struct aoe__packet_);
1388         if (mode == WvDiskIoModeWrite)
1389     tag->PacketSize += tag->SectorCount * disk_ptr->SectorSize;
1390         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
1391       DBG ( "Couldn't allocate tag->packet_data; bye!\n" );
1392       /*
1393        * We failed while allocating an AoE packet; free
1394        * the tags we built
1395        */
1396       wv_free(tag);
1397       tag = new_tag_list;
1398       while ( tag != NULL )
1399         {
1400           previous_tag = tag;
1401           tag = tag->next;
1402           wv_free(previous_tag->packet_data);
1403           wv_free(previous_tag);
1404         }
1405       wv_free(request_ptr);
1406       irp->IoStatus.Information = 0;
1407       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1408       IoCompleteRequest ( irp, IO_NO_INCREMENT );
1409       return STATUS_INSUFFICIENT_RESOURCES;
1410     }
1411         tag->packet_data->Ver = AOEPROTOCOLVER;
1412         tag->packet_data->Major =
1413     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
1414         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
1415         tag->packet_data->Tag = 0;
1416         tag->packet_data->Command = 0;
1417         tag->packet_data->ExtendedAFlag = TRUE;
1418         if (mode == WvDiskIoModeRead)
1419     {
1420       tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
1421     }
1422         else
1423     {
1424       tag->packet_data->Cmd = 0x34;  /* WRITE SECTOR */
1425       tag->packet_data->WriteAFlag = 1;
1426     }
1427         tag->packet_data->Count = ( winvblock__uint8 ) tag->SectorCount;
1428         tag->packet_data->Lba0 =
1429     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 0 ) & 255 );
1430         tag->packet_data->Lba1 =
1431     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 8 ) & 255 );
1432         tag->packet_data->Lba2 =
1433     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 16 ) & 255 );
1434         tag->packet_data->Lba3 =
1435     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 24 ) & 255 );
1436         tag->packet_data->Lba4 =
1437     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 32 ) & 255 );
1438         tag->packet_data->Lba5 =
1439     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 40 ) & 255 );
1440
1441         /* For a write request, copy from the buffer into the AoE packet. */
1442         if (mode == WvDiskIoModeWrite)
1443     RtlCopyMemory ( tag->packet_data->Data, &buffer[tag->BufferOffset],
1444         tag->SectorCount * disk_ptr->SectorSize );
1445
1446         /*
1447          * Add this tag to the request's tag list 
1448          */
1449         tag->previous = previous_tag;
1450         tag->next = NULL;
1451         if ( new_tag_list == NULL )
1452     {
1453       new_tag_list = tag;
1454     }
1455         else
1456     {
1457       previous_tag->next = tag;
1458     }
1459         previous_tag = tag;
1460       }
1461     /*
1462      * Split the requested sectors into packets in tags
1463      */
1464     request_ptr->TotalTags = request_ptr->TagCount;
1465
1466     /*
1467      * Wait until we have the global spin-lock 
1468      */
1469     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1470
1471     /*
1472      * Enqueue our request's tag list to the global tag list 
1473      */
1474     if ( aoe__tag_list_last_ == NULL )
1475       {
1476         aoe__tag_list_ = new_tag_list;
1477       }
1478     else
1479       {
1480         aoe__tag_list_last_->next = new_tag_list;
1481         new_tag_list->previous = aoe__tag_list_last_;
1482       }
1483     /*
1484      * Adjust the global list to reflect our last tag 
1485      */
1486     aoe__tag_list_last_ = tag;
1487
1488     irp->IoStatus.Information = 0;
1489     irp->IoStatus.Status = STATUS_PENDING;
1490     IoMarkIrpPending ( irp );
1491
1492     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1493     KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1494     return STATUS_PENDING;
1495   }
1496
1497 static void STDCALL add_target(
1498     IN winvblock__uint8_ptr ClientMac,
1499     IN winvblock__uint8_ptr ServerMac,
1500     winvblock__uint16 Major,
1501     winvblock__uint8 Minor,
1502     LONGLONG LBASize
1503   )
1504   {
1505     struct aoe__target_list_ * Walker, * Last;
1506     KIRQL Irql;
1507
1508     KeAcquireSpinLock ( &aoe__target_list_spinlock_, &Irql );
1509     Walker = Last = aoe__target_list_;
1510     while ( Walker != NULL )
1511       {
1512         if (wv_memcmpeq(&Walker->Target.ClientMac, ClientMac, 6) &&
1513           wv_memcmpeq(&Walker->Target.ServerMac, ServerMac, 6) &&
1514           Walker->Target.Major == Major
1515           && Walker->Target.Minor == Minor) {
1516       if ( Walker->Target.LBASize != LBASize )
1517         {
1518           DBG ( "LBASize changed for e%d.%d " "(%I64u->%I64u)\n", Major,
1519           Minor, Walker->Target.LBASize, LBASize );
1520           Walker->Target.LBASize = LBASize;
1521         }
1522       KeQuerySystemTime ( &Walker->Target.ProbeTime );
1523       KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1524       return;
1525     }
1526         Last = Walker;
1527         Walker = Walker->next;
1528       }
1529
1530     if ((Walker = wv_malloc(sizeof *Walker)) == NULL) {
1531         DBG("wv_malloc Walker\n");
1532         KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1533         return;
1534       }
1535     Walker->next = NULL;
1536     RtlCopyMemory ( Walker->Target.ClientMac, ClientMac, 6 );
1537     RtlCopyMemory ( Walker->Target.ServerMac, ServerMac, 6 );
1538     Walker->Target.Major = Major;
1539     Walker->Target.Minor = Minor;
1540     Walker->Target.LBASize = LBASize;
1541     KeQuerySystemTime ( &Walker->Target.ProbeTime );
1542
1543     if ( Last == NULL )
1544       {
1545         aoe__target_list_ = Walker;
1546       }
1547     else
1548       {
1549         Last->next = Walker;
1550       }
1551     KeReleaseSpinLock ( &aoe__target_list_spinlock_, Irql );
1552   }
1553
1554 /**
1555  * Process an AoE reply.
1556  *
1557  * @v SourceMac         The AoE server's MAC address.
1558  * @v DestinationMac    The AoE client's MAC address.
1559  * @v Data              The AoE packet.
1560  * @v DataSize          The AoE packet's size.
1561  */
1562 NTSTATUS STDCALL aoe__reply(
1563     IN winvblock__uint8_ptr SourceMac,
1564     IN winvblock__uint8_ptr DestinationMac,
1565     IN winvblock__uint8_ptr Data,
1566     IN winvblock__uint32 DataSize
1567   )
1568   {
1569     struct aoe__packet_ * reply = (struct aoe__packet_ *) Data;
1570     LONGLONG LBASize;
1571     struct aoe__work_tag_ * tag;
1572     KIRQL Irql;
1573     winvblock__bool Found = FALSE;
1574     LARGE_INTEGER CurrentTime;
1575     WV_SP_DISK_T disk_ptr;
1576     struct aoe__disk_type_ * aoe_disk_ptr;
1577
1578     /*
1579      * Discard non-responses 
1580      */
1581     if ( !reply->ResponseFlag )
1582       return STATUS_SUCCESS;
1583
1584     /*
1585      * If the response matches our probe, add the AoE disk device 
1586      */
1587     if ( aoe__probe_tag_->Id == reply->Tag )
1588       {
1589         RtlCopyMemory ( &LBASize, &reply->Data[200], sizeof ( LONGLONG ) );
1590         add_target ( DestinationMac, SourceMac, ntohs ( reply->Major ),
1591          reply->Minor, LBASize );
1592         return STATUS_SUCCESS;
1593       }
1594
1595     /*
1596      * Wait until we have the global spin-lock 
1597      */
1598     KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1599
1600     /*
1601      * Search for request tag 
1602      */
1603     if ( aoe__tag_list_ == NULL )
1604       {
1605         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1606         return STATUS_SUCCESS;
1607       }
1608     tag = aoe__tag_list_;
1609     while ( tag != NULL )
1610       {
1611         if ( ( tag->Id == reply->Tag )
1612        && ( tag->packet_data->Major == reply->Major )
1613        && ( tag->packet_data->Minor == reply->Minor ) )
1614     {
1615       Found = TRUE;
1616       break;
1617     }
1618         tag = tag->next;
1619       }
1620     if ( !Found )
1621       {
1622         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1623         return STATUS_SUCCESS;
1624       }
1625     else
1626       {
1627         /*
1628          * Remove the tag from the global tag list 
1629          */
1630         if ( tag->previous == NULL )
1631     aoe__tag_list_ = tag->next;
1632         else
1633     tag->previous->next = tag->next;
1634         if ( tag->next == NULL )
1635     aoe__tag_list_last_ = tag->previous;
1636         else
1637     tag->next->previous = tag->previous;
1638         aoe__outstanding_tags_--;
1639         if ( aoe__outstanding_tags_ < 0 )
1640     DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1641         KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1642       }
1643     KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1644
1645     /*
1646      * Establish pointers to the disk device and AoE disk
1647      */
1648     disk_ptr = disk__get_ptr ( tag->device );
1649     aoe_disk_ptr = aoe__get_ ( tag->device );
1650
1651     /*
1652      * If our tag was a discovery request, note the server 
1653      */
1654     if (wv_memcmpeq(aoe_disk_ptr->ServerMac, "\xff\xff\xff\xff\xff\xff", 6)) {
1655         RtlCopyMemory ( aoe_disk_ptr->ServerMac, SourceMac, 6 );
1656         DBG ( "Major: %d minor: %d found on server "
1657         "%02x:%02x:%02x:%02x:%02x:%02x\n", aoe_disk_ptr->Major,
1658         aoe_disk_ptr->Minor, SourceMac[0], SourceMac[1], SourceMac[2],
1659         SourceMac[3], SourceMac[4], SourceMac[5] );
1660       }
1661
1662     KeQuerySystemTime ( &CurrentTime );
1663     aoe_disk_ptr->Timeout -=
1664       ( winvblock__uint32 ) ( ( aoe_disk_ptr->Timeout -
1665               ( CurrentTime.QuadPart -
1666           tag->FirstSendTime.QuadPart ) ) / 1024 );
1667     /*
1668      * TODO: Replace the values below with #defined constants 
1669      */
1670     if ( aoe_disk_ptr->Timeout > 100000000 )
1671       aoe_disk_ptr->Timeout = 100000000;
1672
1673     switch ( tag->type )
1674       {
1675         case aoe__tag_type_search_drive_:
1676     KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
1677     switch ( aoe_disk_ptr->search_state )
1678       {
1679         case aoe__search_state_getting_size_:
1680           /*
1681            * The reply tells us the disk size
1682            */
1683           RtlCopyMemory ( &disk_ptr->LBADiskSize, &reply->Data[200],
1684               sizeof ( LONGLONG ) );
1685           /*
1686            * Next we are concerned with the disk geometry
1687            */
1688           aoe_disk_ptr->search_state = aoe__search_state_get_geometry_;
1689           break;
1690         case aoe__search_state_getting_geometry_:
1691           /*
1692            * FIXME: use real values from partition table.
1693            * We used to truncate a fractional end cylinder, but
1694            * now leave it be in the hopes everyone uses LBA
1695            */
1696           disk_ptr->SectorSize = 512;
1697           disk_ptr->Heads = 255;
1698           disk_ptr->Sectors = 63;
1699           disk_ptr->Cylinders =
1700       disk_ptr->LBADiskSize / ( disk_ptr->Heads *
1701               disk_ptr->Sectors );
1702           /*
1703            * Next we are concerned with the maximum sectors per packet
1704            */
1705           aoe_disk_ptr->search_state =
1706             aoe__search_state_get_max_sectors_per_packet_;
1707           break;
1708         case aoe__search_state_getting_max_sectors_per_packet_:
1709           DataSize -= sizeof (struct aoe__packet_);
1710           if ( DataSize <
1711          ( aoe_disk_ptr->MaxSectorsPerPacket *
1712            disk_ptr->SectorSize ) )
1713       {
1714         DBG ( "Packet size too low while getting "
1715         "MaxSectorsPerPacket (tried %d, got size of %d)\n",
1716         aoe_disk_ptr->MaxSectorsPerPacket, DataSize );
1717         aoe_disk_ptr->MaxSectorsPerPacket--;
1718         aoe_disk_ptr->search_state = aoe__search_state_done_;
1719       }
1720           else if ( aoe_disk_ptr->MTU <
1721         ( sizeof (struct aoe__packet_) +
1722           ( ( aoe_disk_ptr->MaxSectorsPerPacket +
1723               1 ) * disk_ptr->SectorSize ) ) )
1724       {
1725         DBG ( "Got MaxSectorsPerPacket %d at size of %d. "
1726         "MTU of %d reached\n",
1727         aoe_disk_ptr->MaxSectorsPerPacket, DataSize,
1728         aoe_disk_ptr->MTU );
1729         aoe_disk_ptr->search_state = aoe__search_state_done_;
1730       }
1731           else
1732       {
1733         DBG ( "Got MaxSectorsPerPacket %d at size of %d, "
1734         "trying next...\n", aoe_disk_ptr->MaxSectorsPerPacket,
1735         DataSize );
1736         aoe_disk_ptr->search_state =
1737           aoe__search_state_get_max_sectors_per_packet_;
1738       }
1739           break;
1740         default:
1741           DBG ( "Undefined search_state!\n" );
1742           break;
1743       }
1744     KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1745     KeSetEvent ( &disk_ptr->SearchEvent, 0, FALSE );
1746     break;
1747         case aoe__tag_type_io_:
1748     /* If the reply is in response to a read request, get our data! */
1749     if (tag->request_ptr->Mode == WvDiskIoModeRead)
1750       RtlCopyMemory ( &tag->request_ptr->Buffer[tag->BufferOffset],
1751           reply->Data,
1752           tag->SectorCount * disk_ptr->SectorSize );
1753     /*
1754      * If this is the last reply expected for the read request,
1755      * complete the IRP and free the request
1756      */
1757     if ( InterlockedDecrement ( &tag->request_ptr->TagCount ) == 0 )
1758       {
1759         tag->request_ptr->Irp->IoStatus.Information =
1760           tag->request_ptr->SectorCount * disk_ptr->SectorSize;
1761         tag->request_ptr->Irp->IoStatus.Status = STATUS_SUCCESS;
1762         Driver_CompletePendingIrp ( tag->request_ptr->Irp );
1763         wv_free(tag->request_ptr);
1764       }
1765     break;
1766         default:
1767     DBG ( "Unknown tag type!!\n" );
1768     break;
1769       }
1770
1771     KeSetEvent ( &aoe__thread_sig_evt_, 0, FALSE );
1772     wv_free(tag->packet_data);
1773     wv_free(tag);
1774     return STATUS_SUCCESS;
1775   }
1776
1777 void aoe__reset_probe(void)
1778   {
1779     aoe__probe_tag_->SendTime.QuadPart = 0LL;
1780   }
1781
1782 static void STDCALL aoe__thread_(IN void *StartContext)
1783   {
1784     LARGE_INTEGER Timeout, CurrentTime, ProbeTime, ReportTime;
1785     winvblock__uint32 NextTagId = 1;
1786     struct aoe__work_tag_ * tag;
1787     KIRQL Irql;
1788     winvblock__uint32 Sends = 0;
1789     winvblock__uint32 Resends = 0;
1790     winvblock__uint32 ResendFails = 0;
1791     winvblock__uint32 Fails = 0;
1792     winvblock__uint32 RequestTimeout = 0;
1793     WV_SP_DISK_T disk_ptr;
1794     struct aoe__disk_type_ * aoe_disk_ptr;
1795
1796     DBG ( "Entry\n" );
1797     ReportTime.QuadPart = 0LL;
1798     ProbeTime.QuadPart = 0LL;
1799
1800     while ( TRUE )
1801       {
1802         /*
1803          * TODO: Make the below value a #defined constant 
1804          */
1805         /*
1806          * 100.000 * 100ns = 10.000.000 ns = 10ms
1807          */
1808         Timeout.QuadPart = -100000LL;
1809         KeWaitForSingleObject ( &aoe__thread_sig_evt_, Executive,
1810               KernelMode, FALSE, &Timeout );
1811         KeResetEvent ( &aoe__thread_sig_evt_ );
1812         if ( aoe__stop_ )
1813     {
1814       DBG ( "Stopping...\n" );
1815       PsTerminateSystemThread ( STATUS_SUCCESS );
1816     }
1817
1818         KeQuerySystemTime ( &CurrentTime );
1819         /*
1820          * TODO: Make the below value a #defined constant 
1821          */
1822         if ( CurrentTime.QuadPart > ( ReportTime.QuadPart + 10000000LL ) )
1823     {
1824       DBG ( "Sends: %d  Resends: %d  ResendFails: %d  Fails: %d  "
1825       "aoe__outstanding_tags_: %d  RequestTimeout: %d\n", Sends,
1826       Resends, ResendFails, Fails, aoe__outstanding_tags_,
1827       RequestTimeout );
1828       Sends = 0;
1829       Resends = 0;
1830       ResendFails = 0;
1831       Fails = 0;
1832       KeQuerySystemTime ( &ReportTime );
1833     }
1834
1835         /*
1836          * TODO: Make the below value a #defined constant 
1837          */
1838         if ( CurrentTime.QuadPart >
1839        ( aoe__probe_tag_->SendTime.QuadPart + 100000000LL ) )
1840     {
1841       aoe__probe_tag_->Id = NextTagId++;
1842       if ( NextTagId == 0 )
1843         NextTagId++;
1844       aoe__probe_tag_->packet_data->Tag = aoe__probe_tag_->Id;
1845       Protocol_Send ( "\xff\xff\xff\xff\xff\xff",
1846           "\xff\xff\xff\xff\xff\xff",
1847           ( winvblock__uint8_ptr ) aoe__probe_tag_->
1848           packet_data, aoe__probe_tag_->PacketSize,
1849           NULL );
1850       KeQuerySystemTime ( &aoe__probe_tag_->SendTime );
1851     }
1852
1853         KeAcquireSpinLock ( &aoe__spinlock_, &Irql );
1854         if ( aoe__tag_list_ == NULL )
1855     {
1856       KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1857       continue;
1858     }
1859         tag = aoe__tag_list_;
1860         while ( tag != NULL )
1861     {
1862       /*
1863        * Establish pointers to the disk and AoE disk
1864        */
1865       disk_ptr = disk__get_ptr ( tag->device );
1866       aoe_disk_ptr = aoe__get_ ( tag->device );
1867
1868       RequestTimeout = aoe_disk_ptr->Timeout;
1869       if ( tag->Id == 0 )
1870         {
1871           if ( aoe__outstanding_tags_ <= 64 )
1872       {
1873         /*
1874          * if ( aoe__outstanding_tags_ <= 102400 ) { 
1875          */
1876         if ( aoe__outstanding_tags_ < 0 )
1877           DBG ( "aoe__outstanding_tags_ < 0!!\n" );
1878         tag->Id = NextTagId++;
1879         if ( NextTagId == 0 )
1880           NextTagId++;
1881         tag->packet_data->Tag = tag->Id;
1882         if ( Protocol_Send
1883              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1884          ( winvblock__uint8_ptr ) tag->packet_data,
1885          tag->PacketSize, tag ) )
1886           {
1887             KeQuerySystemTime ( &tag->FirstSendTime );
1888             KeQuerySystemTime ( &tag->SendTime );
1889             aoe__outstanding_tags_++;
1890             Sends++;
1891           }
1892         else
1893           {
1894             Fails++;
1895             tag->Id = 0;
1896             break;
1897           }
1898       }
1899         }
1900       else
1901         {
1902           KeQuerySystemTime ( &CurrentTime );
1903           if ( CurrentTime.QuadPart >
1904          ( tag->SendTime.QuadPart +
1905            ( LONGLONG ) ( aoe_disk_ptr->Timeout * 2 ) ) )
1906       {
1907         if ( Protocol_Send
1908              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1909          ( winvblock__uint8_ptr ) tag->packet_data,
1910          tag->PacketSize, tag ) )
1911           {
1912             KeQuerySystemTime ( &tag->SendTime );
1913             aoe_disk_ptr->Timeout += aoe_disk_ptr->Timeout / 1000;
1914             if ( aoe_disk_ptr->Timeout > 100000000 )
1915         aoe_disk_ptr->Timeout = 100000000;
1916             Resends++;
1917           }
1918         else
1919           {
1920             ResendFails++;
1921             break;
1922           }
1923       }
1924         }
1925       tag = tag->next;
1926       if ( tag == aoe__tag_list_ )
1927         {
1928           DBG ( "Taglist Cyclic!!\n" );
1929           break;
1930         }
1931     }
1932         KeReleaseSpinLock ( &aoe__spinlock_, Irql );
1933       }
1934     DBG ( "Exit\n" );
1935   }
1936
1937 static winvblock__uint32 max_xfer_len(IN WV_SP_DISK_T disk_ptr) {
1938     struct aoe__disk_type_ * aoe_disk_ptr = aoe__get_(disk_ptr->Dev);
1939
1940     return disk_ptr->SectorSize * aoe_disk_ptr->MaxSectorsPerPacket;
1941   }
1942
1943 static winvblock__uint32 STDCALL query_id(
1944     IN WV_SP_DEV_T dev,
1945     IN BUS_QUERY_ID_TYPE query_type,
1946     IN OUT WCHAR (*buf)[512]
1947   ) {
1948     struct aoe__disk_type_ * aoe_disk = aoe__get_(dev);
1949
1950     switch (query_type) {
1951         case BusQueryDeviceID:
1952           return swprintf(*buf, winvblock__literal_w L"\\AoEHardDisk") + 1;
1953
1954         case BusQueryInstanceID:
1955           return swprintf(
1956               *buf,
1957               L"AoE_at_Shelf_%d.Slot_%d",
1958               aoe_disk->Major,
1959               aoe_disk->Minor
1960             ) + 1;
1961
1962         case BusQueryHardwareIDs: {
1963             winvblock__uint32 tmp;
1964
1965             tmp = swprintf(
1966                 *buf,
1967                 winvblock__literal_w L"\\AoEHardDisk"
1968               ) + 1;
1969             tmp += swprintf(*buf + tmp, L"GenDisk") + 4;
1970             return tmp;
1971           }
1972
1973         case BusQueryCompatibleIDs:
1974           return swprintf(*buf, L"GenDisk") + 4;
1975
1976         default:
1977           return 0;
1978       }
1979   }
1980
1981 #ifdef _MSC_VER
1982 #  pragma pack(1)
1983 #endif
1984 winvblock__def_struct(abft) {
1985     winvblock__uint32 Signature;  /* 0x54464261 (aBFT) */
1986     winvblock__uint32 Length;
1987     winvblock__uint8 Revision;
1988     winvblock__uint8 Checksum;
1989     winvblock__uint8 OEMID[6];
1990     winvblock__uint8 OEMTableID[8];
1991     winvblock__uint8 Reserved1[12];
1992     winvblock__uint16 Major;
1993     winvblock__uint8 Minor;
1994     winvblock__uint8 Reserved2;
1995     winvblock__uint8 ClientMac[6];
1996   } __attribute__((__packed__));
1997 #ifdef _MSC_VER
1998 #  pragma pack()
1999 #endif
2000
2001 static void STDCALL close(IN WV_SP_DISK_T disk_ptr) {
2002     return;
2003   }
2004
2005 static void aoe__process_abft_(void) {
2006     PHYSICAL_ADDRESS PhysicalAddress;
2007     winvblock__uint8_ptr PhysicalMemory;
2008     winvblock__uint32 Offset, Checksum, i;
2009     winvblock__bool FoundAbft = FALSE;
2010     abft AoEBootRecord;
2011     struct aoe__disk_type_ * aoe_disk;
2012
2013     /* Find aBFT. */
2014     PhysicalAddress.QuadPart = 0LL;
2015     PhysicalMemory = MmMapIoSpace(PhysicalAddress, 0xa0000, MmNonCached);
2016     if (!PhysicalMemory) {
2017         DBG("Could not map low memory\n");
2018         goto err_map_mem;
2019       }
2020     for (Offset = 0; Offset < 0xa0000; Offset += 0x10) {
2021         if (!(
2022             ((abft_ptr) (PhysicalMemory + Offset))->Signature ==
2023             0x54464261
2024           ))
2025           continue;
2026         Checksum = 0;
2027         for (
2028             i = 0;
2029             i < ((abft_ptr) (PhysicalMemory + Offset))->Length;
2030             i++
2031           )
2032           Checksum += PhysicalMemory[Offset + i];
2033         if (Checksum & 0xff)
2034           continue;
2035         if (((abft_ptr) (PhysicalMemory + Offset))->Revision != 1) {
2036             DBG(
2037                 "Found aBFT with mismatched revision v%d at "
2038                   "segment 0x%4x. want v1.\n",
2039                 ((abft_ptr) (PhysicalMemory + Offset))->Revision,
2040                 (Offset / 0x10)
2041               );
2042             continue;
2043           }
2044         DBG("Found aBFT at segment: 0x%04x\n", (Offset / 0x10));
2045         RtlCopyMemory(
2046             &AoEBootRecord,
2047             PhysicalMemory + Offset,
2048             sizeof (abft)
2049           );
2050         FoundAbft = TRUE;
2051         break;
2052       }
2053     MmUnmapIoSpace(PhysicalMemory, 0xa0000);
2054
2055     #ifdef RIS
2056     FoundAbft = TRUE;
2057     RtlCopyMemory(AoEBootRecord.ClientMac, "\x00\x0c\x29\x34\x69\x34", 6);
2058     AoEBootRecord.Major = 0;
2059     AoEBootRecord.Minor = 10;
2060     #endif
2061
2062     if (!FoundAbft)
2063       goto out_no_abft;
2064     aoe_disk = aoe__create_disk_();
2065     if(aoe_disk == NULL) {
2066         DBG("Could not create AoE disk from aBFT!\n");
2067         return;
2068       }
2069     DBG(
2070         "Attaching AoE disk from client NIC "
2071           "%02x:%02x:%02x:%02x:%02x:%02x to major: %d minor: %d\n",
2072         AoEBootRecord.ClientMac[0],
2073         AoEBootRecord.ClientMac[1],
2074         AoEBootRecord.ClientMac[2],
2075         AoEBootRecord.ClientMac[3],
2076         AoEBootRecord.ClientMac[4],
2077         AoEBootRecord.ClientMac[5],
2078         AoEBootRecord.Major,
2079         AoEBootRecord.Minor
2080       );
2081     RtlCopyMemory(aoe_disk->ClientMac, AoEBootRecord.ClientMac, 6);
2082     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
2083     aoe_disk->Major = AoEBootRecord.Major;
2084     aoe_disk->Minor = AoEBootRecord.Minor;
2085     aoe_disk->MaxSectorsPerPacket = 1;
2086     aoe_disk->Timeout = 200000;          /* 20 ms. */
2087     aoe_disk->disk->BootDrive = TRUE;
2088     aoe_disk->disk->Media = WvDiskMediaTypeHard;
2089     WvBusAddChild(driver__bus(), aoe_disk->disk->Dev);
2090     return;
2091
2092     out_no_abft:
2093     DBG("No aBFT found\n");
2094
2095     err_map_mem:
2096
2097     return;
2098   }
2099
2100 NTSTATUS STDCALL aoe__scan(
2101     IN WV_SP_DEV_T dev,
2102     IN PIRP irp
2103   ) {
2104     KIRQL irql;
2105     winvblock__uint32 count;
2106     struct aoe__target_list_ * target_walker;
2107     aoe__mount_targets_ptr targets;
2108     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2109
2110     DBG("Got IOCTL_AOE_SCAN...\n");
2111     KeAcquireSpinLock(&aoe__target_list_spinlock_, &irql);
2112
2113     count = 0;
2114     target_walker = aoe__target_list_;
2115     while (target_walker != NULL) {
2116         count++;
2117         target_walker = target_walker->next;
2118       }
2119
2120     targets = wv_malloc(sizeof *targets + (count * sizeof targets->Target[0]));
2121     if (targets == NULL) {
2122         DBG("wv_malloc targets\n");
2123         return driver__complete_irp(
2124             irp,
2125             0,
2126             STATUS_INSUFFICIENT_RESOURCES
2127           );
2128       }
2129     irp->IoStatus.Information =
2130       sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target));
2131     targets->Count = count;
2132
2133     count = 0;
2134     target_walker = aoe__target_list_;
2135     while (target_walker != NULL) {
2136         RtlCopyMemory(
2137             &targets->Target[count],
2138             &target_walker->Target,
2139             sizeof (aoe__mount_target)
2140           );
2141         count++;
2142         target_walker = target_walker->next;
2143       }
2144     RtlCopyMemory(
2145         irp->AssociatedIrp.SystemBuffer,
2146         targets,
2147         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
2148           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target))) ?
2149           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
2150           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target)))
2151         )
2152       );
2153     wv_free(targets);
2154
2155     KeReleaseSpinLock(&aoe__target_list_spinlock_, irql);
2156     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
2157   }
2158
2159 NTSTATUS STDCALL aoe__show(
2160     IN WV_SP_DEV_T dev,
2161     IN PIRP irp
2162   ) {
2163     winvblock__uint32 count;
2164     WV_SP_DEV_T dev_walker;
2165     WV_SP_BUS_T bus;
2166     aoe__mount_disks_ptr disks;
2167     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2168
2169     DBG("Got IOCTL_AOE_SHOW...\n");
2170
2171     bus = WvBusFromDev(dev);
2172     dev_walker = bus->first_child;
2173     count = 0;
2174     while (dev_walker != NULL) {
2175         count++;
2176         dev_walker = dev_walker->next_sibling_ptr;
2177       }
2178
2179     disks = wv_malloc(sizeof *disks + (count * sizeof disks->Disk[0]));
2180     if (disks == NULL) {
2181         DBG("wv_malloc disks\n");
2182         return driver__complete_irp(
2183             irp,
2184             0,
2185             STATUS_INSUFFICIENT_RESOURCES
2186           );
2187       }
2188     irp->IoStatus.Information =
2189       sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk));
2190     disks->Count = count;
2191
2192     count = 0;
2193     dev_walker = bus->first_child;
2194     while (dev_walker != NULL) {
2195         WV_SP_DISK_T disk = disk__get_ptr(dev_walker);
2196         struct aoe__disk_type_ * aoe_disk = aoe__get_(dev_walker);
2197
2198         disks->Disk[count].Disk = dev_walker->DevNum;
2199         RtlCopyMemory(
2200             &disks->Disk[count].ClientMac,
2201             &aoe_disk->ClientMac,
2202             6
2203           );
2204         RtlCopyMemory(
2205             &disks->Disk[count].ServerMac,
2206             &aoe_disk->ServerMac,
2207             6
2208           );
2209         disks->Disk[count].Major = aoe_disk->Major;
2210         disks->Disk[count].Minor = aoe_disk->Minor;
2211         disks->Disk[count].LBASize = disk->LBADiskSize;
2212         count++;
2213         dev_walker = dev_walker->next_sibling_ptr;
2214       }
2215     RtlCopyMemory(
2216         irp->AssociatedIrp.SystemBuffer,
2217         disks,
2218         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
2219           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk))) ?
2220           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
2221           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk)))
2222         )
2223       );
2224     wv_free(disks);
2225     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
2226   }
2227
2228 NTSTATUS STDCALL aoe__mount(
2229     IN WV_SP_DEV_T dev,
2230     IN PIRP irp
2231   ) {
2232     winvblock__uint8_ptr buffer = irp->AssociatedIrp.SystemBuffer;
2233     struct aoe__disk_type_ * aoe_disk;
2234
2235     DBG(
2236         "Got IOCTL_AOE_MOUNT for client: %02x:%02x:%02x:%02x:%02x:%02x "
2237           "Major:%d Minor:%d\n",
2238         buffer[0],
2239         buffer[1],
2240         buffer[2],
2241         buffer[3],
2242         buffer[4],
2243         buffer[5],
2244         *(winvblock__uint16_ptr) (buffer + 6),
2245         (winvblock__uint8) buffer[8]
2246       );
2247     aoe_disk = aoe__create_disk_();
2248     if (aoe_disk == NULL) {
2249         DBG("Could not create AoE disk!\n");
2250         return driver__complete_irp(
2251             irp,
2252             0,
2253             STATUS_INSUFFICIENT_RESOURCES
2254           );
2255       }
2256     RtlCopyMemory(aoe_disk->ClientMac, buffer, 6);
2257     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
2258     aoe_disk->Major = *(winvblock__uint16_ptr) (buffer + 6);
2259     aoe_disk->Minor = (winvblock__uint8) buffer[8];
2260     aoe_disk->MaxSectorsPerPacket = 1;
2261     aoe_disk->Timeout = 200000;             /* 20 ms. */
2262     aoe_disk->disk->BootDrive = FALSE;
2263     aoe_disk->disk->Media = WvDiskMediaTypeHard;
2264     WvBusAddChild(driver__bus(), aoe_disk->disk->Dev);
2265
2266     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
2267   }
2268
2269 /**
2270  * Create a new AoE disk.
2271  *
2272  * @ret aoe_disk *      The address of a new AoE disk, or NULL for failure.
2273  *
2274  * This function should not be confused with a PDO creation routine, which is
2275  * actually implemented for each device type.  This routine will allocate a
2276  * aoe__disk_type_, track it in a global list, as well as populate the disk
2277  * with default values.
2278  */
2279 static struct aoe__disk_type_ * aoe__create_disk_(void) {
2280     WV_SP_DISK_T disk;
2281     struct aoe__disk_type_ * aoe_disk;
2282
2283     /* Try to create a disk. */
2284     disk = disk__create();
2285     if (disk == NULL)
2286       goto err_nodisk;
2287     /*
2288      * AoE disk devices might be used for booting and should
2289      * not be allocated from a paged memory pool.
2290      */
2291     aoe_disk = wv_mallocz(sizeof *aoe_disk);
2292     if (aoe_disk == NULL)
2293       goto err_noaoedisk;
2294     /* Track the new AoE disk in our global list. */
2295     ExInterlockedInsertTailList(
2296         &aoe__disk_list_,
2297         &aoe_disk->tracking,
2298         &aoe__disk_list_lock_
2299       );
2300     /* Populate non-zero device defaults. */
2301     aoe_disk->disk = disk;
2302     aoe_disk->prev_free = disk->Dev->Ops.Free;
2303     disk->Dev->Ops.Free = aoe__free_disk_;
2304     disk->Dev->Ops.PnpId = query_id;
2305     disk->disk_ops.Io = io;
2306     disk->disk_ops.MaxXferLen = max_xfer_len;
2307     disk->disk_ops.Init = init;
2308     disk->disk_ops.Close = close;
2309     disk->ext = aoe_disk;
2310
2311     return aoe_disk;
2312
2313     err_noaoedisk:
2314
2315     WvDevFree(disk->Dev);
2316     err_nodisk:
2317
2318     return NULL;
2319   }
2320
2321 /**
2322  * Default AoE disk deletion operation.
2323  *
2324  * @v dev               Points to the AoE disk device to delete.
2325  */
2326 static void STDCALL aoe__free_disk_(IN WV_SP_DEV_T dev) {
2327     struct aoe__disk_type_ * aoe_disk = aoe__get_(dev);
2328     /* Free the "inherited class". */
2329     aoe_disk->prev_free(dev);
2330     /*
2331      * Track the AoE disk deletion in our global list.  Unfortunately,
2332      * for now we have faith that an AoE disk won't be deleted twice and
2333      * result in a race condition.  Something to keep in mind...
2334      */
2335     ExInterlockedRemoveHeadList(
2336         aoe_disk->tracking.Blink,
2337         &aoe__disk_list_lock_
2338       );
2339
2340     wv_free(aoe_disk);
2341   }