[iscsiboot] Remove extraneous comment block
[sanbootconf.git] / driver / iscsiboot.c
1 /*
2  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #pragma warning(disable:4201)  /* nameless struct/union warning */
20 #pragma warning(disable:4214)  /* non-int bitfield warning */
21
22 #include <ntddk.h>
23 #include <ntstrsafe.h>
24 #include <initguid.h>
25 #include <ndis.h>
26 #include <ndisguid.h>
27 #include <ntddndis.h>
28 #include <wdmsec.h>
29 #include <iscsicfg.h>
30 #include "ibft.h"
31
32 /** Tag to use for memory allocation */
33 #define ISCSIBOOT_POOL_TAG 'bcsi'
34
35 /** Start of region to scan in base memory */
36 #define BASEMEM_START 0x80000
37
38 /** End of region to scan in base memory */
39 #define BASEMEM_END 0xa0000
40
41 /** Length of region to scan in base memory */
42 #define BASEMEM_LEN ( BASEMEM_END - BASEMEM_START )
43
44 /** IoControl code to retrieve iSCSI boot data */
45 #define IOCTL_ISCSIBOOT CTL_CODE ( FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, \
46                                    FILE_READ_ACCESS )
47
48 /** Device private data */
49 typedef struct _ISCSIBOOT_PRIV {
50         /* Copy of iBFT */
51         PIBFT_TABLE ibft;
52 } ISCSIBOOT_PRIV, *PISCSIBOOT_PRIV;
53
54 /** Unique GUID for IoCreateDeviceSecure() */
55 DEFINE_GUID ( GUID_ISCSIBOOT_CLASS, 0x8a2f8602, 0x8f0b, 0x4138,
56               0x8e, 0x16, 0x51, 0x9a, 0x59, 0xf3, 0x07, 0xca );
57
58 /** iSCSI boot device name */
59 static const WCHAR iscsiboot_device_name[] = L"\\Device\\iSCSIBoot";
60
61 /** iSCSI boot device symlink name */
62 static const WCHAR iscsiboot_device_symlink[] = L"\\DosDevices\\iSCSIBoot";
63
64 /**
65  * Calculate byte checksum
66  *
67  * @v data              Region to checksum
68  * @v len               Length of region
69  * @ret checksum        Byte checksum
70  */
71 static UCHAR byte_sum ( PUCHAR data, ULONG len ) {
72         UCHAR checksum = 0;
73         ULONG offset;
74
75         for ( offset = 0 ; offset < len ; offset++ )
76                 checksum = ( ( UCHAR ) ( checksum + data[offset] ) );
77
78         return checksum;
79 }
80
81 /**
82  * Convert IPv4 address to string
83  *
84  * @v ipaddr            IP address
85  * @ret ipaddr          IP address string
86  *
87  * This function returns a static buffer.
88  */
89 static LPSTR inet_ntoa ( ULONG ipaddr ) {
90         static CHAR buf[16];
91
92         RtlStringCbPrintfA ( buf, sizeof ( buf ), "%d.%d.%d.%d",
93                              ( ( ipaddr >> 0  ) & 0xff ),
94                              ( ( ipaddr >> 8  ) & 0xff ),
95                              ( ( ipaddr >> 16 ) & 0xff ),
96                              ( ( ipaddr >> 24 ) & 0xff ) );
97         return buf;
98 }
99
100 /**
101  * Open registry key
102  *
103  * @v reg_key_name      Registry key name
104  * @v reg_key           Registry key to fill in
105  * @ret ntstatus        NT status
106  */
107 static NTSTATUS reg_open ( LPCWSTR reg_key_name, PHANDLE reg_key ) {
108         UNICODE_STRING unicode_string;
109         OBJECT_ATTRIBUTES object_attrs;
110         NTSTATUS status;
111
112         RtlInitUnicodeString ( &unicode_string, reg_key_name );
113         InitializeObjectAttributes ( &object_attrs, &unicode_string,
114                                      OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
115                                      NULL, NULL );
116         status = ZwOpenKey ( reg_key, KEY_ALL_ACCESS, &object_attrs );
117         if ( ! NT_SUCCESS ( status ) ) {
118                 DbgPrint ( "Could not open %S: %x\n", reg_key_name, status );
119                 return status;
120         }
121
122         return STATUS_SUCCESS;
123 }
124
125 /**
126  * Close registry key
127  *
128  * @v reg_key           Registry key
129  */
130 static VOID reg_close ( HANDLE reg_key ) {
131         ZwClose ( reg_key );
132 }
133
134 /**
135  * Fetch registry key value information
136  *
137  * @v reg_key           Registry key
138  * @v value_name        Registry value name
139  * @v kvi               Key value information block to allocate and fill in
140  * @ret ntstatus        NT status
141  *
142  * The caller must eventually free the allocated key value information
143  * block.
144  */
145 static NTSTATUS fetch_reg_kvi ( HANDLE reg_key, LPCWSTR value_name,
146                                 PKEY_VALUE_PARTIAL_INFORMATION *kvi ) {
147         UNICODE_STRING u_value_name;
148         ULONG kvi_len;
149         NTSTATUS status;
150
151         /* Get value length */
152         RtlInitUnicodeString ( &u_value_name, value_name );
153         status = ZwQueryValueKey ( reg_key, &u_value_name,
154                                    KeyValuePartialInformation, NULL, 0,
155                                    &kvi_len );
156         if ( ! ( ( status == STATUS_SUCCESS ) ||
157                  ( status == STATUS_BUFFER_OVERFLOW ) ||
158                  ( status == STATUS_BUFFER_TOO_SMALL ) ) ) {
159                 DbgPrint ( "Could not get KVI length for \"%S\": %x\n",
160                            value_name, status );
161                 goto err_zwqueryvaluekey_len;
162         }
163
164         /* Allocate value buffer */
165         *kvi = ExAllocatePoolWithTag ( NonPagedPool, kvi_len,
166                                        ISCSIBOOT_POOL_TAG );
167         if ( ! *kvi ) {
168                 DbgPrint ( "Could not allocate KVI for \"%S\": %x\n",
169                            value_name, status );
170                 goto err_exallocatepoolwithtag_kvi;
171         }
172
173         /* Fetch value */
174         status = ZwQueryValueKey ( reg_key, &u_value_name,
175                                    KeyValuePartialInformation, *kvi,
176                                    kvi_len, &kvi_len );
177         if ( ! NT_SUCCESS ( status ) ) {
178                 DbgPrint ( "Could not get KVI for \"%S\": %x\n",
179                            value_name, status );
180                 goto err_zwqueryvaluekey;
181         }
182
183         return STATUS_SUCCESS;
184
185  err_zwqueryvaluekey:
186         ExFreePool ( kvi );
187  err_exallocatepoolwithtag_kvi:
188  err_zwqueryvaluekey_len:
189         return status;
190 }
191
192 /**
193  * Fetch registry string value
194  *
195  * @v reg_key           Registry key
196  * @v value_name        Registry value name
197  * @v value             String value to allocate and fill in
198  * @ret ntstatus        NT status
199  *
200  * The caller must eventually free the allocated value.
201  */
202 static NTSTATUS fetch_reg_sz ( HANDLE reg_key, LPCWSTR value_name,
203                                LPWSTR *value ) {
204         PKEY_VALUE_PARTIAL_INFORMATION kvi;
205         ULONG value_len;
206         NTSTATUS status;
207
208         /* Fetch key value information */
209         status = fetch_reg_kvi ( reg_key, value_name, &kvi );
210         if ( ! NT_SUCCESS ( status ) )
211                 goto err_fetch_reg_kvi;
212
213         /* Allocate and populate string */
214         value_len = ( kvi->DataLength + sizeof ( value[0] ) );
215         *value = ExAllocatePoolWithTag ( NonPagedPool, value_len,
216                                          ISCSIBOOT_POOL_TAG );
217         if ( ! *value ) {
218                 DbgPrint ( "Could not allocate value for \"%S\"\n",
219                            value_name );
220                 status = STATUS_UNSUCCESSFUL;
221                 goto err_exallocatepoolwithtag_value;
222         }
223         RtlZeroMemory ( *value, value_len );
224         RtlCopyMemory ( *value, kvi->Data, kvi->DataLength );
225
226  err_exallocatepoolwithtag_value:
227         ExFreePool ( kvi );
228  err_fetch_reg_kvi:
229         return status;
230 }
231
232 /**
233  * Fetch registry multiple-string value
234  *
235  * @v reg_key           Registry key
236  * @v value_name        Registry value name
237  * @v values            Array of string values to allocate and fill in
238  * @ret ntstatus        NT status
239  *
240  * The caller must eventually free the allocated values.
241  */
242 static NTSTATUS fetch_reg_multi_sz ( HANDLE reg_key, LPCWSTR value_name,
243                                      LPWSTR **values ) {
244         PKEY_VALUE_PARTIAL_INFORMATION kvi;
245         LPWSTR string;
246         ULONG num_strings;
247         ULONG values_len;
248         ULONG i;
249         NTSTATUS status;
250
251         /* Fetch key value information */
252         status = fetch_reg_kvi ( reg_key, value_name, &kvi );
253         if ( ! NT_SUCCESS ( status ) )
254                 goto err_fetch_reg_kvi;
255
256         /* Count number of strings in the array.  This is a
257          * potential(ly harmless) overestimate.
258          */
259         num_strings = 0;
260         for ( string = ( ( LPWSTR ) kvi->Data ) ;
261               string < ( ( LPWSTR ) ( kvi->Data + kvi->DataLength ) ) ;
262               string++ ) {
263                 if ( ! *string )
264                         num_strings++;
265         }
266         
267         /* Allocate and populate string array */
268         values_len = ( ( ( num_strings + 1 ) * sizeof ( values[0] ) ) +
269                        kvi->DataLength + sizeof ( values[0][0] ) );
270         *values = ExAllocatePoolWithTag ( NonPagedPool, values_len,
271                                           ISCSIBOOT_POOL_TAG );
272         if ( ! *values ) {
273                 DbgPrint ( "Could not allocate value array for \"%S\"\n",
274                            value_name );
275                 status = STATUS_UNSUCCESSFUL;
276                 goto err_exallocatepoolwithtag_value;
277         }
278         RtlZeroMemory ( *values, values_len );
279         string = ( ( LPWSTR ) ( *values + num_strings + 1 ) );
280         RtlCopyMemory ( string, kvi->Data, kvi->DataLength );
281         for ( i = 0 ; i < num_strings ; i++ ) {
282                 (*values)[i] = string;
283                 while ( *string )
284                         string++;
285                 while ( ! *string )
286                         string++;
287         }
288
289  err_exallocatepoolwithtag_value:
290         ExFreePool ( kvi );
291  err_fetch_reg_kvi:
292         return status;
293 }
294
295 /**
296  * Store registry string value
297  *
298  * @v reg_key           Registry key
299  * @v value_name        Registry value name
300  * @v value             String value to store
301  * @ret ntstatus        NT status
302  */
303 static NTSTATUS reg_store_sz ( HANDLE reg_key, LPCWSTR value_name,
304                                LPWSTR value ) {
305         UNICODE_STRING u_value_name;
306         SIZE_T value_len;
307         NTSTATUS status;
308
309         RtlInitUnicodeString ( &u_value_name, value_name );
310         value_len = ( ( wcslen ( value ) + 1 ) * sizeof ( value[0] ) );
311         status = ZwSetValueKey ( reg_key, &u_value_name, 0, REG_SZ,
312                                  value, ( ( ULONG ) value_len ) );
313         if ( ! NT_SUCCESS ( status ) ) {
314                 DbgPrint ( "Could not store value \"%S\": %x\n",
315                            value_name, status );
316                 return status;
317         }
318
319         return STATUS_SUCCESS;
320 }
321
322 /**
323  * Store registry string value
324  *
325  * @v reg_key           Registry key
326  * @v value_name        Registry value name
327  * @v ...               String values to store (NULL terminated)
328  * @ret ntstatus        NT status
329  */
330 static NTSTATUS reg_store_multi_sz ( HANDLE reg_key, LPCWSTR value_name,
331                                      ... ) {
332         UNICODE_STRING u_value_name;
333         va_list args;
334         LPCWSTR string;
335         SIZE_T values_len;
336         LPWSTR values;
337         LPWSTR value;
338         SIZE_T values_remaining;
339         SIZE_T value_len;
340         NTSTATUS status;
341
342         /* Calculate total buffer length */
343         values_len = sizeof ( string[0] );
344         va_start ( args, value_name );
345         while ( ( string = va_arg ( args, LPCWSTR ) ) != NULL ) {
346                 values_len += ( ( wcslen ( string ) + 1 ) *
347                                 sizeof ( string[0] ) );
348         }
349         va_end ( args );
350
351         /* Allocate buffer */
352         values = ExAllocatePoolWithTag ( NonPagedPool, values_len,
353                                          ISCSIBOOT_POOL_TAG );
354         if ( ! values ) {
355                 DbgPrint ( "Could not allocate value buffer for \"%S\"\n" );
356                 status = STATUS_UNSUCCESSFUL;
357                 goto err_exallocatepoolwithtag;
358         }
359
360         /* Copy strings into buffer */
361         RtlZeroMemory ( values, values_len );
362         value = values;
363         values_remaining = values_len;
364         va_start ( args, value_name );
365         while ( ( string = va_arg ( args, LPCWSTR ) ) != NULL ) {
366                 RtlStringCbCatW ( value, values_remaining, string );
367                 value_len = ( ( wcslen ( value ) + 1 ) * sizeof ( value[0] ) );
368                 value += ( value_len / sizeof ( value[0] ) );
369                 values_remaining -= value_len;
370         }
371         va_end ( args );
372
373         /* Store value */
374         RtlInitUnicodeString ( &u_value_name, value_name );
375         status = ZwSetValueKey ( reg_key, &u_value_name, 0, REG_MULTI_SZ,
376                                  values, ( ( ULONG ) values_len ) );
377         if ( ! NT_SUCCESS ( status ) ) {
378                 DbgPrint ( "Could not store value \"%S\": %x\n",
379                            value_name, status );
380                 goto err_zwsetvaluekey;
381         }
382
383  err_zwsetvaluekey:
384         ExFreePool ( values );
385  err_exallocatepoolwithtag:
386         return STATUS_SUCCESS;
387 }
388
389 /**
390  * Store registry dword value
391  *
392  * @v reg_key           Registry key
393  * @v value_name        Registry value name
394  * @v value             String value to store, or NULL
395  * @ret ntstatus        NT status
396  */
397 static NTSTATUS reg_store_dword ( HANDLE reg_key, LPCWSTR value_name,
398                                   ULONG value ) {
399         UNICODE_STRING u_value_name;
400         NTSTATUS status;
401
402         RtlInitUnicodeString ( &u_value_name, value_name );
403         status = ZwSetValueKey ( reg_key, &u_value_name, 0, REG_DWORD,
404                                  &value, sizeof ( value ) );
405         if ( ! NT_SUCCESS ( status ) ) {
406                 DbgPrint ( "Could not store value \"%S\": %x\n",
407                            value_name, status );
408                 return status;
409         }
410
411         return STATUS_SUCCESS;
412 }
413
414 /**
415  * Search for iBFT
416  *
417  * @v start             Region in which to start searching
418  * @v len               Length of region
419  * @ret ibft_copy       Copy of iBFT, or NULL
420  *
421  * The returned iBFT is allocated using ExAllocatePool().
422  */
423 static NTSTATUS find_ibft ( PIBFT_TABLE *ibft_copy ) {
424         PHYSICAL_ADDRESS basemem_phy;
425         PUCHAR basemem;
426         ULONG offset;
427         PIBFT_TABLE ibft;
428         NTSTATUS status;
429
430         /* Map base memory */
431         basemem_phy.QuadPart = BASEMEM_START;
432         basemem = MmMapIoSpace ( basemem_phy, BASEMEM_LEN, MmNonCached );
433         if ( ! basemem ) {
434                 DbgPrint ( "Could not map base memory\n" );
435                 status = STATUS_UNSUCCESSFUL;
436                 goto err_mmmapiospace;
437         }
438
439         /* Scan for iBFT */
440         status = STATUS_NO_SUCH_FILE;
441         for ( offset = 0 ; offset < BASEMEM_LEN ; offset += 16 ) {
442                 ibft = ( ( PIBFT_TABLE ) ( basemem + offset ) );
443                 if ( memcmp ( ibft->acpi.signature, IBFT_SIG,
444                               sizeof ( ibft->acpi.signature ) ) != 0 )
445                         continue;
446                 if ( ( offset + ibft->acpi.length ) > BASEMEM_LEN )
447                         continue;
448                 if ( byte_sum ( ( ( PUCHAR ) ibft ), ibft->acpi.length ) != 0 )
449                         continue;
450                 DbgPrint ( "Found iBFT at %05x OEM ID \"%.6s\" OEM table ID "
451                            "\"%.8s\"\n", ( BASEMEM_START + offset ),
452                            ibft->acpi.oem_id, ibft->acpi.oem_table_id );
453                 /* Create copy of iBFT */
454                 *ibft_copy = ExAllocatePoolWithTag ( NonPagedPool,
455                                                      ibft->acpi.length,
456                                                      ISCSIBOOT_POOL_TAG );
457                 if ( ! *ibft_copy ) {
458                         DbgPrint ( "Could not allocate iBFT copy\n" );
459                         status = STATUS_NO_MEMORY;
460                         goto err_exallocatepoolwithtag;
461                 }
462                 RtlCopyMemory ( *ibft_copy, ibft, ibft->acpi.length );
463                 status = STATUS_SUCCESS;
464                 break;
465         }
466
467  err_exallocatepoolwithtag:
468         MmUnmapIoSpace ( basemem, BASEMEM_LEN );
469  err_mmmapiospace:
470         return status;
471 }
472
473 /**
474  * Check to see if iBFT string exists
475  *
476  * @v string            iBFT string
477  * @ret exists          String exists
478  */
479 static BOOLEAN ibft_string_exists ( PIBFT_STRING string ) {
480         return ( ( BOOLEAN ) ( string->offset != 0 ) );
481 }
482
483 /**
484  * Read iBFT string
485  *
486  * @v ibft              iBFT
487  * @v string            iBFT string
488  * @ret string          Standard C string
489  */
490 static LPSTR ibft_string ( PIBFT_TABLE ibft, PIBFT_STRING string ) {
491         if ( string->offset ) {
492                 return ( ( ( PCHAR ) ibft ) + string->offset );
493         } else {
494                 return "";
495         }
496 }
497
498 /**
499  * Check to see if iBFT IP address exists
500  *
501  * @v ipaddr            IP address
502  * @ret exists          IP address exists
503  */
504 static BOOLEAN ibft_ipaddr_exists ( PIBFT_IPADDR ipaddr ) {
505         return ( ( BOOLEAN ) ( ipaddr->in != 0 ) );
506 }
507
508 /**
509  * Convert iBFT IP address to string
510  *
511  * @v ipaddr            IP address
512  * @ret ipaddr          IP address string
513  *
514  * This function returns a static buffer, as per inet_ntoa().
515  */
516 static LPSTR ibft_ipaddr ( PIBFT_IPADDR ipaddr ) {
517         return inet_ntoa ( ipaddr->in );
518 }
519
520 /**
521  * Parse iBFT initiator structure
522  *
523  * @v ibft              iBFT
524  * @v initiator         Initiator structure
525  */
526 static VOID parse_ibft_initiator ( PIBFT_TABLE ibft,
527                                    PIBFT_INITIATOR initiator ) {
528         PIBFT_HEADER header = &initiator->header;
529
530         /* Dump structure information */
531         DbgPrint ( "Found iBFT Initiator %d:\n", header->index );
532         DbgPrint ( "  Flags = %#02x%s%s\n", header->flags,
533                    ( header->flags & IBFT_FL_INITIATOR_BLOCK_VALID
534                      ? ", valid" : "" ),
535                    ( header->flags & IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED
536                      ? ", boot selected" : "" ) );
537         if ( ! ( header->flags & IBFT_FL_INITIATOR_BLOCK_VALID ) )
538                 return;
539         DbgPrint ( "  iSNS = %s\n", ibft_ipaddr ( &initiator->isns_server ) );
540         DbgPrint ( "  SLP = %s\n", ibft_ipaddr ( &initiator->slp_server ) );
541         DbgPrint ( "  Radius = %s", ibft_ipaddr ( &initiator->radius[0] ) );
542         DbgPrint ( ", %s\n", ibft_ipaddr ( &initiator->radius[1] ) );
543         DbgPrint ( "  Name = %s\n",
544                    ibft_string ( ibft, &initiator->initiator_name ) );
545 }
546
547 /**
548  * Fetch NIC MAC address
549  *
550  * @v name              NDIS device name
551  * @v device            NDIS device object
552  * @v file              NDIS file object
553  * @v mac               MAC address buffer
554  * @v mac_len           MAC address buffer length
555  * @ret ntstatus        NT status
556  */
557 static NTSTATUS fetch_mac ( PUNICODE_STRING name, PDEVICE_OBJECT device,
558                             PFILE_OBJECT file, PUCHAR mac, ULONG mac_len ) {
559         KEVENT event;
560         ULONG in_buf;
561         IO_STATUS_BLOCK io_status;
562         PIRP irp;
563         PIO_STACK_LOCATION io_stack;
564         ULONG i;
565         NTSTATUS status;
566
567         /* Construct IRP to query MAC address */
568         KeInitializeEvent ( &event, NotificationEvent, FALSE );
569         in_buf = OID_802_3_CURRENT_ADDRESS;
570         irp = IoBuildDeviceIoControlRequest ( IOCTL_NDIS_QUERY_GLOBAL_STATS,
571                                               device, &in_buf,
572                                               sizeof ( in_buf ), mac, mac_len,
573                                               FALSE, &event, &io_status );
574         if ( ! irp ) {
575                 DbgPrint ( "Could not build IRP to retrieve MAC for \"%wZ\"\n",
576                            name );
577                 return STATUS_UNSUCCESSFUL;
578         }
579         io_stack = IoGetNextIrpStackLocation( irp );
580         io_stack->FileObject = file;
581
582         /* Issue IRP */
583         status = IoCallDriver ( device, irp );
584         if ( status == STATUS_PENDING ) {
585                 status = KeWaitForSingleObject ( &event, Executive, KernelMode,
586                                                  FALSE, NULL );
587         }
588         if ( NT_SUCCESS ( status ) )
589                 status = io_status.Status;
590         if ( ! NT_SUCCESS ( status ) ) {
591                 DbgPrint ( "IRP failed to retrieve MAC for \"%wZ\": %x\n",
592                            name, status );
593                 return status;
594         }
595
596         /* Dump MAC address */
597         DbgPrint ( "Found NIC with MAC address" );
598         for ( i = 0 ; i < mac_len ; i++ )
599                 DbgPrint ( "%c%02x", ( i ? ':' : ' ' ), mac[i] );
600         DbgPrint ( " at \"%wZ\"\n", name );
601
602         return STATUS_SUCCESS;
603 }
604
605 /**
606  * Fetch NIC PDO
607  *
608  * @v name              NDIS device name
609  * @v device            NDIS device object
610  * @v pdo               Associated physical device object
611  * @ret ntstatus        NT status
612  */
613 static NTSTATUS fetch_pdo ( PUNICODE_STRING name, PDEVICE_OBJECT device,
614                             PDEVICE_OBJECT *pdo ) {
615         KEVENT event;
616         IO_STATUS_BLOCK io_status;
617         PIRP irp;
618         PIO_STACK_LOCATION io_stack;
619         PDEVICE_RELATIONS relations;
620         NTSTATUS status;
621
622         /* Construct IRP to query MAC address */
623         KeInitializeEvent ( &event, NotificationEvent, FALSE );
624         irp = IoBuildSynchronousFsdRequest ( IRP_MJ_PNP, device, NULL, 0, NULL,
625                                              &event, &io_status );
626         if ( ! irp ) {
627                 DbgPrint ( "Could not build IRP to retrieve PDO for \"%wZ\"\n",
628                            name );
629                 return STATUS_UNSUCCESSFUL;
630         }
631         io_stack = IoGetNextIrpStackLocation( irp );
632         io_stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
633         io_stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
634
635         /* Issue IRP */
636         status = IoCallDriver ( device, irp );
637         if ( status == STATUS_PENDING ) {
638                 status = KeWaitForSingleObject ( &event, Executive, KernelMode,
639                                                  FALSE, NULL );
640         }
641         if ( NT_SUCCESS ( status ) )
642                 status = io_status.Status;
643         if ( ! NT_SUCCESS ( status ) ) {
644                 DbgPrint ( "IRP failed to retrieve PDO for \"%wZ\": %x\n",
645                            name, status );
646                 return status;
647         }
648
649         /* Extract PDO */
650         relations = ( ( PDEVICE_RELATIONS ) io_status.Information );
651         *pdo = relations->Objects[0];
652
653         /* Free the relations list allocated by the IRP */
654         ExFreePool ( relations );
655
656         return STATUS_SUCCESS;
657 }
658
659 /**
660  * Fetch NetCfgInstanceId registry value
661  *
662  * @v pdo               Physical device object
663  * @v netcfginstanceid  Value to allocate and fill in
664  * @ret ntstatus        NT status
665  *
666  * The caller must eventually free the allocated value.
667  */
668 static NTSTATUS fetch_netcfginstanceid ( PDEVICE_OBJECT pdo,
669                                          LPWSTR *netcfginstanceid ) {
670         HANDLE reg_key;
671         NTSTATUS status;
672
673         /* Open driver registry key */
674         status = IoOpenDeviceRegistryKey ( pdo, PLUGPLAY_REGKEY_DRIVER,
675                                            KEY_READ, &reg_key );
676         if ( ! NT_SUCCESS ( status ) ) {
677                 DbgPrint ( "Could not open driver registry key for PDO %p: "
678                            "%x\n", pdo, status );
679                 goto err_ioopendeviceregistrykey;
680         }
681
682         /* Read NetCfgInstanceId value */
683         status = fetch_reg_sz ( reg_key, L"NetCfgInstanceId",
684                                 netcfginstanceid );
685         if ( ! NT_SUCCESS ( status ) )
686                 goto err_fetch_reg_wstr;
687
688  err_fetch_reg_wstr:
689         ZwClose ( reg_key );
690  err_ioopendeviceregistrykey:
691         return status;
692 }
693
694 /**
695  * Store IPv4 parameter into a string registry value
696  *
697  * @v reg_key           Registry key
698  * @v value_name        Registry value name
699  * @v ipaddr            IPv4 address
700  * @ret ntstatus        NT status
701  */
702 static NTSTATUS store_ipv4_parameter_sz ( HANDLE reg_key, LPCWSTR value_name,
703                                           ULONG ipaddr ) {
704         WCHAR buf[16];
705         LPWSTR value;
706
707         if ( ipaddr ) {
708                 RtlStringCbPrintfW ( buf, sizeof ( buf ),
709                                      L"%S", inet_ntoa ( ipaddr ) );
710                 value = buf;
711         } else {
712                 value = L"";
713         }
714
715         return reg_store_sz ( reg_key, value_name, value );
716 }
717
718 /**
719  * Store IPv4 parameter into a multi-string registry value
720  *
721  * @v reg_key           Registry key
722  * @v value_name        Registry value name
723  * @v ipaddr            IPv4 address
724  * @ret ntstatus        NT status
725  */
726 static NTSTATUS store_ipv4_parameter_multi_sz ( HANDLE reg_key,
727                                                 LPCWSTR value_name,
728                                                 ULONG ipaddr ) {
729         WCHAR buf[16];
730         LPWSTR value;
731
732         if ( ipaddr ) {
733                 RtlStringCbPrintfW ( buf, sizeof ( buf ),
734                                      L"%S", inet_ntoa ( ipaddr ) );
735                 value = buf;
736         } else {
737                 value = NULL;
738         }
739
740         return reg_store_multi_sz ( reg_key, value_name, value, NULL );
741 }
742
743 /**
744  * Store TCP/IP parameters in registry
745  *
746  * @v nic               iBFT NIC structure
747  * @v netcfginstanceid  Interface name within registry
748  * @ret ntstatus        NT status
749  */
750 static NTSTATUS store_tcpip_parameters ( PIBFT_NIC nic,
751                                          LPCWSTR netcfginstanceid ) {
752         LPCWSTR key_name_prefix = ( L"\\Registry\\Machine\\SYSTEM\\"
753                                     L"CurrentControlSet\\Services\\"
754                                     L"Tcpip\\Parameters\\Interfaces\\" );
755         LPWSTR key_name;
756         SIZE_T key_name_len;
757         HANDLE reg_key;
758         ULONG subnet_mask;
759         NTSTATUS status;
760
761         /* Allocate key name */
762         key_name_len = ( ( wcslen ( key_name_prefix ) +
763                            wcslen ( netcfginstanceid ) + 1 ) *
764                          sizeof ( key_name[0] ) );
765         key_name = ExAllocatePoolWithTag ( NonPagedPool, key_name_len,
766                                            ISCSIBOOT_POOL_TAG );
767         if ( ! key_name ) {
768                 DbgPrint ( "Could not allocate TCP/IP key name\n" );
769                 status = STATUS_UNSUCCESSFUL;
770                 goto err_exallocatepoolwithtag;
771         }
772
773         /* Construct key name */
774         RtlStringCbCopyW ( key_name, key_name_len, key_name_prefix );
775         RtlStringCbCatW ( key_name, key_name_len, netcfginstanceid );
776
777         /* Open key */
778         status = reg_open ( key_name, &reg_key );
779         if ( ! NT_SUCCESS ( status ) )
780                 goto err_reg_open;
781
782         /* Store IP address */
783         status = store_ipv4_parameter_multi_sz ( reg_key, L"IPAddress",
784                                                  nic->ip_address.in );
785         if ( ! NT_SUCCESS ( status ) )
786                 goto err_reg_store;
787
788         /* Store subnet mask */
789         subnet_mask = RtlUlongByteSwap ( 0xffffffffUL << ( 32 - nic->subnet_mask_prefix ) );
790         status = store_ipv4_parameter_multi_sz ( reg_key, L"SubnetMask",
791                                                  subnet_mask );
792         if ( ! NT_SUCCESS ( status ) )
793                 goto err_reg_store;
794
795         /* Store default gateway */
796         status = store_ipv4_parameter_multi_sz ( reg_key, L"DefaultGateway",
797                                                  nic->gateway.in );
798         if ( ! NT_SUCCESS ( status ) )
799                 goto err_reg_store;
800
801         /* Store DNS servers */
802         status = store_ipv4_parameter_sz ( reg_key, L"NameServer",
803                                            nic->dns[0].in );
804         if ( ! NT_SUCCESS ( status ) )
805                 goto err_reg_store;
806
807         /* Disable DHCP */
808         status = reg_store_dword ( reg_key, L"EnableDHCP", 0 );
809         if ( ! NT_SUCCESS ( status ) )
810                 goto err_reg_store;
811
812  err_reg_store:
813         reg_close ( reg_key );
814  err_reg_open:
815         ExFreePool ( key_name );
816  err_exallocatepoolwithtag:
817         return status;
818 }
819
820 /**
821  * Try to configure NIC from iBFT NIC structure
822  *
823  * @v nic               iBFT NIC structure
824  * @v name              NDIS device name
825  * @ret ntstatus        NT status
826  */
827 static NTSTATUS try_configure_nic ( PIBFT_NIC nic, PUNICODE_STRING name ) {
828         BOOLEAN must_disable;
829         PFILE_OBJECT file;
830         PDEVICE_OBJECT device;
831         UCHAR mac[6];
832         PDEVICE_OBJECT pdo;
833         LPWSTR netcfginstanceid;
834         NTSTATUS status;
835
836         /* Enable interface if not already done */
837         status = IoSetDeviceInterfaceState ( name, TRUE );
838         must_disable = ( NT_SUCCESS ( status ) ? TRUE : FALSE );
839
840         /* Get device and file object pointers */
841         status = IoGetDeviceObjectPointer ( name, FILE_ALL_ACCESS, &file,
842                                             &device );
843         if ( ! NT_SUCCESS ( status ) ) {
844                 /* Not an error, apparently; IoGetDeviceInterfaces()
845                  * seems to return a whole load of interfaces that
846                  * aren't attached to any objects.
847                  */
848                 goto err_iogetdeviceobjectpointer;
849         }
850
851         /* See if NIC matches */
852         status = fetch_mac ( name, device, file, mac, sizeof ( mac ) );
853         if ( ! NT_SUCCESS ( status ) )
854                 goto err_fetch_mac;
855         if ( memcmp ( nic->mac_address, mac, sizeof ( mac ) ) != 0 )
856                 goto err_compare_mac;
857         DbgPrint ( "iBFT NIC %d is interface \"%wZ\"\n",
858                    nic->header.index, name );
859
860         /* Get matching PDO */
861         status = fetch_pdo ( name, device, &pdo );
862         if ( ! NT_SUCCESS ( status ) )
863                 goto err_fetch_pdo;
864         DbgPrint ( "iBFT NIC %d is PDO %p\n", nic->header.index, pdo );
865
866         /* Get NetCfgInstanceId */
867         status = fetch_netcfginstanceid ( pdo, &netcfginstanceid );
868         if ( ! NT_SUCCESS ( status ) )
869                 goto err_fetch_netcfginstanceid;
870         DbgPrint ( "iBFT NIC %d is NetCfgInstanceId \"%S\"\n",
871                    nic->header.index, netcfginstanceid );
872
873         /* Store registry values */
874         status = store_tcpip_parameters ( nic, netcfginstanceid );
875         if ( ! NT_SUCCESS ( status ) )
876                 goto err_store_tcpip_parameters;
877
878  err_store_tcpip_parameters:
879         ExFreePool ( netcfginstanceid );
880  err_fetch_netcfginstanceid:
881  err_fetch_pdo:
882  err_compare_mac:
883  err_fetch_mac:
884         /* Drop object reference */
885         ObDereferenceObject ( file );
886  err_iogetdeviceobjectpointer:
887         /* Disable interface if we had to enable it */
888         if ( must_disable )
889                 IoSetDeviceInterfaceState ( name, FALSE );
890         return status;
891 }
892
893 /**
894  * Parse iBFT NIC structure
895  *
896  * @v ibft              iBFT
897  * @v nic               NIC structure
898  */
899 static VOID parse_ibft_nic ( PIBFT_TABLE ibft, PIBFT_NIC nic ) {
900         PIBFT_HEADER header = &nic->header;
901         PWSTR symlinks;
902         PWSTR symlink;
903         UNICODE_STRING u_symlink;
904         NTSTATUS status;
905
906         /* Dump structure information */
907         DbgPrint ( "Found iBFT NIC %d:\n", header->index );
908         DbgPrint ( "  Flags = %#02x%s%s\n", header->flags,
909                    ( header->flags & IBFT_FL_NIC_BLOCK_VALID
910                      ? ", valid" : "" ),
911                    ( header->flags & IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED
912                      ? ", boot selected" : "" ),
913                    ( header->flags & IBFT_FL_NIC_GLOBAL
914                      ? ", global address" : ", link local address" ) );
915         if ( ! ( header->flags & IBFT_FL_NIC_BLOCK_VALID ) )
916                 return;
917         DbgPrint ( "  IP = %s/%d\n", ibft_ipaddr ( &nic->ip_address ),
918                    nic->subnet_mask_prefix );
919         DbgPrint ( "  Origin = %d\n", nic->origin );
920         DbgPrint ( "  Gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
921         DbgPrint ( "  DNS = %s", ibft_ipaddr ( &nic->dns[0] ) );
922         DbgPrint ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) );
923         DbgPrint ( "  DHCP = %s\n", ibft_ipaddr ( &nic->dhcp ) );
924         DbgPrint ( "  VLAN = %04x\n", nic->vlan );
925         DbgPrint ( "  MAC = %02x:%02x:%02x:%02x:%02x:%02x\n",
926                    nic->mac_address[0], nic->mac_address[1],
927                    nic->mac_address[2], nic->mac_address[3],
928                    nic->mac_address[4], nic->mac_address[5] );
929         DbgPrint ( "  PCI = %02x:%02x.%x\n",
930                    ( ( nic->pci_bus_dev_func >> 8 ) & 0xff ),
931                    ( ( nic->pci_bus_dev_func >> 3 ) & 0x1f ),
932                    ( ( nic->pci_bus_dev_func >> 0 ) & 0x07 ) );
933         DbgPrint ( "  Hostname = %s\n", ibft_string ( ibft, &nic->hostname ) );
934
935         /* Get list of all objects providing GUID_NDIS_LAN_CLASS interface */
936         status = IoGetDeviceInterfaces ( &GUID_NDIS_LAN_CLASS, NULL,
937                                          DEVICE_INTERFACE_INCLUDE_NONACTIVE,
938                                          &symlinks );
939         if ( ! NT_SUCCESS ( status ) ) {
940                 DbgPrint ( "Could not fetch NIC list: %x\n", status );
941                 return;
942         }
943
944         /* Configure any matching NICs */
945         for ( symlink = symlinks ;
946               RtlInitUnicodeString ( &u_symlink, symlink ) , *symlink ;
947               symlink += ( ( u_symlink.Length / sizeof ( *symlink ) ) + 1 ) ) {
948                 try_configure_nic ( nic, &u_symlink );
949         }
950
951         /* Free object list */
952         ExFreePool ( symlinks );
953 }
954
955 /**
956  * Parse iBFT target structure
957  *
958  * @v ibft              iBFT
959  * @v target            Target structure
960  */
961 static VOID parse_ibft_target ( PIBFT_TABLE ibft, PIBFT_TARGET target ) {
962         PIBFT_HEADER header = &target->header;
963
964         /* Dump structure information */
965         DbgPrint ( "Found iBFT target %d:\n", header->index );
966         DbgPrint ( "  Flags = %#02x%s%s\n", header->flags,
967                    ( header->flags & IBFT_FL_TARGET_BLOCK_VALID
968                      ? ", valid" : "" ),
969                    ( header->flags & IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED
970                      ? ", boot selected" : "" ),
971                    ( header->flags & IBFT_FL_TARGET_USE_CHAP
972                      ? ", Radius CHAP" : "" ),
973                    ( header->flags & IBFT_FL_TARGET_USE_RCHAP
974                      ? ", Radius rCHAP" : "" ) );
975         if ( ! ( header->flags & IBFT_FL_TARGET_BLOCK_VALID ) )
976                 return;
977         DbgPrint ( "  IP = %s\n",
978                    ibft_ipaddr ( &target->ip_address ) );
979         DbgPrint ( "  Port = %d\n", target->socket );
980         DbgPrint ( "  LUN = %04x-%04x-%04x-%04x\n",
981                    ( ( target->boot_lun >> 48 ) & 0xffff ),
982                    ( ( target->boot_lun >> 32 ) & 0xffff ),
983                    ( ( target->boot_lun >> 16 ) & 0xffff ),
984                    ( ( target->boot_lun >> 0  ) & 0xffff ) );
985         DbgPrint ( "  CHAP type = %d (%s)\n", target->chap_type,
986                    ( ( target->chap_type == IBFT_CHAP_NONE ) ? "None" :
987                      ( ( target->chap_type == IBFT_CHAP_ONE_WAY ) ? "One-way" :
988                        ( ( target->chap_type == IBFT_CHAP_MUTUAL ) ? "Mutual" :
989                          "Unknown" ) ) ) );
990         DbgPrint ( "  NIC = %d\n", target->nic_association );
991         DbgPrint ( "  Name = %s\n",
992                    ibft_string ( ibft, &target->target_name ) );
993         DbgPrint ( "  CHAP name = %s\n",
994                    ibft_string ( ibft, &target->chap_name ) );
995         DbgPrint ( "  CHAP secret = %s\n",
996                    ( ibft_string_exists ( &target->chap_secret ) ?
997                      "<omitted>" : "" ) );
998         DbgPrint ( "  Reverse CHAP name = %s\n",
999                    ibft_string ( ibft, &target->reverse_chap_name ) );
1000         DbgPrint ( "  Reverse CHAP secret = %s\n",
1001                    ( ibft_string_exists ( &target->reverse_chap_secret ) ?
1002                      "<omitted>" : "" ) );
1003 }
1004
1005 /**
1006  * Parse iBFT
1007  *
1008  * @v ibft              iBFT
1009  */
1010 static VOID parse_ibft ( PIBFT_TABLE ibft ) {
1011         PIBFT_CONTROL control = &ibft->control;
1012         PUSHORT offset;
1013         PIBFT_HEADER header;
1014
1015         /* Scan through all entries in the Control structure */
1016         for ( offset = &control->extensions ;
1017               ( ( PUCHAR ) offset ) <
1018                       ( ( ( PUCHAR ) control ) + control->header.length ) ;
1019               offset++ ) {
1020                 if ( ! *offset )
1021                         continue;
1022                 header = ( ( PIBFT_HEADER ) ( ( ( PUCHAR ) ibft ) + *offset ));
1023                 switch ( header->structure_id ) {
1024                 case IBFT_STRUCTURE_ID_INITIATOR :
1025                         parse_ibft_initiator ( ibft,
1026                                                ( ( PIBFT_INITIATOR ) header ));
1027                         break;
1028                 case IBFT_STRUCTURE_ID_NIC :
1029                         parse_ibft_nic ( ibft, ( ( PIBFT_NIC ) header ) );
1030                         break;
1031                 case IBFT_STRUCTURE_ID_TARGET :
1032                         parse_ibft_target ( ibft,
1033                                             ( ( PIBFT_TARGET ) header ) );
1034                         break;
1035                 default :
1036                         DbgPrint ( "Ignoring unknown iBFT structure ID %d "
1037                                    "index %d\n", header->structure_id,
1038                                    header->index );
1039                         break;
1040                 }
1041         }
1042 }
1043
1044 /**
1045  * Dummy IRP handler
1046  *
1047  * @v device            Device object
1048  * @v irp               IRP
1049  * @ret ntstatus        NT status
1050  */
1051 static NTSTATUS iscsiboot_dummy_irp ( PDEVICE_OBJECT device, PIRP irp ) {
1052
1053         irp->IoStatus.Status = STATUS_SUCCESS;
1054         irp->IoStatus.Information = 0;
1055         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1056
1057         ( VOID ) device;
1058         return STATUS_SUCCESS;
1059 }
1060
1061 /**
1062  * IoControl IRP handler
1063  *
1064  * @v device            Device object
1065  * @v irp               IRP
1066  * @ret ntstatus        NT status
1067  */
1068 static NTSTATUS iscsiboot_iocontrol_irp ( PDEVICE_OBJECT device, PIRP irp ) {
1069         PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation ( irp );
1070         PISCSIBOOT_PRIV priv = device->DeviceExtension;
1071         ULONG len;
1072         NTSTATUS status;
1073
1074         switch ( irpsp->Parameters.DeviceIoControl.IoControlCode ) {
1075         case IOCTL_ISCSIBOOT:
1076                 DbgPrint ( "iSCSI boot parameters requested\n" );
1077                 len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
1078                 if ( len > priv->ibft->acpi.length )
1079                         len = priv->ibft->acpi.length;
1080                 RtlCopyMemory ( irp->AssociatedIrp.SystemBuffer,
1081                                 priv->ibft, len );
1082                 status = STATUS_SUCCESS;
1083                 break;
1084         default:
1085                 DbgPrint ( "Unrecognised IoControl %x\n",
1086                            irpsp->Parameters.DeviceIoControl.IoControlCode );
1087                 status = STATUS_INVALID_DEVICE_REQUEST;
1088                 break;
1089         }
1090
1091         irp->IoStatus.Status = status;
1092         irp->IoStatus.Information = 0;
1093         IoCompleteRequest ( irp, IO_NO_INCREMENT );
1094         return status;
1095 }
1096
1097 /**
1098  * Create device object and symlink
1099  *
1100  * @v driver            Driver object
1101  * @ret ntstatus        NT status
1102  */
1103 static NTSTATUS create_iscsiboot_device ( PDRIVER_OBJECT driver,
1104                                           PIBFT_TABLE ibft ) {
1105         UNICODE_STRING u_device_name;
1106         UNICODE_STRING u_device_symlink;
1107         PISCSIBOOT_PRIV priv;
1108         PDEVICE_OBJECT device;
1109         NTSTATUS status;
1110
1111         /* Create device */
1112         RtlInitUnicodeString ( &u_device_name, iscsiboot_device_name );
1113         status = IoCreateDeviceSecure ( driver, sizeof ( *priv ),
1114                                         &u_device_name, FILE_DEVICE_UNKNOWN,
1115                                         FILE_DEVICE_SECURE_OPEN, FALSE,
1116                                         &SDDL_DEVOBJ_SYS_ALL_ADM_ALL,
1117                                         &GUID_ISCSIBOOT_CLASS, &device );
1118         if ( ! NT_SUCCESS ( status ) ) {
1119                 DbgPrint ( "Could not create device \"%S\": %x\n",
1120                            iscsiboot_device_name, status );
1121                 return status;
1122         }
1123         priv = device->DeviceExtension;
1124         priv->ibft = ibft;
1125         device->Flags &= ~DO_DEVICE_INITIALIZING;
1126
1127         /* Create device symlink */
1128         RtlInitUnicodeString ( &u_device_symlink, iscsiboot_device_symlink );
1129         status = IoCreateSymbolicLink ( &u_device_symlink, &u_device_name );
1130         if ( ! NT_SUCCESS ( status ) ) {
1131                 DbgPrint ( "Could not create device symlink \"%S\": %x\n",
1132                            iscsiboot_device_symlink, status );
1133                 return status;
1134         }
1135
1136         return STATUS_SUCCESS;
1137 }
1138
1139 /**
1140  * Driver entry point
1141  *
1142  * @v DriverObject      Driver object
1143  * @v RegistryPath      Driver-specific registry path
1144  * @ret ntstatus        NT status
1145  */
1146 NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject,
1147                        IN PUNICODE_STRING RegistryPath ) {
1148         PIBFT_TABLE ibft;
1149         NTSTATUS status;
1150
1151         DbgPrint ( "iSCSI Boot Parameter Driver initialising\n" );
1152
1153         /* Scan for iBFT */
1154         status = find_ibft ( &ibft );
1155         if ( ! NT_SUCCESS ( status ) ) {
1156                 DbgPrint ( "No iBFT found\n" );
1157                 /* Lack of an iBFT is not necessarily an error */
1158                 status = STATUS_SUCCESS;
1159                 goto err_no_ibft;
1160         }
1161
1162         /* Parse iBFT */
1163         parse_ibft ( ibft );
1164
1165         /* Hook in driver methods */
1166         DriverObject->MajorFunction[IRP_MJ_CREATE] = iscsiboot_dummy_irp;
1167         DriverObject->MajorFunction[IRP_MJ_CLOSE] = iscsiboot_dummy_irp;
1168         DriverObject->MajorFunction[IRP_MJ_CLEANUP] = iscsiboot_dummy_irp;
1169         DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
1170                 iscsiboot_iocontrol_irp;
1171
1172         /* Create device object */
1173         status = create_iscsiboot_device ( DriverObject, ibft );
1174         if ( ! NT_SUCCESS ( status ) )
1175                 goto err_create_iscsiboot_device;
1176
1177         DbgPrint ( "iSCSI Boot Parameter Driver initialisation complete\n" );
1178
1179  err_create_iscsiboot_device:
1180  err_no_ibft:
1181         ( VOID ) RegistryPath;
1182         return status;
1183 }